我有一个包含约20个属性的组件的数据库。为了找出是否需要更新,我想检查两个对象的所有属性(DateCreated和Id除外)是否匹配。 如果所有都没有更新,否则更新数据库。
Component comp_InApp = new Component()
{
Id = null,
Description = "Commponent",
Price = 100,
DateCreated = "2019-01-30",
// Twenty more prop
};
Component comp_InDb = new Component()
{
Id = 1,
Description = "Component",
Price = 100,
DateCreated = "2019-01-01",
// Twenty more prop
};
// Check if all properties match, except DateCreated and Id.
if (comp_InApp.Description == comp_InDb.Description &&
comp_InApp.Price == comp_InDb.Price
// Twenty more prop
)
{
// Everything up to date.
}
else
{
// Update db.
}
这可行,但是用20个属性并不是一种很干净的方法。有没有更好的方法可以更干净地获得相同的结果?
答案 0 :(得分:3)
当我不想/没有时间编写自己的Equals
和GetHashCode
方法时,我正在使用DeepEqual。
您可以使用以下方法从NuGet进行简单安装:
Install-Package DeepEqual
并像这样使用它:
if (comp_InApp.IsDeepEqual(comp_InDb))
{
// Everything up to date.
}
else
{
// Update db.
}
但是请记住,这仅适用于您要显式比较对象的情况,不适用于想要从List
删除对象的情况或类似情况(当{{ 1}}和Equals
被调用。
答案 1 :(得分:2)
一种方法,创建一个实现IEqualityComparer<Component>
的类以封装此逻辑,并避免修改类Comparer
本身(如果您不希望全部使用Equals
逻辑)时间)。然后,您可以将其用于Equals
的两个实例的简单Component
,甚至用于将其接受为附加参数的所有LINQ methods。
class ComponentComparer : IEqualityComparer<Component>
{
public bool Equals(Component x, Component y)
{
if (object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Price == y.Price && x.Description == y.Description;
}
public int GetHashCode(Component obj)
{
unchecked
{
int hash = 17;
hash = hash * 23 + obj.Price.GetHashCode();
hash = hash * 23 + obj.Description?.GetHashCode() ?? 0;
// ...
return hash;
}
}
}
您的简单用例:
var comparer = new ComponentComparer();
bool equal = comparer.Equals(comp_InApp, comp_InDb);
如果您有两个集合并想知道它们之间的区别,它也可以工作,例如:
IEnumerable<Component> missingInDb = inAppList.Except( inDbList, comparer );
答案 2 :(得分:1)
以下是反射解决方案:
>>> import numpy as np
>>> x = np.array([1,0,2,0,3,0,0,4,0,5,0,6]).reshape(4, 3)
>>> np.nonzero(x==0) # this is what you want
(array([0, 1, 1, 2, 2, 3]), array([1, 0, 2, 0, 2, 1]))
>>> np.nonzero(x)
(array([0, 0, 1, 2, 3, 3]), array([0, 2, 1, 1, 0, 2]))
答案 3 :(得分:0)
您可以使用反射,但是这可能会使您的应用程序变慢。创建该比较器的另一种方法是使用Linq Expressions生成它。尝试以下代码:
public static Expression<Func<T, T, bool>> CreateAreEqualExpression<T>(params string[] toExclude)
{
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !toExclude.Contains(p.Name))
.ToArray();
var p1 = Expression.Parameter(type, "p1");
var p2 = Expression.Parameter(type, "p2");
Expression body = null;
foreach (var property in props)
{
var pare = Expression.Equal(
Expression.PropertyOrField(p1, property.Name),
Expression.PropertyOrField(p2, property.Name)
);
body = body == null ? pare : Expression.AndAlso(body, pare);
}
if (body == null) // all properties are excluded
body = Expression.Constant(true);
var lambda = Expression.Lambda<Func<T, T, bool>>(body, p1, p2);
return lambda;
}
它将生成一个看起来像
的表达式(Component p1, Component p2) => ((p1.Description == p2.Description) && (p1.Price == p2.Price))
用法很简单
var comporator = CreateAreEqualExpression<Component>("Id", "DateCreated")
.Compile(); // save compiled comparator somewhere to use it again later
var areEqual = comporator(comp_InApp, comp_InDb);
编辑:要使其更加安全,您可以使用Lambda排除属性
public static Expression<Func<T, T, bool>> CreateAreEqualExpression<T>(
params Expression<Func<T, object>>[] toExclude)
{
var exclude = toExclude
.Select(e =>
{
// for properties that is value types (int, DateTime and so on)
var name = ((e.Body as UnaryExpression)?.Operand as MemberExpression)?.Member.Name;
if (name != null)
return name;
// for properties that is reference type
return (e.Body as MemberExpression)?.Member.Name;
})
.Where(n => n != null)
.Distinct()
.ToArray();
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !exclude.Contains(p.Name))
.ToArray();
/* rest of code is unchanged */
}
现在使用它时,我们已经有了IntelliSense支持:
var comparator = CreateAreEqualExpression<Component>(
c => c.Id,
c => c.DateCreated)
.Compile();