2013-11-25 57 views
1

我一直在一个项目工作一段时间来解析csv文件中的条目列表并使用该数据来更新数据库。C#比较复杂的对象返回差异列表

对于每个条目,我创建一个新的用户实例,我把它放在一个集合中。现在我想迭代该集合并将用户条目与数据库中的用户进行比较(如果存在)。我的问题是,如何将用户(条目)对象与用户(db)对象进行比较,并返回具有差异的列表?

对于从数据库中生成例如以下类:

public class User 
{ 
    public int ID { get; set; } 
    public string EmployeeNumber { get; set; } 
    public string UserName { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public Nullable<int> OfficeID { get; set; } 

    public virtual Office Office { get; set; } 
} 

public class Office 
{ 
    public int ID { get; set; } 
    public string Code { get; set; } 

    public virtual ICollection<User> Users { get; set; } 
} 

为了一些查询保存到数据库中,我只是填补我可以从CSV文件中检索的属性,所以ID的(例如)不可用于平等检查。

有没有什么办法可以比较这些对象,而无需为每个属性定义规则并返回修改的属性列表?我知道这个问题似乎与以前的一些帖子类似。我已经阅读了很多,但由于我对编程没有经验,所以我会很感激一些建议。

从我读过的内容中,我应该将'一般性比较属性'与'忽略使用数据注释的属性'和'返回CompareResults列表'结合起来?

+1

为了更好地理解您的问题,如果您的CSV文件中没有该ID,那么您将用什么作为用户的唯一标识符?即您何时决定不创建新条目? –

+0

噢,对不起,我遗漏了一些属性来将这个例子稍微扁平化。我将编辑示例。唯一标识符是一个员工编号,这对于决定是否在数据库中创建新用户当然是必需的。 – Leegaert

+0

你见过[比较两个对象并找到差异](http://stackoverflow.com/questions/4951233/compare-two-objects-and-find-the-differences)? –

回答

0

有一些可以解决这个几种方法:
方法#1是为CSV文件的内容创建单独的DTO样式类。虽然这涉及到创建具有许多类似字段的新类,但它将CSV文件格式从数据库中分离出来,并让您能够在不影响其他部分的情况下更改它们。为了实现比较,你可以创建一个Comparer类。只要类几乎相同,比较就可以从DTO类中获取所有属性并动态实现比较(例如,通过创建和评估包含类型为Equal的BinaryExpression的Lambda表达式)。
方法#2避免了DTO,但使用属性来标记属于比较的属性。您需要创建一个您分配给相关属性的自定义属性。在比较中,您将分析该类的所有属性并过滤出用该属性标记的属性。为了比较属性,你可以使用与#1相同的方法。这种方法的缺点是你将比较逻辑与数据类紧密地结合在一起。如果你需要实现几个不同的比较,你会用这些属性混淆数据类。
当然,#1会比#2产生更大的努力。我明白,这不是你正在寻找的,但也许有一个单独的,强类型的对比课也是一个可以考虑的方法。
更多有关动态比较算法的详细信息:它基于反射以获取需要比较的属性(取决于您获得DTO属性或相关数据类的属性的方法)。一旦拥有属性(对于DTO,属性应该具有相同的名称和数据类型),您可以创建LamdaExpression并动态编译和评估它。下列行示出了示例代码的摘录:

public static bool AreEqual<TDTO, TDATA>(TDTO dto, TDATA data) 
{ 
    foreach(var prop in typeof(TDTO).GetProperties()) 
    { 
     var dataProp = typeof(TDATA).GetProperty(prop.Name); 
     if (dataProp == null) 
      throw new InvalidOperationException(string.Format("Property {0} is missing in data class.", prop.Name)); 
     var compExpr = GetComparisonExpression(prop, dataProp); 
     var del = compExpr.Compile(); 
     if (!(bool)del.DynamicInvoke(dto, data)) 
      return false; 
    } 
    return true; 
} 

private static LambdaExpression GetComparisonExpression(PropertyInfo dtoProp, PropertyInfo dataProp) 
{ 
    var dtoParam = Expression.Parameter(dtoProp.DeclaringType, "dto"); 
    var dataParam = Expression.Parameter(dataProp.DeclaringType, "data"); 
    return Expression.Lambda(
     Expression.MakeBinary(ExpressionType.Equal, 
      Expression.MakeMemberAccess(
       dtoParam, dtoProp), 
      Expression.MakeMemberAccess(
       dataParam, dataProp)), dtoParam, dataParam); 
} 

对于全样本,请参阅本link。请注意,这种动态方法只是一个简单的实现,有待改进的余地(例如,不检查属性的数据类型)。它也只是检查是否相等,不收集不相等的属性;但这应该很容易转移。
虽然动态方法很容易实现,但运行时错误的风险比强类型方法要大。

+0

感谢您的输入Markus。我从单独的DTO课程开始,我会再看看它。你能否详细说一下比较级课程,你有没有让我失望。 – Leegaert

+0

我已将我的答案扩展为显示简单的动态比较。 – Markus

+0

感谢您的示例。我必须承认GetComparisonExpression部分高于我的头。我会和它一起玩一下,看看我是否可以绕过它,稍后我会发布一个更新。 – Leegaert