web-dev-qa-db-ja.com

エンティティフレームワーク:LINQ to Entitiesは、エンティティデータモデルプリミティブ型のキャストのみをサポートします

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();
24
Programmin Tool

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);
}
13
Ben M

私はこれが古いことを知っていますが、私はOPとまったく同じことを達成しようとしていて、辞書でFunc<IQueryable<T>, IOrderedQueryable<T>>を使用したくありませんでした。主な理由は、OrderByOrderByDescendingの両方のデリゲートを実装する必要があるためです。

ObjectSortと呼ばれるIQueryableの拡張メソッドを作成することになりました。これは、式の戻り値の型を確認し、その型を使用して新しいラムダを作成するだけで、LINQ toEntitiesが狂わないようにします。 。

これが良い解決策であるかどうかはわかりませんが、以下の例はDateTimeintで機能するので、同様のことを達成しようとしている場合に、いくつかのアイデアが得られることを願っています。 !!

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);
}
23
Lucifer Sam

「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);
        }
    }
4
OldNic

私はあなたの問題に対する非常に簡単な解決策を見つけました(そして私のものも)。検索式を作成するときは、プロパティのタイプを渡す必要がありますが(その場合はそれを知っています)、式を動的変数に格納します。

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);
    }
0
Peter Ragndahl