使用泛型执行 EntityFramework 核心 LINQ 查询

Executing EntityFramework Core LINQ query with generic types

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

/// <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 无法翻译表达式。

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

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

如有任何新想法,我们将不胜感激。

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

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();
}