linq - where 子句的通用表达式 - "The LINQ expression node type ' Invoke' 在 LINQ to Entities 中不受支持。”

标签 linq entity-framework generics linq-to-sql expression-trees

我正在尝试编写一种非常通用的方法来批量加载 EF 实体,使用 Contains 方法生成 SQL IN 语句。如果我传递整个表达式,我就可以正常工作,但是当我尝试动态构建表达式时,我收到“LINQ to Entities 不支持 LINQ 表达式节点类型‘Invoke’”。所以我知道这意味着 EF 认为我正在调用一个任意方法并且它无法将其转换为 SQL,但我无法弄清楚如何让它理解底层表达式。

所以如果我做这样的事情(只显示相关的片段):

函数声明:

public static List<T> Load<T>(IQueryable<T> entityQuery, int[] entityIds, Func<T, int> entityKey, int batchSize = 500, Func<T, bool> postFilter = null) where T : EntityObject
{
    var retList = new List<T>();

    // Append a where clause to the query passed in, that will use a Contains expression, which generates a SQL IN statement.  So our SQL looks something like
    // WHERE [ItemTypeId] IN (1921,1920,1922)
    // See http://rogeralsing.com/2009/05/21/entity-framework-4-where-entity-id-in-array/ for details
    Func<int[], Expression<Func<T, bool>>> containsExpression = (entityArray => (expr => entityArray.Contains(entityKey(expr))));

    // Build a new query with the current batch of IDs to retrieve and add it to the list we are returning
    newQuery = entityQuery.Where<T>(containsExpression(entityIds));
    retList.AddRange(newQuery.ToList());

    return retList;
} 

调用函数:
var entities = BatchEntity.Load<ItemType>(from eItemType in dal.Context.InstanceContainer.ItemTypes
select eItemType
, itemTypeData
, (ek => ek.ItemTypeId)
);

我收到“LINQ to Entities 不支持 LINQ 表达式节点类型‘Invoke’。”

但是如果我把它改成这样:

函数声明:
public static List<T> Load<T>(IQueryable<T> entityQuery, int[] entityIds, Func<int[], Expression<Func<T, bool>>> containsExpression, int batchSize = 500, Func<T, bool> postFilter = null) where T : EntityObject
{
    var retList = new List<T>();

    // Build a new query with the current batch of IDs to retrieve and add it to the list we are returning
    newQuery = entityQuery.Where<T>(containsExpression(entityIds));
    retList.AddRange(newQuery.ToList());

    return retList;
} 

调用函数:
var entities = BatchEntity.Load<ItemType>(from eItemType in dal.Context.InstanceContainer.ItemTypes
select eItemType
, itemTypeData
, (entityArray => (ek => entityArray.Contains(ek.ItemTypeId)))
);

它工作正常。有什么办法可以让 EF 理解更通用的版本?

最佳答案

正如您所描述的,问题在于第一个示例中的 entityKey 函数是不透明的,因为它属于 Func 而不是 Expression 类型。但是,您可以通过实现 Compose() 方法来组合两个表达式来获得所需的行为。我在这个问题中发布了实现 compose 的代码:use Expression<Func<T,X>> in Linq contains extension .

实现 Compose() 后,您的函数可以实现如下:

public static List<T> Load<T>(this IQueryable<T> entityQuery, 
                              int[] entityIds, 
                              // note that this is an expression now
                              Expression<Func<T, int>> entityKey, 
                              int batchSize = 500, 
                              Expression<Func<T, bool>> postFilter = null) 
    where T : EntityObject
{
    Expression<Func<int, bool>> containsExpression = id => entityIds.Contains(id);
    Expression<Func<T, bool>> whereInEntityIdsExpression = containsExpression.Compose(entityKey);

    IQueryable<T> filteredById = entityQuery.Where(whereInEntityIdsExpression);

    // if your post filter is compilable to SQL, you might as well do the filtering
    // in the database
    if (postFilter != null) { filteredById = filteredById.Where(postFilter); }

    // finally, pull into memory
    return filteredById.ToList();
} 

关于linq - where 子句的通用表达式 - "The LINQ expression node type ' Invoke' 在 LINQ to Entities 中不受支持。”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12755022/

相关文章:

c# - 使用 Linq 选择列表中的一系列成员

c# - 使用 LINQ 对分层数据进行分组

entity-framework - 清除 EntityCollection 然后在同一事务中添加到它似乎是不可能的

c# - 将实体集合拆分为 n 个部分

c# - Java 的使用站点差异与 C# 的声明站点差异相比如何?

c++ - 如何将 N 个通用参数传递给 typedef 函数指针?

c# - Entity Framework Core linq 查询返回 InvalidCastException

c# - 如何删除对其他层中 Entity Framework 的引用

java - 在 Java 中获取参数化类的实例

c# - 如何在 C# Linq 的 Expressions.Expression 中用空字符串替换 null