Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
775 views
in Technique[技术] by (71.8m points)

c# - The LINQ expression could not be translated for base property

I have custom OrderBy implementation, it only works for types without inheritance, if I want order by field from base type I got The LINQ expression could not be translated

public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
    if (source == null)
    {
        throw new ArgumentNullException(nameof(source));
    }

    if (string.IsNullOrEmpty(orderByProperty))
    {
        throw new ArgumentNullException(nameof(orderByProperty));
    }

    var command = desc ? "OrderByDescending" : "OrderBy";

    var type = typeof(TEntity);

    var param = Expression.Parameter(type, "p");
    var property = type.GetProperty(orderByProperty);
    var propertyAccess = Expression.MakeMemberAccess(param, property);
    var orderByExpression = Expression.Lambda(propertyAccess, param);
    var resultExpression = Expression.Call(
            typeof(Queryable),
            command,
            new Type[] { type, property.PropertyType },
            source.Expression,
            Expression.Quote(orderByExpression));
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery(resultExpression);
}

I am using entityframework core 2.2 but the very interesting thigs is that if I write just source.OrderBy(x=>x.someBaseField) then it works without any problem, so there must by something with my custom implementation

In error log I got also the translated query and it looks like this, intereresting is end part

orderby new SomeType() {NewField = [entity].DbField, Id = [entity].Id}.Id desc

orderByExpression.Body {p => p.Id}

resultExpression

.Call System.Linq.Queryable.OrderByDescending(
    .Call System.Linq.Queryable.Select(
        .Call System.Linq.Queryable.Where(
            .Call System.Linq.Queryable.Where(
                .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MyTypeView]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MyTypeView]),
                '(.Lambda #Lambda1<System.Func`2[MyTypeView,System.Boolean]>)),
            '(.Lambda #Lambda2<System.Func`2[MyTypeView,System.Boolean]>)),
        '(.Lambda #Lambda3<System.Func`2[MyTypeView, MyTypeResult]>))
    ,
    '(.Lambda #Lambda4<System.Func`2[MyTypeResult,System.Guid]>))
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I've seen something like this before. The only difference between compiler generated and manual expression is the ReflectedType property of the PropertyInfo - in compiler generated code it's the same as DeclaringType, which in this case is the base class, while in the PropertyInfo obtained via type.GetProperty it is the derived type used to obtain it.

For some unknown reason (probably a bug) this is confusing EF Core. The workaround is to change the code as follows:

var property = type.GetProperty(orderByProperty);
if (property.DeclaringType != property.ReflectedType)
    property = property.DeclaringType.GetProperty(property.Name);

or use a helper method like this

static PropertyInfo GetProperty(Type type, string name)
{
    for (; type != null; type = type.BaseType)
    {
        var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
        if (property != null) return property;
    }
    return null;
}

In order to support nested properties, I would add the following helpers

static Expression Property(Expression target, string name) =>
    name.Split('.').Aggregate(target, SimpleProperty);

static Expression SimpleProperty(Expression target, string name) =>
    Expression.MakeMemberAccess(target, GetProperty(target.Type, name));

and then use

var propertyAccess = Property(param, orderByProperty);

and

new Type[] { type, orderByExpression.ReturnType },

inside the method in question.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...