【问题标题】:The LINQ expression could not be translated for base property无法为基本属性翻译 LINQ 表达式
【发布时间】:2019-03-22 07:46:54
【问题描述】:

我有自定义 OrderBy 实现,它只适用于没有继承的类型,如果我想从我得到的基类型中按字段排序 无法翻译 LINQ 表达式

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=&gt;x.someBaseField),那么它可以毫无问题地工作,所以我的自定义实现必须有一些东西

在错误日志中,我还得到了翻译后的查询,看起来像这样,有趣的是结尾部分

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

【问题讨论】:

  • 我会先尝试一个稍微简单一点的版本,没有“按点分割” - 你有很多工作吗?
  • @JonSkeet 嗨,乔恩,我只是简单地说,但问题是一样的。
  • 好的,很好 - 这简化了事情。如果您打印出orderByExpressionresultExpression,它们看起来像您期望的那样吗?
  • @JonSkeet 对我来说看起来不错,我更新了问题,没有包含所有 lambdas 定义,仅模板
  • 嗯……我也觉得不错。出于兴趣,如果您通过AsQueryable 使用List&lt;T&gt; 作为源,代码是否有效?如果没有,minimal reproducible example 表明这将非常有用。

标签: c# .net-core entity-framework-core


【解决方案1】:

我以前见过这样的事情。编译器生成和手动表达式之间的唯一区别是PropertyInfoReflectedType 属性 - 在编译器生成的代码中它与DeclaringType 相同,在这种情况下是基类,而在PropertyInfo 中通过type.GetProperty是用来获取它的派生类型。

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

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

或者使用这样的辅助方法

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

为了支持嵌套属性,我会添加以下助手

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 表达式 'DbSet.OrderByDescending(f => f.Domain.ShortName)'。它不适用于嵌套属性 - 仅适用于简单属性...有什么想法吗?
  • @Alexander 以上适用于嵌套属性。如果没有看到具体案例,很难告诉您有什么问题 - 可能还有其他因素导致它无法翻译。首先确保手写同样有效。如果是这样,请准备一个可重复的最小示例并发布您自己的问题。
  • 啊,我想通了 :) 我的嵌套属性在 OnModelCreating 中被“忽略”了......对不起。
猜你喜欢
  • 2021-12-28
  • 1970-01-01
  • 2022-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 2022-10-20
  • 2020-09-29
相关资源
最近更新 更多