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);
次に、ソート文字列を受け取り、辞書のキーと一致する場合は式を返し、一致しない場合はデフォルトを返すメソッドがあります。 (これは、順序付けできるものを制御する方法です)これは、文字列プロパティで機能しますが、上記のエラーメッセージが表示されるため、これまでのところ、日時または整数では機能しません。
私が(大まかに)理解している限り、問題は、C#DateTimeをデータベースが処理できるものに変換する必要があるため、EntityFrameworkがプライマリ/ EDM型である必要があることです。
これが引き続き機能するように、日時をプリミティブ型に変換する方法はありますか?
解決策
メソッドごとの注文を取得する方法:(クエリを受け取り、「注文された形式」で返します)
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();
Entity Frameworkはこれを困難にし、単一の戻り値型(IComparable、オブジェクトなど)でやりたいことを実行する方法があるかどうかはわかりません。デザインを名前から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);
}
私はこれが古いことを知っていますが、私はOPとまったく同じことを達成しようとしていて、辞書でFunc<IQueryable<T>, IOrderedQueryable<T>>
を使用したくありませんでした。主な理由は、OrderBy
とOrderByDescending
の両方のデリゲートを実装する必要があるためです。
ObjectSort
と呼ばれるIQueryableの拡張メソッドを作成することになりました。これは、式の戻り値の型を確認し、その型を使用して新しいラムダを作成するだけで、LINQ toEntitiesが狂わないようにします。 。
これが良い解決策であるかどうかはわかりませんが、以下の例は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);
}
「OrderBy」式がExpression <Func <T、object >>型のラムダとして記述されている、元のポスターと同様の問題が発生しました。これらはNHibernatelinqプロバイダーによって正しく解釈されましたが、EF 5に移行すると、「タイプ 'System.DateTime'をタイプ 'System.IComparable'にキャストできません。LINQtoEntitiesは、エンティティデータモデルプリミティブタイプのキャストのみをサポートします。」
次のメソッドは、さまざまな「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);
}
}
私はあなたの問題に対する非常に簡単な解決策を見つけました(そして私のものも)。検索式を作成するときは、プロパティのタイプを渡す必要がありますが(その場合はそれを知っています)、式を動的変数に格納します。
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)で1つの式を使用できます。
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);
}