我编写了一个方法来允许为orderby子句传递Expression,但我遇到了这个问题。
无法转换类型 输入'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或整数,因为我得到上面的错误消息。
到目前为止,我(松散地)理解问题是实体框架需要它是主/ 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();
答案 0 :(得分:23)
我知道这是旧的,但我希望完成与OP完全相同的事情,并且不想在我的字典中使用Func<IQueryable<T>, IOrderedQueryable<T>>
。主要是因为我必须同时实施OrderBy
和OrderByDescending
委托。
我最终为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);
}
答案 1 :(得分:13)
实体框架使这很困难,我不确定是否有办法用单一的返回值类型(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);
}
答案 2 :(得分:4)
遇到与原始海报类似的问题,其中“Order By”表达式写为Expression&lt; Func&lt; T,object&gt;&gt;类型的lambda。这些由NHibernate linq提供程序正确解释,但迁移到EF 5导致“无法将类型'System.DateTime'转换为'System.IComparable'类型.LINQ to Entities仅支持转换实体数据模型基元类型。”< / p>
以下方法提供对表达式&lt; Func&lt; T,TKey&gt;&gt;的转换。当调用各种“OrderBy”方法时(使用反射 - 道歉......)注意它们最初封装在一个通用类OrderBy&lt; T&gt;中。
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);
}
}
答案 3 :(得分:3)
我找到了解决问题的非常简单的解决方案(我的也是)。在创建搜索表达式时,应该传递属性类型(然后你知道),但将表达式存储在动态变量中:
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)。
答案 4 :(得分:0)
在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);
}