【问题标题】:Entity Framework: LINQ to Entities only supports casting Entity Data Model primitive types实体框架:LINQ to Entities 仅支持转换实体数据模型原始类型
【发布时间】:2015-08-18 23:19:54
【问题描述】:

我写了一个方法来允许为 orderby 子句传递一个表达式,但是我遇到了这个问题。

无法转换类型 'System.DateTime' 键入 'System.IComparable'。 LINQ 到实体 仅支持转换实体数据 模型基元类型。

基本上表达式是这样的:

Expression<Func<K, IComparable>> orderBy

并且是这样使用的:

SomeEntities.SomeTable
.Where
(
   whereClause
)
.Select
(
   selectClause
)
.OrderBy(orderBy)

这个想法是为了让我可以使用字典来保存与表达式匹配的字符串,例如:

_possibleSortForForumItem.Add("CreateDate", item => item.CreateDate);

然后我有一个方法,它接受排序字符串并返回表达式,如果它匹配字典中的键,如果不返回一些默认值。 (这个想法是一种控制可以订购什么的方法)现在这适用于字符串属性,但到目前为止不适用于日期时间或整数,因为我收到上面的错误消息。

现在据我(大致)了解,问题在于实体框架需要它是 Primary/EDM 类型,因为它必须将 C# DateTime 转换为数据库可以处理的东西。

有没有办法将日期时间转换为原始类型,这样它仍然可以工作?

解决方案

按方法获取订单的方法:(取一个查询,以“有序形式”返回)

private static Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>> GetMethodForSort(String sortBy)
{
  if (_methodForSort == null)
  {
    _methodForSort = new Dictionary<String, Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>>>();
    _methodForSort.Add(SortForumViewItemCreatedOn, item => item.OrderBy(innerItem => innerItem.CreatedOn));
    ...
  }

  Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>> orderMethod;

  if(String.IsNullOrEmpty(sortBy) || !_methodForSort.ContainsKey(sortBy))
  {
    orderMethod = _methodForSort["ForumName"];
  }
  else
  {
    orderMethod = _methodForSort[sortBy];
  }

  return orderMethod;
}

通用查询方法的方法签名:

IList<K> GetListForGrid<T, K>(this ObjectQuery<T> query, ... Func<IQueryable<K>, IOrderedQueryable<K>> orderBy, ...)

以及传入方法的使用:

initialQuery = query
  .Where
  (
    somethingEqualsSomething
  )
  .Select
  (
    selectClause
  );

var orderedQuery = orderBy(initialQuery);

returnValue = orderedQuery
  .Skip(numberToShow * realPage)
  .Take(numberToShow)
  .ToList();

