C#比较返回差异列表的复杂对象

时间:2013-11-25 15:16:31

标签: c# object instance equality

我一直在研究项目一段时间来解析csv文件中的条目列表并使用该数据来更新数据库。

对于每个条目,我创建了一个我放入集合的新用户实例。现在我想迭代该集合并将数据库中的用户条目与用户进行比较(如果存在)。我的问题是,如何将该用户(条目)对象与用户(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 个答案:

答案 0 :(得分: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。请注意,这种动态方法只是一个简单的实现,留有改进的余地(例如,没有检查属性的数据类型)。它也只检查相等性,不收集不相等的属性;但这应该很容易转移。
虽然动态方法易于实现,但运行时错误的风险大于强类型方法。