8

IQueryable<T>定義をカプセル化し、保存して再利用するか、後でより大きなクエリに埋め込む式があるとします。例えば:

IQueryable<Foo> myQuery =
    from foo in blah.Foos
    where foo.Bar == bar
    select foo;

これで、その myQuery オブジェクトを保持して、説明したように使用できると思います。しかし、私がよくわからないことがいくつかあります:

  1. どのようにパラメータ化するのが最善ですか? 最初にこれをメソッドで定義し、メソッドIQueryable<T>の結果として を返しました。blahこのようにして、メソッドの引数としてandを定義でき、毎回bar新しいものを作成するだけだと思います。IQueryable<T>これは、のロジックをカプセル化する最良の方法IQueryable<T>ですか? 他の方法はありますか?

  2. クエリが ではなくスカラーに解決される場合はどうなりIQueryableますか? たとえば、このクエリを表示どおりにしたいが.Any()、一致する結果があったかどうかを知らせるだけにしたい場合はどうすればよいでしょうか? を追加する(...).Any()と、結果がboolすぐに実行されますよね?これらのQueryable演算子 ( AnySindleOrDefaultなど) をすぐに実行せずに利用する方法はありますか? LINQ-to-SQL はこれをどのように処理しますか?

編集:パート 2 は、 と の制限の違いを理解しようとすることに関するものIQueryable<T>.Where(Expression<Func<T, bool>>)ですIQueryable<T>.Any(Expression<Func<T, bool>>)。実行が遅れる大きなクエリを作成する場合、後者はそれほど柔軟ではないようです。を追加してから、他のWhere()構成を後で追加して、最後に実行することができます。はスカラー値を返すためAny()、残りのクエリを作成する前にすぐに実行されるように聞こえます。

4

4 に答える 4

5
  1. DataContext を使用している場合は、IQueryables の受け渡しに細心の注意を払う必要があります。これは、コンテキストが破棄されると、その IQueryable で実行できなくなるためです。コンテキストを使用していない場合は問題ないかもしれませんが、そのことに注意してください。

  2. .Any() と .FirstOrDefault() は延期されません。それらを呼び出すと、実行発生します。ただし、これはあなたが思っていることをしないかもしれません。たとえば、LINQ to SQL では、IQueryable で .Any() を実行すると、基本的に IF EXISTS( SQL HERE ) として機能します。

必要に応じて、次のように IQueryable を連鎖させることができます。

var firstQuery = from f in context.Foos
                    where f.Bar == bar
                    select f;

var secondQuery = from f in firstQuery
                    where f.Bar == anotherBar
                    orderby f.SomeDate
                    select f;

if (secondQuery.Any())  //immediately executes IF EXISTS( second query in SQL )
{
    //causes execution on second query 
    //and allows you to enumerate through the results
    foreach (var foo in secondQuery)  
    {
        //do something
    }

    //or

    //immediately executes second query in SQL with a TOP 1 
    //or something like that
    var foo = secondQuery.FirstOrDefault(); 
}
于 2009-08-20T19:03:08.317 に答える
2

Any()この方法の使用は延期されます。

var q = dc.Customers.Where(c => c.Orders.Any());

Any()このように使用すると遅延は発生しませんが、引き続き SQL に変換されます (customers テーブル全体がメモリに読み込まれるわけではありません)。

bool result = dc.Customers.Any();

遅延 Any() が必要な場合は、次のようにします。

public static class QueryableExtensions
{
  public static Func<bool> DeferredAny<T>(this IQueryable<T> source)
  {
    return () => source.Any();
  }
}

これは次のように呼び出されます:

Func<bool> f = dc.Customers.DeferredAny();
bool result = f();

欠点は、この手法ではサブクエリができないことです。

于 2009-08-20T19:36:38.607 に答える
2

IQueryable オブジェクトをキャッシュするよりもはるかに優れたオプションは、式ツリーをキャッシュすることです。すべての IQueryable オブジェクトには、そのクエリの現在の式ツリーを表す Expression というプロパティがあります (私は信じています)。

後で、 queryable.Provider.CreateQuery(expression) を呼び出すか、プロバイダーが何であれ (この場合は Linq2Sql データ コンテキスト) で直接クエリを再作成できます。

ただし、これらの式ツリーのパラメーター化は、ConstantExpressions を使用して値を組み込むため、少し難しくなります。これらのクエリをパラメータ化するには、異なるパラメータが必要になるたびにクエリを再構築する必要があります。

于 2009-08-20T19:16:58.443 に答える
0

式内でクエリの部分適用を作成する

Func[Bar,IQueryable[Blah],IQueryable[Foo]] queryMaker = 
(criteria, queryable) => from foo in queryable.Foos
        where foo.Bar == criteria
        select foo;

そして、あなたはそれを使用することができます...

IQueryable[Blah] blah = context.Blah;
Bar someCriteria = new Bar();
IQueryable[Foo] someFoosQuery = queryMaker(blah, someCriteria);

移植性や再利用性を高めたい場合は、クエリをクラス内にカプセル化できます。

public class FooBarQuery
{
  public Bar Criteria { get; set; }

  public IQueryable[Foo] GetQuery( IQueryable[Blah] queryable )
  {
     return from foo in queryable.Foos
        where foo.Bar == Criteria
        select foo;
  } 
}
于 2009-09-17T03:29:18.327 に答える