【问题讨论】:

    标签: entity-framework primitive-types


    【解决方案1】:

    我知道这是旧的,但我希望完成与 OP 完全相同的事情,并且不想在我的字典中使用 Func&lt;IQueryable&lt;T&gt;, IOrderedQueryable&lt;T&gt;&gt;。主要是因为我必须同时实现 OrderByOrderByDescending 委托。

    我最终为 IQueryable 创建了一个名为 ObjectSort 的扩展方法,它将简单地检查表达式的返回类型应该是什么,然后使用该类型创建一个新的 lambda,这样 LINQ to Entities 就不会崩溃.

    我不确定这是否是一个好的解决方案,但下面的示例确实适用于 DateTimeint,所以如果您想要完成类似的事情,希望它能给您一些想法!

    public static IOrderedQueryable<T> ObjectSort<T>(this IQueryable<T> entities, Expression<Func<T, object>> expression, SortOrder order = SortOrder.Ascending)
    {
        var unaryExpression = expression.Body as UnaryExpression;
        if (unaryExpression != null)
        {
            var propertyExpression = (MemberExpression)unaryExpression.Operand;
            var parameters = expression.Parameters;
    
            if (propertyExpression.Type == typeof(DateTime))
            {
                var newExpression = Expression.Lambda<Func<T, DateTime>>(propertyExpression, parameters);
                return order == SortOrder.Ascending ? entities.OrderBy(newExpression) : entities.OrderByDescending(newExpression);
            }
    
            if (propertyExpression.Type == typeof(int))
            {
                var newExpression = Expression.Lambda<Func<T, int>>(propertyExpression, parameters);
                return order == SortOrder.Ascending ? entities.OrderBy(newExpression) : entities.OrderByDescending(newExpression);
            }
    
            throw new NotSupportedException("Object type resolution not implemented for this type");
        }
        return entities.OrderBy(expression);
    }
    

    【讨论】:

    • 感谢这个伟大的扩展!它仍然不能正确地对字符串列进行排序。我通过将最后一行更改为:return order == SortOrder.Ascending ? entities.OrderBy(expression) : entities.OrderByDescending(expression); 解决了这个问题
    【解决方案2】:

    实体框架使这变得困难,我不确定是否有一种方法可以使用单个返回值类型(IComparable、对象等)来执行您想要执行的操作。您可能会考虑将您的设计重新设计成一个名称到Func&lt;IQueryable&lt;K&gt;, IOrderedQueryable&lt;K&gt;&gt; 值的字典:

    _possibleSortForForumItem.Add("CreateDate", 
        query => query.OrderBy(item.CreateDate));
    

    然后像这样应用它:

    var orderedQuery = query.OrderBy(item => item.DefaultOrderColumn);
    
    Func<IQueryable<K>, IOrderedQueryable<K>> assignOrderBy = null;
    
    if (_possibleSortForForumItem.TryGetValue(orderColumnName, out assignOrderBy))
    {
        orderedQuery = assignOrderBy(query);
    }
    

    【讨论】:

    • 我建议使用 IOrderedQueryable 作为 Func 的结果,这将在编译时强制使用 OrderBy,因为例如分页不喜欢无序查询。
    • 我花了几分钟才弄清楚如何工作,但最后它非常可行。
    • 小心,如果我没记错的话,这个签名只适用于 IEnumerables,不适用于 IQueryables。同样,如果我没记错的话,这意味着这将在结果集上运行,因此 LINQ 引擎不会将其转换为 SQL,而是将其视为查询后的内存中排序。
    • 哦,对了,这将应用于覆盖两个签名的 ObjectQuery。我的错。
    【解决方案3】:

    遇到了与原始海报类似的问题,其中“Order By”表达式写为 Expression> 类型的 lambdas。 NHibernate linq 提供程序正确解释了这些内容,但迁移到 EF 5 导致“无法将类型 'System.DateTime' 转换为类型 'System.IComparable'。LINQ to Entities 仅支持转换实体数据模型原始类型。”

    以下方法在调用各种“OrderBy”方法时提供了到 Expression> 的转换(使用反射 - 道歉...)请注意,它们最初封装在通用类 OrderBy 中。

        private static readonly Type QueryableType = typeof(Queryable);
    
        // HACK: Use reflection to call strongly-typed methods instead of object-based methods
        // This is to work around "Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
        private IOrderedQueryable<T> ApplyOrderByTo(
            IQueryable<T> query,
            Expression<Func<T, object>> keySelector,
            bool sortAscending,
            bool useReflection)
        {
            if (useReflection)
            {
                var body = keySelector.Body as UnaryExpression;
                var keyExpr = body.Operand as MemberExpression;
    
                return (IOrderedQueryable<T>)query.Provider.CreateQuery(
                    Expression.Call(
                    QueryableType,
                    sortAscending ? "OrderBy" : "OrderByDescending",
                    new Type[] { typeof(T), keyExpr.Type },
                    query.Expression,
                    Expression.Lambda(keyExpr, keySelector.Parameters)));
            }
            else
            {
                if (sortAscending)
                    return query.OrderBy(keySelector);
                else
                    return query.OrderByDescending(keySelector);
            }
        }
    
        // HACK: Use reflection to call strongly-typed methods instead of object-based methods
        // This is to work around "Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
        private IOrderedQueryable<T> ApplyOrderByTo(
            IOrderedQueryable<T> query,
            Expression<Func<T, object>> keySelector,
            bool sortAscending,
            bool useReflection)
        {
            if (useReflection)
            {
                var body = keySelector.Body as UnaryExpression;
                var keyExpr = body.Operand as MemberExpression;
    
                return (IOrderedQueryable<T>)query.Provider.CreateQuery(
                    Expression.Call(
                    QueryableType,
                    sortAscending ? "ThenBy" : "ThenByDescending",
                    new Type[] { typeof(T), keyExpr.Type },
                    query.Expression,
                    Expression.Lambda(keyExpr, keySelector.Parameters)));
            }
            else
            {
                if (sortAscending)
                    return query.ThenBy(keySelector);
                else
                    return query.ThenByDescending(keySelector);
            }
        }
    

    【讨论】:

      【解决方案4】:

      我为您的问题找到了非常简单的解决方案(也是我的问题)。当你创建你的搜索表达式时,你应该传递属性的类型(那时你就知道了),但将表达式存储在动态变量中:

      Expression<Func<TObject, DateTime>> Expr = obj=>obj.StartDate;
      dynamic dynExpr=Expr;
      

      现在您可以将 dynExpr 与 int 表达式、字符串表达式等一起存储在表中或任何您想要的地方,到时候您可以在 OrderBy 方法中使用它。但不是标准方式(扩展方式):

      query=query.OrderBy(dynExpr);
      

      ,只能这样:

      query=Queryable.OrderBy(query, dynExpr);
      

      通过这种方式,您可以在所有排序函数(OrderBy、OrderByDescending、ThenBy、ThenByDescending)中使用一个表达式。

      【讨论】:

      • 这完全解决了我的问题,因为我想动态构建具有许多不同数据类型的搜索谓词和列排序。
      【解决方案5】:

      在 OldNic 的启发下,我创建了几个用于按成员排序的扩展方法。这对我很有效。我也在使用 System.Data.SqlClient.SortOrder 枚举来定义排序顺序。

              /// <summary>
          ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
          ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
          ///     LINQ to Entities only supports casting Entity Data Model primitive types.
          /// </summary>
          /// <typeparam name="T">entity type</typeparam>
          /// <param name="query">query to apply sorting on.</param>
          /// <param name="expression">the member expression to apply</param>
          /// <param name="sortOrder">the sort order to apply</param>
          /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
          public static IOrderedQueryable<T> OrderByMember<T>(
              this IQueryable<T> query, 
              Expression<Func<T, object>> expression, 
              SortOrder sortOrder)
          {
              var body = expression.Body as UnaryExpression;
      
              if (body != null)
              {
                  var memberExpression = body.Operand as MemberExpression;
      
                  if (memberExpression != null)
                  {
                      return
                          (IOrderedQueryable<T>)
                          query.Provider.CreateQuery(
                              Expression.Call(
                                  typeof(Queryable), 
                                  sortOrder == SortOrder.Ascending ? "OrderBy" : "OrderByDescending",
                                  new[] { typeof(T), memberExpression.Type }, 
                                  query.Expression,
                                  Expression.Lambda(memberExpression, expression.Parameters)));
                  }
              }
      
              return sortOrder == SortOrder.Ascending ? query.OrderBy(expression) : query.OrderByDescending(expression);
          }
      
          /// <summary>
          ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
          ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
          ///     LINQ to Entities only supports casting Entity Data Model primitive types.
          /// </summary>
          /// <typeparam name="T">entity type</typeparam>
          /// <param name="query">query to apply sorting on.</param>
          /// <param name="expression">the member expression to apply</param>
          /// <param name="sortOrder">the sort order to apply</param>
          /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
          public static IOrderedQueryable<T> ThenByMember<T>(
              this IQueryable<T> query, 
              Expression<Func<T, object>> expression, 
              SortOrder sortOrder)
          {
              return ((IOrderedQueryable<T>)query).ThenByMember(expression, sortOrder);
          }
      
          /// <summary>
          ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
          ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
          ///     LINQ to Entities only supports casting Entity Data Model primitive types.
          /// </summary>
          /// <typeparam name="T">entity type</typeparam>
          /// <param name="query">query to apply sorting on.</param>
          /// <param name="expression">the member expression to apply</param>
          /// <param name="sortOrder">the sort order to apply</param>
          /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
          public static IOrderedQueryable<T> ThenByMember<T>(
              this IOrderedQueryable<T> query, 
              Expression<Func<T, object>> expression, 
              SortOrder sortOrder)
          {
              var body = expression.Body as UnaryExpression;
      
              if (body != null)
              {
                  var memberExpression = body.Operand as MemberExpression;
      
                  if (memberExpression != null)
                  {
                      return
                          (IOrderedQueryable<T>)
                          query.Provider.CreateQuery(
                              Expression.Call(
                                  typeof(Queryable), 
                                  sortOrder == SortOrder.Ascending ? "ThenBy" : "ThenByDescending",
                                  new[] { typeof(T), memberExpression.Type }, 
                                  query.Expression,
                                  Expression.Lambda(memberExpression, expression.Parameters)));
                  }
              }
      
              return sortOrder == SortOrder.Ascending ? query.ThenBy(expression) : query.ThenByDescending(expression);
          }
      

      【讨论】:

      • 谢谢,这是唯一完美的解决方案!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多