Общее выражение для предложения where - "Тип LINQ-выражения" Invoke "не поддерживается в LINQ to Entities".

Я пытаюсь написать действительно общий способ загрузки сущностей EF в пакетах, используя метод Contains для генерации оператора SQL IN. У меня это работает, если я передаю все выражение целиком, но когда я пытаюсь построить выражение динамически, я получаю "Тип узла выражения LINQ" Invoke "не поддерживается в LINQ to Entities". Итак, я знаю, что это означает, что 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 'Invoke' не поддерживается в LINQ to Entities."

Но если я изменю это так:

Объявление функции:

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 понять более общую версию?

1 ответ

Решение

Проблема, как вы описываете, заключается в том, что функция entityKey в первом примере непрозрачна, поскольку она имеет тип Func, а не Expression. Однако вы можете получить желаемое поведение, реализовав метод Compose() для объединения двух выражений. Я разместил код для реализации compose в этом вопросе: используйте Expression> в Linq содержит расширение.

С помощью 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();
} 
Другие вопросы по тегам