LINQ表达式无法转换为基本属性 [英] The LINQ expression could not be translated for base property

查看:206
本文介绍了LINQ表达式无法转换为基本属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有自定义的OrderBy实现,它仅适用于没有继承的类型,如果我想按基本类型的字段进行排序,则无法翻译LINQ表达式

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);
}

我使用的是EntityFramework Core 2.2,但非常有趣的想法是,如果我写只需 source.OrderBy(x => x.someBaseField),它就可以正常工作,因此必须通过我的自定义实现来实现

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]>))


推荐答案

我以前见过类似的东西。编译器生成和手动表达式之间的唯一区别是 PropertyInfo ReflectedType 属性-在编译器生成的代码中相同为 DeclaringType ,在本例中为基类,而在通过获得的 PropertyInfo 中type.GetProperty 是用于获取它的派生类型。

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.

由于某些未知原因(可能是错误),这使EF Core感到困惑。解决方法是更改​​代码,如下所示:

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));

然后使用

var propertyAccess = Property(param, orderByProperty);

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

这篇关于LINQ表达式无法转换为基本属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