构建动态表达式以比较两个 table
Building dynamic expression to compare between two table
我有两个 tables,table A 和 table B,它们具有相同的方案。
我想比较来自不同上下文的两个 table。如果 table B 中的记录在 table A 中不存在,那么它将把它插入到 A 上下文中。
什么我搞清楚单型table长得像
var a = context.Set<T>().AsEnumerable();
var b = context2.Set<T>().AsEnumerable();
var NotExistsOnA = b.Where(bRecord =>
!a.Any(aRecord =>
bRecord.xxxId.Equals(aRecord.xxxId))).ToList();
如何创建动态表达式以使代码可重用,因为有几个 table 将用于比较。我坚持如何处理 xxxId 部分,因为不同的 table 将具有不同的主键名称。
非常感谢任何帮助。
EF Core 元数据服务可用于获取有关实体主键的信息,例如
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
然后这些信息可用于构建动态相等比较器:
public static Expression<Func<T, T, bool>> GetPrimaryKeyCompareExpression<T>(this DbContext context)
{
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
var first = Expression.Parameter(typeof(T), "first");
var second = Expression.Parameter(typeof(T), "second");
var body = primaryKey.Properties
.Select(p => Expression.Equal(
Expression.Property(first, p.PropertyInfo),
Expression.Property(second, p.PropertyInfo)))
.Aggregate(Expression.AndAlso); // handles composite PKs
return Expression.Lambda<Func<T, T, bool>>(body, first, second);
}
这又可以编译为委托并在 LINQ to Objects 中使用(因为您使用的是 IEnumerable
s)查询,例如
var setA = context.Set<T>().AsEnumerable();
var setB = context2.Set<T>().AsEnumerable();
var comparePKs = context.GetPrimaryKeyCompareExpression<T>().Compile();
var notExistsOnA = setB
.Where(b => !setA.Any(a => comparePKs(a, b)))
.ToList();
但请注意,在 LINQ to Objects 中,!Any(...)
内部查询条件效率低下,因为它是线性搜索操作,因此产生的时间复杂度是二次方 (O(Na * Nb
),因此您将更大的数据集有性能问题。
所以一般来说,使用连接运算符会更好。但不是比较,它需要键选择器,它也可以像上面那样构建,但这次发出 Tuple
个实例(为了处理复合 PK),例如
public static Expression<Func<T, object>> GetPrimaryKeySelector<T>(this DbContext context)
{
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
var item = Expression.Parameter(typeof(T), "item");
var body = Expression.Call(
typeof(Tuple), nameof(Tuple.Create),
primaryKey.Properties.Select(p => p.ClrType).ToArray(),
primaryKey.Properties.Select(p => Expression.Property(item, p.PropertyInfo)).ToArray()
);
return Expression.Lambda<Func<T, object>>(body, item);
}
并且可以如下使用
var selectPK = context.GetPrimaryKeySelector<T>().Compile();
var notExistsOnA = setB
.GroupJoin(setA, selectPK, selectPK, (b, As) => (b, As))
.Where(r => !r.As.Any())
.Select(r => r.b)
.ToList();
我有两个 tables,table A 和 table B,它们具有相同的方案。
我想比较来自不同上下文的两个 table。如果 table B 中的记录在 table A 中不存在,那么它将把它插入到 A 上下文中。
什么我搞清楚单型table长得像
var a = context.Set<T>().AsEnumerable();
var b = context2.Set<T>().AsEnumerable();
var NotExistsOnA = b.Where(bRecord =>
!a.Any(aRecord =>
bRecord.xxxId.Equals(aRecord.xxxId))).ToList();
如何创建动态表达式以使代码可重用,因为有几个 table 将用于比较。我坚持如何处理 xxxId 部分,因为不同的 table 将具有不同的主键名称。
非常感谢任何帮助。
EF Core 元数据服务可用于获取有关实体主键的信息,例如
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
然后这些信息可用于构建动态相等比较器:
public static Expression<Func<T, T, bool>> GetPrimaryKeyCompareExpression<T>(this DbContext context)
{
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
var first = Expression.Parameter(typeof(T), "first");
var second = Expression.Parameter(typeof(T), "second");
var body = primaryKey.Properties
.Select(p => Expression.Equal(
Expression.Property(first, p.PropertyInfo),
Expression.Property(second, p.PropertyInfo)))
.Aggregate(Expression.AndAlso); // handles composite PKs
return Expression.Lambda<Func<T, T, bool>>(body, first, second);
}
这又可以编译为委托并在 LINQ to Objects 中使用(因为您使用的是 IEnumerable
s)查询,例如
var setA = context.Set<T>().AsEnumerable();
var setB = context2.Set<T>().AsEnumerable();
var comparePKs = context.GetPrimaryKeyCompareExpression<T>().Compile();
var notExistsOnA = setB
.Where(b => !setA.Any(a => comparePKs(a, b)))
.ToList();
但请注意,在 LINQ to Objects 中,!Any(...)
内部查询条件效率低下,因为它是线性搜索操作,因此产生的时间复杂度是二次方 (O(Na * Nb
),因此您将更大的数据集有性能问题。
所以一般来说,使用连接运算符会更好。但不是比较,它需要键选择器,它也可以像上面那样构建,但这次发出 Tuple
个实例(为了处理复合 PK),例如
public static Expression<Func<T, object>> GetPrimaryKeySelector<T>(this DbContext context)
{
var entityType = context.Model.FindEntityType(typeof(T));
var primaryKey = entityType.FindPrimaryKey();
var item = Expression.Parameter(typeof(T), "item");
var body = Expression.Call(
typeof(Tuple), nameof(Tuple.Create),
primaryKey.Properties.Select(p => p.ClrType).ToArray(),
primaryKey.Properties.Select(p => Expression.Property(item, p.PropertyInfo)).ToArray()
);
return Expression.Lambda<Func<T, object>>(body, item);
}
并且可以如下使用
var selectPK = context.GetPrimaryKeySelector<T>().Compile();
var notExistsOnA = setB
.GroupJoin(setA, selectPK, selectPK, (b, As) => (b, As))
.Where(r => !r.As.Any())
.Select(r => r.b)
.ToList();