1

LINQ2Entities を使用して、サーバー側 (SQLServer) でエンティティを並べ替える API を書きたいと思います。

エンティティのソート フィールドとソート方向を表す式を含むクラスがあります。

    public class SortOption<TEntity>
    {
       public SortOption(Expression<Func<TEntity, dynamic>> keySelector, 
            bool ascending = true)
        {
            KeySelector = keySelector;
            Ascending = ascending;
        }

       public Expression<Func<TEntity, dynamic>> KeySelector { get; private set; }
       public bool Ascending { get; private set; }
    }

私のエンティティごとに、上から継承するクラスがあります。例えば:

    public class PostSorting: SortOption<PostEntity>
    {
        public PostSorting(): base(p => p.Published)
        {
        }
    }

    public class PostEntity
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }
        public DateTime? Published { get; set; }
        public DateTime Modified { get; set; }
        public string Title { get; set; }
    }

主な目標は、エンティティを返すリポジトリのメソッドで SortOption クラスのプロパティを使用することです。

  public class Repository<TEntity>
     {
        public IEnumerable<TEntity> List(SortOption<TEntity> sortOptions)
         {
            IQueryable<TEntity> query;

            if (sortOptions.Ascending)
              query = dbSet.OrderBy(sortOptions.KeySelector);
            else
              query = dbSet.OrderByDescending(sortOptions.KeySelector);

            return query;
         }
     }

※「dbSet」フィールドはSystem.Data.Entity.DbSet<TEntity>

PostSorting クラスを使用して文字列型とは異なる型を持つプロパティでエンティティを並べ替えようとすると、次のようなエラーが発生します。

"LINQ to Entities only supports casting EDM primitive or enumeration types.".

例 (公開済みフィールドで並べ替え):

 "Unable to cast the type 'System.Nullable`1[[System.DateTime, mscorlib, 
    Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' to type
     'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types."

または(Modifiedフィールドで注文したい場合)

 "Unable to cast the type 'System.DateTime' to type 'System.Object'.
 LINQ to Entities only supports casting EDM primitive or enumeration types."

の(IDフィールドで注文したい場合)

 "Unable to cast the type 'System.Guid' to type 'System.Object'. 
 LINQ to Entities only supports casting EDM primitive or enumeration types."

このタスクに数日間取り組みましたが、問題を解決するための答えが見つかりません。

4

1 に答える 1

5

これを使用してみてください:

public static class QueryableEx
{
    public static IOrderedQueryable<TSource> OrderByEx<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, object>> keySelector)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (keySelector == null)
        {
            throw new ArgumentNullException("keySelector");
        }

        // While the return type of keySelector is object, the "type" of 
        // keySelector.Body is the "real" type *or* it is a
        // Convert(body). We rebuild a new Expression with this "correct" 
        // Body (removing the Convert if present). The return type is
        // automatically chosen from the type of the keySelector.Body .
        Expression body = keySelector.Body;

        if (body.NodeType == ExpressionType.Convert)
        {
            body = ((UnaryExpression)body).Operand;
        }

        LambdaExpression keySelector2 = Expression.Lambda(body, keySelector.Parameters);
        Type tkey = keySelector2.ReturnType;

        MethodInfo orderbyMethod = (from x in typeof(Queryable).GetMethods()
                                    where x.Name == "OrderBy"
                                    let parameters = x.GetParameters()
                                    where parameters.Length == 2
                                    let generics = x.GetGenericArguments()
                                    where generics.Length == 2
                                    where parameters[0].ParameterType == typeof(IQueryable<>).MakeGenericType(generics[0]) && 
                                        parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(generics[0], generics[1]))
                                    select x).Single();

        return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(Expression.Call(null, orderbyMethod.MakeGenericMethod(new Type[]
        {
            typeof(TSource),
            tkey
        }), new Expression[]
        {
            source.Expression,
            Expression.Quote(keySelector2)
        }));
    }
}

と書く必要がありますが、OrderByAscendingを に置き換えるだけで同じOrderByですOrderByAscendingExpressionこのメソッドは、「正しい」型を使用するように を書き換えます。

コードはQueryable.OrderByから大いに影響を受けています。

于 2015-04-30T15:57:40.527 に答える