在运行时将两个C#对象合并在一起

21

我有一个情况,需要从Excel加载一组非规范化的记录。 我逐行导入并逐个创建对象。 每行可能包含公司和/或客户。

我的问题是,多个行可能具有相同的对象,因此我可能已经创建了该对象。 我进行比较以查看它是否已经在列表中。 如果是,则需要合并这两个对象以确保我没有从第二行获得任何新信息。

所以:

company   - client   - address   - phone
----------------------------------------
mycompany -          - myaddress - 
mycompnay - myclient -           - myphone

第一行将创建一个地址为"myaddress"的公司对象。

第二行将创建另一个公司对象(根据我的规则,名称相同的是同一家公司),其中还包括客户参考和电话号码。

所以我知道它们是相同的,但需要确保所有数据合并成一个对象。

目前我正在创建一个实用程序类,它接受两个对象(一个是主要的,另一个是要合并的,如果有冲突,则优先使用主要对象),遍历每个变量并分配值(如果有的话)。 这有点样板重复,并且我希望有一些实用工具可以帮助我完成手动工作。

此示例已经简化了,因为还有很多其他变量,一些是基本类型,另一些是更复杂的项目。


你确定总是只有最多两个需要合并的对象吗?如果同一列/属性定义了两次会怎么样?忽略其中一个听起来很危险! - Achim
目前我正在迭代我的集合,每次找到匹配项时都会进行合并。理论上,集合中可能有多个匹配项,但每次合并都会逐一完成。如果用户输入了错误的数据,以至于我必须因为多个字段而失去一些值,我无法想到任何解决方法。 - Jon
5个回答

38

反射会起作用。类似于这样:

public static void MergeWith<T>(this T primary, T secondary) {
    foreach (var pi in typeof(T).GetProperties()) {
       var priValue = pi.GetGetMethod().Invoke(primary, null);
       var secValue = pi.GetGetMethod().Invoke(secondary, null);
       if (priValue == null || (pi.PropertyType.IsValueType && priValue.Equals(Activator.CreateInstance(pi.PropertyType)))) {
          pi.GetSetMethod().Invoke(primary, new object[]{secValue});
       }
    }
}

8
通用而优雅,但不知怎么的,它让我感到一阵寒意。 - CesarGon
非常好,向您致敬!;-) - BlackTea
1
由于某种原因,对于值类型,priValue == Activator.CreateInstance(pi.PropertyType) 返回的是false。相反,使用 priValue.Equals(Activator.CreateInstance(pi.PropertyType)) 可以得到正确的输出结果。 - Mrchief
1
优雅,但对于像字符串这样的引用类型,它将如何工作,因为它具有这个子句“pi.PropertyType.IsValueType”。 - Raghav

3

我建议将此拆分为多个步骤:分而治之。

首先,将所有对象读入一个大列表中。

其次,使用您的主键(例如公司名称)选择不同的列表。从不同的列表中,使用具有最多已设置字段数量的项目(即主项目)。然后,迭代尚未合并其值的所有字段,并将它们的值合并到主项目中。在许多这些步骤中,LINQ 将帮助您省去编写复杂算法的麻烦。

这样可以轻松地自定义逻辑,例如如果您有一组不同的“主键”,或者您想对特定字段进行特殊比较。


1
尝试创建一个基于字符串的哈希表。使用你认为是重复记录触发器的字段子集的连接作为键。哈希表不允许重复,因此您可以将此错误用作触发器以执行进一步处理。

0

如果不了解您的环境和要求,这可能没有什么用处。但是,如果您有某种数据库后端可用(即使是免费的客户端),您可以将数据存储在表中,并使用SQL Merge语句更新数据。合并操作将根据需要添加或更新记录。触发器可以进一步优化操作。这是一个相当重量级的解决方案,但如果您已经在混合中使用了某些DBMS,则可能是实施它的简单方法。


0
当您从Excel检索数据时,无需为每一行创建一个对象。实际上,您可能希望转到中间形式,预先读取所有行,然后从那里创建对象。Kibbey的使用哈希表的解决方案在这里也可以起作用。

抱歉,为了简便起见,我简化了我的例子。我直接将整个xls读入datatable中,然后创建每行的结构体来处理,而不是直接处理它。 - Jon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接