EF orderby / thenby combo扩展方法

时间:2012-10-24 20:12:49

标签: c# linq entity-framework linq-to-entities linq-expressions

我希望能够按如下方式应用组合firstby / thenby排序:

allOrders().sort(s => s.ProductName, s => s.OrderDate)

所以使用this post as inspiration,我写了这个扩展方法,它编译得很好:

public static IQueryable<T> sort<T>(this IQueryable<T> entities, params 
  Expression<Func<T, object>>[] predicates) where T : class 
{
  var sorted = entities.OrderBy(predicates[0]);
  for (int i = 1; i < predicates.Length; i++)
    sorted = sorted.ThenBy(predicates[i]);

  return sorted;
}

我也试过这个succint版本,它也编译:

public static IQueryable<T> sort<T>(this IQueryable<T> entities, params 
  Expression<Func<T, object>>[] predicates) where T : class
{
  return predicates.Skip(1).Aggregate(
    entities.OrderBy(predicates[0]),
    (aggregate, currentPredicate) => aggregate.ThenBy(currentPredicate));
}

但是,如果我尝试按DateTime排序,我会遇到以下异常:

  

无法将类型'System.DateTime'强制转换为'System.Object'。 LINQ to Entities仅支持转换EDM原语或枚举类型。

我做错了什么?我正在使用EF5。

2 个答案:

答案 0 :(得分:6)

从返回int的lambda表达式返回值类型(例如DateTimeobject)时,编译器会生成Box()调用以转换值键入一个盒装对象。

Entity Framework表达式解析器无法处理此框表达式 唯一的解决方案是传递一个返回值类型的强类型lambda表达式。

为此,您可能会误用集合初始值设定项:

public class OrderingCollection<TEntity> : IEnumerable {
    public void Add<TProperty>(Expression<Func<TEntity, TProperty>>) {
        ...
    }
}

public static IQueryable<T> Sort<T>(this IQueryable<T> entities, 
                                    OrderingCollection<T> o) where T : class {
    ...
}


q = q.Sort(new OrderingCollection { s => s.ProductName, s => s.OrderDate });

集合初始值设定项允许您使用具有任意数量的不同类型参数的类型推断。

答案 1 :(得分:0)

我认为您的问题出在Func<T, object>上。这意味着无论您正在排序什么,您都将返回一个对象,并且EF无法将其映射到数据库列类型。

OrderBy()函数同时使用TSourceTKey,因此您需要为每种排序提供密钥类型,这很快就会失控(请参阅{{ 3}})。