如何在 C# 中 Update/Merge 两个具有数百个属性的巨大列表 <Class>,以最有效的方式基于公共匹配键

How to Update/Merge two huge List<Class> with hundreds of properties in C#, based on common matching Key in most efficient way

我有两大系列 List<Class>,其中有数百个属性。

例如原始合集 List<OriginalCollection> 和更新合集 List<UpdatedColleciton>

UpdatedCollection 将包含某些列中的值,这些列很可能不会成为 OriginalCollection 的一部分,并且 UpdatedCollection 可能具有某些 KeyColumn [ID Column] 可能不是 OriginalCollection 的一部分,我在 OriginalCollection 中收到了数千个数据集,并且 UpdatedColletion 的记录会在一段时间内增加。

我确实有一个要求,只有 OriginalCollection 的 null 或空列应该被 ID 匹配的 UpdatedCollection 值替换,如果没有匹配的 ID可用,那么这些记录应该从 UpdatedCollection.

添加到 OriginalCollection

我尝试使用 AutoMapper,我尝试根据匹配 ID 使用 UpdatedCollection 更新 OriginalCollection,但我找不到任何 AutoMapper configuration我的上述要求。

我正在寻找不应该影响性能的最有效的解决方案,这就是为什么我没有采用典型的并集和交集方式,因为 Modal 有数百个 属性 并且有数千条记录,因为我确实有很多属性,所以我认为像 AutoMapper 这样的库比在循环中编写逻辑来检查所有数千条记录的每一列的值更好。

请提出任何更好且性能高效的解决方案,例如 AutoMapper Configuration 或任何其他 .Net 内置功能来实现此场景。

我还从 https://github.com/AutoMapper/AutoMapper.Collection

中检查了 AutoMapper.Collection
 cfg.CreateMap<OrderItemDTO, OrderItem>().EqualityComparison((odto, o) => odto.ID == o.ID);
 Mapping OrderDTO back to Order will compare Order items list based on if their ID's match

 Mapper.Map<List<OrderDTO>,List<Order>>(orderDtos, orders);

但它有以下行为,无法按我的要求正常工作

  1. 如果 ID 匹配会将 OrderDTO 映射到 Order
  2. 如果 OrderDTO 存在并且订单没有添加到集合中不适合我
  3. 如果订单存在并且 OrderDTO 没有从集合中删除不适合我

AutoMapper 是已知的映射库并且也有很好的文档,但是无法找到 AutoMapper.Collection 的类似详细文档,我已经探索了 AutoMapper.Collection 但它没有提供解决方案按照我的要求。

所以,我需要走传统的路。

通过 LINQ 查询准备的集合差异。

 var common = original.Where(x => revised.Exists(z => z.ID == x.ID)).ToList();
 var nonCommon = revised.Where(x => !original.Exists(z => z.ID == x.ID)).ToList();

 foreach(var item in common)
 {
      var derived = revised.FirstOrDefault(x => x.ID == item.ID);

      // Added Extention Method to compare and update property
      var data = item.UpdateProperties(derived);
 }
 common.AddRange(nonCommon);

利用反射比较对象并在 属性 级别比较后更新它的值,适用于所有数据类型。

public static T UpdateProperties<T>(this T source, T destination)
{
    Type type = source.GetType();   // Gets Source Object Type
    PropertyInfo[] props = type.GetProperties();    // Gets Source object Properties

    foreach (var prop in props)     // Iterate threw all properties of source object
    {
        var sourceValue = prop.GetValue(source);    // Get source object value by Property Name
        var destinationValue = prop.GetValue(destination);      // Get destination object value by Property Name, to update source object

        // Update source object property value only if derived object's property has value and source object doesn't
        if (string.IsNullOrEmpty(sourceValue?.ToString()) && !string.IsNullOrEmpty(destinationValue?.ToString()))
        {
            prop.SetValue(source, destinationValue);    // Update source object's property with value of derived object
        }
    }
    return source;
}