Как использовать предложение where с IQueryable <dynamic> - PullRequest
2 голосов
/ 18 июня 2020

Я использую Entity Framework Core с отражением для динамического создания некоторых форм. Все работает, кроме предложения WHERE. Я получаю следующую ошибку:

Дерево выражения может не содержать динамическую c операцию

Я могу исправить это, преобразовав свой IQueryable в список, но это создает различные проблемы, которых я бы хотел избежать.

Вот мой код:

public async void ViewCollection(PropertyInfo propertyInfo)
{
    Type propertyType = propertyInfo.PropertyType;
    InversePropertyAttribute inversePropertyAttribute = (InversePropertyAttribute)ReflectionHelpers.GetAttribute(propertyInfo, typeof(InversePropertyAttribute));


    //GET THE TYPE OF THE COLLECTION
    Type collectionType = propertyInfo.PropertyType.GenericTypeArguments[0];

    //GET THE INVERSE PROPERTY INFO
    PropertyInfo inverseProperty = collectionType.GetProperty(inversePropertyAttribute.Property);

    //GET THE FOREIGN KEY ATTRIBUTE FROM THE INVERSE PROPERTY
    ForeignKeyAttribute foreignKeyAttribute = (ForeignKeyAttribute)ReflectionHelpers.GetAttribute(inverseProperty, typeof(ForeignKeyAttribute));

    //GET THE FOREIGN KEY PROPERTY FROM THE FOREIGN KEY ATTRIBUTE
    PropertyInfo foreignKeyProperty = collectionType.GetProperty(foreignKeyAttribute.Name);

    //GET INCLUDED TYPE NAMES BY FOREIGN KEY
    IEnumerable<string> includedTypes = collectionType.GetProperties().Where(p => p.PropertyType.IsClass).Where(p => ReflectionHelpers.HasAttribute(p, typeof(ForeignKeyAttribute))).Select(r => r.Name);

    //GET THE DATA SET
    IQueryable<dynamic> items = ReflectionHelpers.GetDbCollectionByType(Db, collectionType);

    //INCLUDE THE INCLUDED TYPES BY NAME
    foreach (string includedType in includedTypes) items = items.Include(includedType);

    //THIS IS WHERE THE ERROR IS
    items = items.Where(i => foreignKeyProperty.GetValue(i, null) == PrimaryKeyProperty.GetValue(Item, null));

    await ShowCollection(collectionType, items, propertyInfo.Name);
}

Как я могу решить эту проблему, не меняя свой тип на список?

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Вы не можете строить запросы, превышающие dynamic, потому что это не поддерживается изначально в деревьях выражений. Вместо этого вы должны основывать свою работу на non generi c IQueryable или generi c IQueryable<object>. например, если ReflectionHelpers.GetDbCollectionByType вызывает DbContext.Set<T> динамически (аналогично здесь Таблица динамического доступа в EF Core 2.0 ), вы должны иметь возможность преобразовать его в IQueryable<object>:

var items = (IQueryable<object>)ReflectionHelpers.GetDbCollectionByType(Db, collectionType);

Чтобы добавить предложение Where, вы не должны использовать вызовы отражения внутри выражения предиката, а строите его динамически, используя методы класса Expression (из пространства имен System.Linq.Expressions). Примерно так:

// (object i) => (({collectionType})i).{ForeignKey} == Item.{PrimaryKey}
var parameter = Expression.Parameter(typeof(object), "i");
var body = Expression.Equal(
    Expression.Property(Expression.Convert(parameter, collectionType), foreignKeyProperty),
    Expression.Property(Expression.Constant(Item), PrimaryKeyProperty));
var predicate = Expression.Lambda<Func<object, bool>>(body, parameter);

а потом

items = items.Where(predicate); // still IQueryable<object>
1 голос
/ 18 июня 2020

Вы не можете. IQueryable является частью интегрированного запроса языка (LINQ) - он статически типизирован, поэтому вы не можете использовать типы значений.

За кулисами IQueryable - это дерево выражений, которое представляет запрос, который еще не был выполнен. Часть дерева выражения представляет запрос. Типы stati c представляют данные, над которыми работает запрос.

Лучшая альтернатива - построить дерево выражений вручную, используя API дерева выражений .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...