创建引用完整表的表达式,而无需访问上下文
本文关键字:访问 上下文 表达式 引用 创建 | 更新日期: 2023-09-27 18:22:36
我的模型类与上下文实例严格分离,并且不知道上下文实例。
每个查询都由表达式(如Expression<Func<Entity, bool>>
(驱动,这些表达式使用 PredicateBuilder
(LinqKit( 进行OR
和AND
组合。表达式创建一次(与上下文无关(,稍后针对新创建的上下文(DbContext
派生类(在不同的线程上执行。
这适用于数十种表达式。但是我不知道如何引用导航属性无法访问的其他表:
工作示例(如果我有上下文变量(:
using(var context = new MyEntities())
{
Expression<Func<Entity1, bool>> filter = e1 =>
context.Set2.Any(e2 => e2.Data.StartsWith(e1.Prefix));
var result = context.Set1.Where(filter).ToList();
}
但是我必须在构建上下文之前创建表达式,因此无法访问context.Set2
。
问题
是否存在另一种语法来替换context.Set2
,例如DbSet<Entity2>.All
或Queryable<Entity2>.All
或类似的东西,不需要上下文实例?
使用导航属性,我已经可以创建与其他表联接的表达式,这可以产生 KB 的 SQL 代码,而无需引用上下文实例 - 所以我绝对看不出为什么现在根本不需要上下文实例。
我已经尝试使用"假"空实例,但这引发了一个空异常:
protected override Expression<Func<Entity1, bool>> BuildFilter()
{
MyEntities fake = null;
return e1 => fake.Set2.Any(e2 => e2.Data.StartsWith(e1.Prefix));
}
回答自己/我会尝试的路
由于我已经在使用 PredicateBuilder/LinqKit,我将通过参数将上下文传递到表达式本身中,并在我真正有一个上下文实例时对其进行扩展 - 到目前为止,LinqPad 中的预测试表现良好:
Expression<Func<MyEntities, Entity1, bool>> filterTemplate = (ctx, e1) =>
ctx.Set2.Any(e2 => e2.Data.StartsWith(e1.Prefix));
using(var context = new MyEntities())
{
Expression<Func<Entity1, bool>> filter = e1 =>
filterTemplate.Invoke(context, e1); // .Invoke() -> LinqKit
var result = context.Set1.Where(filter.Expand()).ToList(); // .Expand() -> LinqKit
// or
var result = context.Set1.AsExpandable().Where(filter).ToList(); // .AsExpandable() -> LinqKit
}
至少在生产者端类层次结构中,我可以以不间断的方式引入它,因此不必更改现有的 1000 个表达式,新方法仅创建第 1001 个表达式:
以前
protected abstract Expression<Func<Entity1, bool>> BuildFilter();
后
protected virtual Expression<Func<Entity1, bool>> BuildFilter()
{
throw new NotImplementedException($"Either {nameof(this.BuildFilter)} or {nameof(this.BuildFilter2)} has to be overridden!");
}
protected virtual Expression<Func<MyEntities, Entity1, bool>> BuildFilter2()
{
var legacyFilter = this.BuildFilter();
Expression<Func<MyEntities, Entity1, bool>> result = (context, e1) => legacyFilter.Invoke(e1);
return result.Expand();
}