代码之家  ›  专栏  ›  技术社区  ›  Programmin Tool

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

  •  24
  • Programmin Tool  · 技术社区  · 15 年前

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

    要键入的“System.DateTime” 仅支持转换实体数据 模型基元类型。

    Expression<Func<K, IComparable>> orderBy
    

    它的用法如下:

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

    这样我就可以使用字典来保存表达式的字符串匹配,如:

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

    现在,据我(粗略地)理解,问题在于实体框架需要它是主/EDM类型,因为它必须将C#DateTime转换为数据库可以处理的内容。

    解决方案

    通过方法获取订单的方法:(接受查询并以“ordered form”形式返回)

    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();
    
    5 回复  |  直到 15 年前
        1
  •  24
  •   Jeff Camera    13 年前

    我知道这已经很老了,但我想完成与OP完全相同的任务,不想使用 Func<IQueryable<T>, IOrderedQueryable<T>> 在我的字典里。主要是因为我必须同时实现 OrderBy OrderByDescending 代表

    我最终为IQueryable创建了一个扩展方法,名为 ObjectSort

    我不确定这是否是一个好的解决方案,但下面的示例确实适用于 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);
    }
    
        2
  •  13
  •   Ben M    15 年前

    实体框架使这一点变得困难,我不确定是否有一种方法可以处理单个返回值类型(IComparable、object等)。您可能会考虑将您的设计改写为名称词典。 Func<IQueryable<K>, IOrderedQueryable<K>> 价值观:

    _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);
    }
    
        3
  •  5
  •   OldNic    11 年前

    以下方法提供了到表达式的转换<Func<T、 TKey>&燃气轮机;调用各种“OrderBy”方法(使用反射-道歉…)时,请注意,它们最初封装在一个泛型类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);
            }
        }
    
        4
  •  4
  •   Wojciech Mikołajewicz    9 年前

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

    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
  •  1
  •   Peter Ragndahl    11 年前

    在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);
        }