Подтвердить что ты не робот

Entity Framework: LINQ to Entities поддерживает только моделирование примитивных типов данных Entity Data Model

Я написал метод, позволяющий передать выражение для предложения orderby, но я столкнулся с этой проблемой.

Невозможно применить тип 'System.DateTime' для ввода типа 'System.IComparable. LINQ to Entities поддерживает только листинг данных сущности Модельные примитивные типы.

В основном выражение следующее:

Expression<Func<K, IComparable>> orderBy

И используется следующим образом:

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

Идея заключается в том, что я могу использовать словарь для совпадения строк с выражениями типа:

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

Затем у меня есть метод, который принимает строку сортировки и возвращает выражение, если оно соответствует ключу в словаре, если не возвращает значение по умолчанию. (Идея - способ контролировать то, что ее можно упорядочить). Теперь это работает для свойств String, но пока не для datetime или integer, поскольку я получаю сообщение об ошибке выше.

Насколько я понимаю, проблема заключается в том, что Entity Framework нуждается в том, чтобы быть основным/EDM-типом, потому что он должен преобразовать С# DateTime во что-то, что может обрабатывать база данных.

Есть ли способ конвертировать 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();
4b9b3361

Ответ 1

Entity Framework делает это сложным, и я не уверен, что есть способ сделать то, что вы хотите сделать, с одним типом возвращаемого значения (IComparable, object и т.д.). Вы можете подумать о том, чтобы переработать свой дизайн в словаре значений name-to- 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);
}

Ответ 2

Я знаю, что это старо, но я искал то же самое, что и OP, и не хотел использовать Func<IQueryable<T>, IOrderedQueryable<T>> в моем словаре. В основном потому, что мне нужно было бы реализовать делегат OrderBy и OrderByDescending.

В результате я создал метод расширения для IQueryable, называемый ObjectSort, который просто проверяет, что должен выглядеть тип возвращаемого выражения, а затем создайте новую лямбду, используя этот тип, чтобы 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);
}

Ответ 3

Обнаружена аналогичная проблема с исходным плакатом, где выражения "Order By", написанные как lambdas типа Expression < Func < T, object → . Они были правильно интерпретированы провайдером linib-сервера NHibernate, но переход на EF 5 привел к "Невозможно передать тип" System.DateTime "для ввода" System.IComparable ". LINQ to Entities поддерживает только приведение примитивных типов данных Entity Data Model".

Следующие способы обеспечивают преобразование в выражение < 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

Я нашел очень простое решение вашей проблемы (и мое тоже). Когда вы создаете свое выражение поиска, вы должны передать тип свойства (вы его знаете), но сохраните выражение в динамической переменной:

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