Все сгенерированные выражения во время выполнения EF Core 3.1 завершаются сбоем, поскольку невозможно обработать запрос на сервере - PullRequest
2 голосов
/ 30 января 2020

В прошлом мы могли использовать код, подобный следующему:

public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
    // LAMBDA: x => x.[PropertyName]
    var parameter = Expression.Parameter(typeof(TSource), "x");
    Expression property = Expression.Property(parameter, propertyName);
    var lambda = Expression.Lambda(property, parameter);

    // REFLECTION: source.OrderBy(x => x.Property)
    var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
    var orderByGeneric = orderByMethod.MakeGenericMethod(typeof(TSource), property.Type);
    var result = orderByGeneric.Invoke(null, new object[] { source, lambda });

    return (IOrderedQueryable<TSource>)result;
}

Чтобы сделать заказ, но теперь это не работает в EF Core 3.1

И использовать в source.QueryBy (x => EF.Property (x, "Name")) дает неправильное использование ошибки EF.Property, поэтому я в растерянности относительно того, как я могу избежать этих ошибок и по-прежнему создавать динамическое c выражение, которое пытается на IQueryable .

Что я делаю не так?

1 Ответ

1 голос
/ 31 января 2020

Вы не делаете ничего плохого - EF Core делает это неправильно "для вас": - (

Не уверен, что вы подразумеваете под "в прошлом", но у EF Core есть проблемы с некоторыми, что они называют «не сгенерированные компилятором» выражения с самого начала, такие как следующие сообщения SO

Как использовать унаследованные свойства в выражениях EF Core?

Почему Linq "где "выражение после выбора вычисляется локально при создании с помощью метода generi c?

Не удалось преобразовать выражение LINQ для базового свойства

и многих других GitHub выдает такие сообщения, как

Построение фильтра и / или OrderBy с помощью выражений для бросков промежуточного типа memberInfo в иерархии для сокращения MemberInitExpression # 19182 , но, к сожалению, не включен в 3.1, поэтому мы должны ждать 5.0 в конце года (для изменения 1 строки!).

До тех пор вы должны использовать упомянутые обходные пути, например, использовать следующую Expression.Property замену (в основном код из Как использовать унаследованные свойства в выражениях EF Core? ):

static MemberExpression Property(Expression expression, string propertyName)
{
    var propertyInfo = expression.Type.GetProperty(propertyName);
    if (propertyInfo.ReflectedType != propertyInfo.DeclaringType)
        propertyInfo = propertyInfo.DeclaringType.GetProperty(propertyName);
    return Expression.MakeMemberAccess(expression, propertyInfo);  
}

Например,

var property = Property(propertyName);

должен решить проблему.


Не имеет отношения, но вы также можете улучшить свой метод двумя способами.

Во-первых, вы можете легко добавить поддержку вложенных свойств через строку, разделенную точками, например «Prop1.Prop2.Prop3», используя

var property = propertyName.Split(".")
    .Aggregate((Expression)parameter, Property);

Во-вторых, вместо отражения вы можете просто испустить Expression.Call в Queryable.OrderBy.

Весь метод может быть таким:

public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
    var parameter = Expression.Parameter(source.ElementType, "x");
    var property = propertyName.Split(".")
        .Aggregate((Expression)parameter, Property);
    var selector = Expression.Lambda(property, new[] { parameter });
    var expression = Expression.Call(typeof(Queryable), nameof(Queryable.OrderBy),
        new[] { source.ElementType, property.Type },
        new[] { source.Expression, Expression.Quote(selector) });
    return (IOrderedQueryable<TSource>)source.Provider.CreateQuery(expression);
}
...