如何指定直到运行时才知道的谓词类型?

本文关键字:谓词 类型 何指定 运行时 | 更新日期: 2023-09-27 18:17:20

我的repository方法从数据库中获取内容。它接受排序顺序作为参数:

IEnumerable<Car> getCars<TSortKey>(Expression<Func<Car, TSortKey>> sort);

我使用TSortKey,因为我不知道哪个属性将被使用,直到运行时,它可以是x => x.Namex => x.Make这是字符串,但它也可以是x => x.History.Age这是一个整数。

用户选择排序顺序,然后我在switch中设置排序谓词并调用该方法。

  Expression<Func<Car, object>> sortPredicate;
  switch (sortOption) {
    case SortOption.Name: sortPredicate = s => s.Name; break;
    case SortOption.Make: sortPredicate = s => s.Make; break;
    case SortOption.Age:  sortPredicate = s => s.History.Age; break;
    default:              sortPredicate = s => s.Name; break;
    }
  var cars = repo.getCars(sortPredicate);

我在谓词中使用object,因为直到运行时我才知道类型。但是这会生成错误的SQL,并抛出。

那么我该如何解决这个问题呢?

如何指定直到运行时才知道的谓词类型?

问题是Expression<Func<T, object>>为值类型属性生成额外的Convert, EF不喜欢这一点,并抛出NotSupportedException

您可以在存储库类中使用以下帮助器方法,而不是OrderBy。它所做的是在需要时剥离Convert表达式,并动态调用Queryable.OrderBy方法:

public static partial class EFExtensions
{
    public static IOrderedQueryable<T> SortBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector)
    {
        var body = keySelector.Body;
        if (body.NodeType == ExpressionType.Convert)
            body = ((UnaryExpression)keySelector.Body).Operand;
        var selector = Expression.Lambda(body, keySelector.Parameters);
        var orderByCall = Expression.Call(
            typeof(Queryable), "OrderBy", new[] { typeof(T), body.Type },
            source.Expression, Expression.Quote(selector));
        return (IOrderedQueryable<T>)source.Provider.CreateQuery(orderByCall);
    }
}