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();
Ответы
Ответ 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);
}