实体框架:LINQ to Entities仅支持转换实体数据模型基元类型

Pro*_*ool 24 entity-framework primitive-types

我编写了一个方法来允许为orderby子句传递Expression,但我遇到了这个问题.

无法将类型"System.DateTime"强制转换为"System.IComparable"类型.LINQ to Entities仅支持转换实体数据模型基元类型.

基本上表达式如下:

Expression<Func<K, IComparable>> orderBy
Run Code Online (Sandbox Code Playgroud)

并使用如下:

SomeEntities.SomeTable
.Where
(
   whereClause
)
.Select
(
   selectClause
)
.OrderBy(orderBy)
Run Code Online (Sandbox Code Playgroud)

我的想法是,我可以使用字典来保存字符串匹配,例如:

_possibleSortForForumItem.Add("CreateDate", item => item.CreateDate);
Run Code Online (Sandbox Code Playgroud)

然后我有一个方法接受排序字符串并返回表达式,如果它匹配字典中的键,如果不返回一些默认值.(这个想法是一种控制它可以被命令的方式)现在这适用于String属性,但到目前为止还没有为datetime或整数,因为我得到上面的错误消息.

到目前为止,我(松散地)理解问题是实体框架需要它是主/ 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;
}
Run Code Online (Sandbox Code Playgroud)

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

IList<K> GetListForGrid<T, K>(this ObjectQuery<T> query, ... Func<IQueryable<K>, IOrderedQueryable<K>> orderBy, ...)
Run Code Online (Sandbox Code Playgroud)

并使用传入的方法:

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

var orderedQuery = orderBy(initialQuery);

returnValue = orderedQuery
  .Skip(numberToShow * realPage)
  .Take(numberToShow)
  .ToList();
Run Code Online (Sandbox Code Playgroud)

Luc*_*Sam 23

我知道这是旧的,但我希望完成与OP完全相同的事情,并且不想Func<IQueryable<T>, IOrderedQueryable<T>>在我的字典中使用它.主要是因为我必须实现一个OrderByOrderByDescending委托.

我最终创建了一个名为IQueryable的扩展方法,ObjectSort它只是检查表达式的返回类型应该是什么,然后使用该类型创建一个新的lambda,以便LINQ to Entities不会被吓到.

我不确定这是否是一个很好的解决方案,但下面的例子确实有用DateTime,int所以希望它可以给你一些想法,如果你想要完成类似的事情!

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢这个伟大的扩展!仍然没有正确排序字符串列.我通过将最后一行更改为:`return order == SortOrder.Ascending?entities.OrderBy(expression):entities.OrderByDescending(expression);` (4认同)

Ben*_*n M 13

实体框架使这很困难,我不确定是否有办法用单一返回值类型(IComparable,object等)做你想做的事情.您可以考虑将设计重新编写为名称到Func<IQueryable<K>, IOrderedQueryable<K>>值的字典:

_possibleSortForForumItem.Add("CreateDate", 
    query => query.OrderBy(item.CreateDate));
Run Code Online (Sandbox Code Playgroud)

然后像这样应用它:

var orderedQuery = query.OrderBy(item => item.DefaultOrderColumn);

Func<IQueryable<K>, IOrderedQueryable<K>> assignOrderBy = null;

if (_possibleSortForForumItem.TryGetValue(orderColumnName, out assignOrderBy))
{
    orderedQuery = assignOrderBy(query);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)