动态 lambda 表达式 (OrderBy) 和可为空的 属性 类型

Dynamic lambda expression (OrderBy) and nullable property type

我正在尝试动态创建表达式,通过 Entity Framework 对数据库中的数据进行排序。但是我遇到了一个问题,无法克服。也许让我解释一下我想做什么。我的目标是创建这样的表达式:

x => x.Property

其中 "Property" 是我想动态指定的 属性 的名称。

现在,让我们转到 class,它代表数据库中的 table(我将其简化以使事情更清楚):

public class MyModelClass
{
    public long MyLongProperty { get; set; }
    public decimal? MyNullableDecimalProperty { get; set; } // IMPORTANT: it's nullable
}

这是我的代码,我在其中尝试创建前面描述的表达式:

// Db is EntityFramework context
IQueryable<MyModelClass> list = Db.MyModels.Select(x => x);

// x =>
var argument = Expression.Parameter(list.ElementType, "x");

// x.MyNullableDecimalProperty
var propertyToOrder = Expression.Property(argument, "MyNullableDecimalProperty");

// x => x.MyNullableDecimalProperty
var finalExpression = Expression.Call(
    typeof (Queryable),
    "OrderBy",
    new[] { list.ElementType, typeof(IComparable) },
    list.Expression,
    Expression.Lambda<Func<MyModelClass, IComparable>>(propertyToOrder, argument));

list = list.Provider.CreateQuery<MyModelClass>(finalExpression);

问题出现在第 4 个语句 (var finalExpression = Expression.Call(...))。我得到一个例外:

Expression of type „System.Nullable`1[System.Decimal]” cannot be used for return type „System.IComparable”.

据我所知,问题出在我使用 "IComparable" 类型,其中 "MyNullableDecimalProperty" 是 Nullable 并且 Nullable 不使用 IComparable 接口。当我按 "MyLongProperty" 订购或替换 "IComparable".

时,不会抛出异常

所以我的问题:

  1. 我应该使用什么类型来使其与任何可为 null 的属性一起工作?

  2. 是否可以使用一种类型并且它适用于所有属性,无论它们是可为空还是不可为空。

注意:我知道我可以用于ex。动态 Linq 库,但我对此解决方案不感兴趣 - 我想学习如何在不使用第 3 方库的情况下克服它。

您不需要指定 IComparable 部分,您也可以使用 Queryable.OrderBy / OrderByDescending 方法在这里帮助您:

IQueryable<TSource> source = .....
var sourceType = typeof(TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = GetProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
orderBy = Expression.Lambda(orderByProperty, new[] { parameter });

return Queryable.OrderBy(source, (dynamic)orderBy)

试一试,看看进展如何,我很确定这对本机类型和可空类型都有效。

没有理由使用 IComparable。事实上,许多可比较的类型并没有实现 IComparable。只需使用您传递的任何内容的运行时类型:

var finalExpression = Expression.Call(
    typeof (Queryable),
    "OrderBy",
    new[] { list.ElementType, propertyToOrder.Type },
    list.Expression,
    Expression.Lambda(propertyToOrder, new [] { argument }));