使用泛型类型执行 EntityFramework Core LINQ 查询

Posted

技术标签:

【中文标题】使用泛型类型执行 EntityFramework Core LINQ 查询【英文标题】:Executing EntityFramework Core LINQ query with generic types 【发布时间】:2022-01-15 12:35:46 【问题描述】:

我正在尝试为使用 EF Core 和泛型从数据库中获取对象的服务实现一些基类。 考虑以下类:

/// <summary>
/// Base entity getter service
/// </summary>
/// <typeparam name="TEntity">The  database entity type</typeparam>
/// <typeparam name="TPK">The primary key type for example: int</typeparam>
class Service<TEntity, TPK>
    protected Func<TEntity, TPK, bool> pkFinder;
    
    public Service(string pkname) 
        var entityExpr = Expression.Parameter(typeof(TEntity), "instance");
        var propertyExpr = Expression.Property(entityExpr, pkname);
        var pkExpr = Expression.Parameter(typeof(TPK), "value");
        var eqExpr = Expression.Equal(propertyExpr, pkExpr);
        pkFinder = Expression.Lambda<Func<TEntity, TPK, bool>>(eqExpr, entityExpr, pkExpr).Compile();
    
    public TEntity Get(TPK pk)
       return ctx.Set<TEntity>().Where(e=>pkFinder.Invoke(e, pk)).SingleOrDefault();
    

pkname 参数是保存该实体主键的属性的名称(通常为“Id”)

ctx 对象是 EF 上下文。

上面的代码(或与此示例非常相似的代码为了清楚起见被过度简化了)失败,Entity Framework 无法翻译表达式。

到目前为止唯一有效的是在每个派生类中为具有实际非泛型类型的 .Where 子句实现覆盖,但是我想避免一遍又一遍地重新实现相同的代码。

另一个想法是让实体类派生自某个基类,但由于它们是自动生成的,这可能会有问题。

任何新想法都将不胜感激。

【问题讨论】:

您已经获得了用于生成表达式以根据已知值检查 ID 的所有代码。只需将其放入您的 Get 方法中,并为您的 pkExpr 使用带有 pk 值的 Expression.Constant 而不是 Expression.Parameter 【参考方案1】:

您已经获得了生成表达式以根据已知值检查 ID 的所有代码。只需将其放入您的 Get 方法中,并为您的 pkExpr 使用带有 pk 值的 Expression.Constant 而不是 Expression.Parameter

我还没有测试过,但是这样的东西应该可以工作:

private readonly string pkname;
public Service(string pkname) 
    this.pkname = pkname;

public TEntity Get(TPK pk)
    var entityExpr = Expression.Parameter(typeof(TEntity), "instance");
    var propertyExpr = Expression.Property(entityExpr, pkname);
    var pkExpr = Expression.Constant(pk, typeof(TPK));
    var eqExpr = Expression.Equal(propertyExpr, pkExpr);
    var pkFinder = Expression.Lambda<Func<TEntity, bool>>(eqExpr, entityExpr);
    return ctx.Set<TEntity>().Where(pkFinder).SingleOrDefault();

【讨论】:

但这不是意味着我必须在每次执行时重新编译表达式吗?还是说 EF 每次都会重新编译? 是的,它有效并且对性能的影响微不足道。非常感谢。其中之一是“我以前怎么没想到……”。应该是很晚了。 ;-) 是的,要回答您之前的问题,表达式根本不需要“编译”。它只需要构建,然后由实体框架解释,如果你有一个硬编码的表达式,所有这些都会发生。如果您查看在代码中说 e =&gt; e.Id == id 时生成的 IL 代码,它实际上是在生成对所有 Expression 方法的调用。

以上是关于使用泛型类型执行 EntityFramework Core LINQ 查询的主要内容,如果未能解决你的问题,请参考以下文章

使用仅在执行时已知的类型参数调用泛型方法[重复]

为啥泛型类型约束不可继承/分层强制执行

使用类型安全定义 typescript 泛型

面向对象设计——“泛型”的起步

Java泛型

Java泛型