3

リポジトリにアクセスするための一般的なクエリ メカニズムを構築しようとしています。ラムダ式を使用してクエリをフィルタリングおよびソートしたいと考えています。一般的な Lambda 式のリストを渡す方法を見つけるのに苦労しています。具体的には order-by です。その際に助けていただければ幸いです。

編集:私が満たそうとしている2つの要件は、リポジトリを超えてIQueryableを公開しないことですが、データベースレベルでフィルタリングとソートを実行できることです。

これをよりよく説明するために、コードをお見せしましょう

public class Query<T>
{
    public class OrderBy<T>
    {
        public Expression<Func<T, **int**>> Clause { set; get; } // Order By clause
        public bool Descending = true;
    }

    public Expression<Func<T, bool>> Where { set; get; } // Where clause
    public IList<OrderBy<T>> OrderBys { set; get; } // Where clause

    public Query()
    {
        OrderBys = new List<OrderBy<T>>();
    }
}    

public IEnumerable<Person> FindBy(Query<Person> query)
{
    IQueryable<Person> Temp = GetObjectSet();
    if (query.Where != null) 
        Temp = Temp.Where(query.Where);
    foreach (var OrderByThis in query.OrderBys)
    {
        if (OrderByThis.Descending) 
            Temp = Temp.OrderByDescending(OrderByThis.Clause);
        else 
            Temp = Temp.OrderBy(OrderByThis.Clause);
    }

    return Temp.ToList<Person>();
}

これはすべて非常に優れていますが、 Expression< Func< T, int >> は一般的ではありません。次のようなことができる必要があります。

Query<Person> query = new Query<Person>();
Query<Person>.OrderBy<Person> clause1 = new Query<Person>.OrderBy<Person>();
clause1.Clause = m => m.Username;
Query<Person>.OrderBy<Person> clause2 = new Query<Person>.OrderBy<Person>();
clause2.Clause = m => m.DateOfBirth;
query.OrderBys.Add(clause1);
query.OrderBys.Add(clause2);

つまり、さまざまなタイプのさまざまなフィールドを多数追加します。

これらを一般的な Lambda 関数として保存し、リポジトリで必要な厳密に型指定された Lambda 関数に変換する方法が必要だと思います。

これどうやってするの?

4

2 に答える 2

1

あなたの他の質問に対する私の答えで述べたように、私は一般的にこのアプローチを思いとどまらせます。IQueryable<T>/を公開するだけの方が理にかなっていますIOrderedQueryable<T>

そうは言っても、EFのOrderByに複数の式を渡す方法の選択された回答で利用可能なあなたの意図に沿った解決策がありますか?。

次のような構文を使用できます。

var query = context.Users ... ;

var queryWithOrderBy = ApplyOrderBy(query,
    new OrderByExpression<User, string>(expression: u => u.UserName, descending: false),    // a string, asc
    new OrderByExpression<User, int>(expression: u => u.UserId, descending: true));  // an int, desc

var result = queryWithOrderBy.ToList(); // didn't throw an exception for me
于 2012-08-16T03:43:08.733 に答える
1

私のコメントを詳しく説明すると、その翻訳を完全にスキップできるのに、式から独自の中間クエリオブジェクトを構築し、その中間オブジェクトから式を再構築する必要がある理由がわかりません。

あなたの例のクエリを考えると:

repository.FindBy(people => people.OrderBy(p => p.Username).ThenBy(p => p.DateOfBirth));

たとえば、ユーザーの選択に基づいて実行されている場合は、引き続きクエリを段階的に構築できることに注意してください。次のクエリは上記と同等です。

Func<IEnumerable<Person>, IEnumerable<Person>> query = people => people.OrderBy(p => p.Username);
 query = query.ThenBy(p => p.DateOfBirth);

リポジトリを超えて公開したくないことは理解していますIQueryableが、次のような署名で LINQ を引き続き使用できます。

public IEnumerable<Person> FindBy(Func<IEnumerable<Person>, IEnumerable<Person>> query)
{
    return query(GetObjectSet()).ToList();
}

ただし、実際の質問について言えば、 for を使用してプロパティタイプOrderByを使用することでタスクを達成できます。または、それが不安な場合は、オブジェクトの代わりに使用することでもう少し制約することができます。文字列と数値型はすべてそれを実装しています。Expression<Func<T, object>>ClauseIComparable

于 2012-08-16T02:05:02.013 に答える