2011-03-03 40 views
4

我想从一个传入的表达式中按日期排序IQueryable实体< Func < T,object >> and am getting错误:“无法将类型'System.Nullable`1强制类型为'System.Object'.LINQ to Entities仅支持强制转换实体数据模型原语类型。”在实体上有一个可为空的日期时间属性上我试图排序:LINQ to Entities - 无法强制'System.DateTime'键入'System.Object'的顺序By

例子:(其中e.Date是一个可为空的DateTime)

Expression<Func<T,object>> sorter = (e) => e.Date; 
IOrderedQueryable<T> sortedData = data.OrderBy(sorter); 

提前感谢!

回答

4

我写了一个简单的类订购基于在运行时lambda表达式实体:其次,所以你必须定义应与元素发生什么地方日期是null每个元素都必须有顺序的地方。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace DataModeling 
{ 
    public class QueryOrderer<TEntity> 
     where TEntity : class 
    { 
     private LambdaExpression defaultSortExpression; 
     private Dictionary<string, LambdaExpression> orderFieldLookup; 

     public QueryOrderer() 
     { 
      orderFieldLookup = new Dictionary<string, LambdaExpression>(); 
     } 

     public void AddOrderMapping<TProp>(string fieldName, Expression<Func<TEntity, TProp>> selector) 
     { 
      orderFieldLookup[fieldName] = selector; 
     } 

     public void SetDefaultSortExpression<TProp>(Expression<Func<TEntity, TProp>> selector) 
     { 
      defaultSortExpression = selector; 
     } 

     public IQueryable<TEntity> GetOrderedEntries(string field, bool isDescending, IQueryable<TEntity> entries) 
     { 
      return orderEntries(entries, field, isDescending); 
     } 

     private IQueryable<TEntity> orderEntries(IQueryable<TEntity> entries, string fieldName, bool isDescending) 
     { 
      dynamic lambda = getOrderByLambda(entries, fieldName); 
      if (lambda == null) 
      { 
       return entries; 
      } 
      if (isDescending) 
      { 
       return Queryable.OrderByDescending(entries, lambda); 
      } 
      else 
      { 
       return Queryable.OrderBy(entries, lambda); 
      } 
     } 

     private dynamic getOrderByLambda(IQueryable<TEntity> entries, string fieldName) 
     { 
      if (!String.IsNullOrWhiteSpace(fieldName) && orderFieldLookup.ContainsKey(fieldName)) 
      { 
       return orderFieldLookup[fieldName]; 
      } 
      else 
      { 
       return defaultSortExpression; 
      } 
     } 
    } 
} 

您可以通过最初设置的所有字段使用这个类:

QueryOrderer<User> orderer = new QueryOrderer<User>(); 
orderer.SetDefaultSortExpression(u => u.FullName); 
orderer.AddOrderMapping("UserId", u => u.UserId); 
orderer.AddOrderMapping("Name", u => u.FullName); 
orderer.AddOrderMapping("Email", u => u.Email); 
orderer.AddOrderMapping("CreatedOn", u => u.CreatedOn); 

... 

var users = orderer.GetOrderedEntries("CreatedOn", isDescending: false, context.Users); 

我这个代码的很好的特性是它完美地处理查找值。举例来说,如果你想使用的描述,而不是一键排序,你可以建立的那种表达时所使用的外部背景:

orderer.AddOrderMapping("UserType", 
    u => context.UserTypes 
       .Where(t => t.UserTypeId == u.UserTypeId) 
       .Select(t => t.Description) 
       .FirstOrDefault()); 

实体框架是足够聪明,只是折叠子查询直接进入外部查询。

+0

我想感谢你的这一小段代码。我能够用它来弄清楚我的通用资料库的排序方法出错了。 http://pastebin.com/M6xV9wHd – Kibner 2015-04-17 17:53:34

+0

不错。我最近扩展了这个也是由多个领域订购。我更喜欢你的实现。 – 2015-04-17 19:48:29

+0

这工作就像一个魅力!我扩展了该类来处理任何TKey(我需要一个int vs字符串)。 – 2016-11-16 23:06:35

1

这里有两个问题:首先在分拣机中使用object,则应该使用DateTime

Expression<Func<T, DateTime>> sorter = (e) => e.Date ?? DateTime.MaxValue; 
IOrderedQueryable<T> sortedData = data.OrderBy(sorter); 
+2

这将有可能保持它一般能够通过日期时间,同时为其它数据类型(如字符串)排序?我试图保持足够的通用性,以便如果我的实体具有像string类型的entity.name这样的属性,它可以重复使用相同的代码来进行排序,而不必知道排序器的类型。 – rgimmy 2011-03-03 02:46:27

+0

@rgimmy:关于这个问题的任何消息?我面临同样的 – chiccodoro 2012-05-29 10:29:21

+0

它不是通用的解决方案。 – Abhishek 2017-08-20 09:01:04

0

试图重建表达身体

private LambdaExpression CreateLambdaPropertyGetter(Expression<Func<TEntity, object>> expression) 
    { 
     Expression body; 
     if (expression.Body is UnaryExpression && ((UnaryExpression)expression.Body).NodeType == ExpressionType.Convert)    
      body = ((UnaryExpression)expression.Body).Operand;    
     else 
      body = expression.Body; 
     var lambda = Expression.Lambda(body, expression.Parameters); 

     return lambda; 
    }