创建引用完整表的表达式,而无需访问上下文

本文关键字:访问 上下文 表达式 引用 创建 | 更新日期: 2023-09-27 18:22:36

我的模型类与上下文实例严格分离,并且不知道上下文实例。

每个查询都由表达式(如Expression<Func<Entity, bool>>(驱动,这些表达式使用 PredicateBuilder (LinqKit( 进行ORAND组合。表达式创建一次(与上下文无关(,稍后针对新创建的上下文(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>.AllQueryable<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();
    }