web-dev-qa-db-ja.com

AsQueryable()の目的は何ですか?

AsQueryable()の目的は、IEnumerableIQueryableを期待する可能性のあるメソッドに渡すことができるようにするためですか、それともIEnumerableIQueryableとして表す有用な理由がありますか?たとえば、次のような場合に想定されていますか?

IEnumerable<Order> orders = orderRepo.GetAll();

// I don't want to create another method that works on IEnumerable,
// so I convert it here.
CountOrders(orders.AsQueryable());

public static int CountOrders(IQueryable<Order> ordersQuery)
{
    return ordersQuery.Count();
}

または、実際に何か違うことをするのですか?

IEnumerable<Order> orders = orderRepo.GetAll();
IQueryable<Order> ordersQuery = orders.AsQueryable();

IEnumerable<Order> filteredOrders = orders.Where(o => o.CustomerId == 3);
IQueryable<Order> filteredOrdersQuery = ordersQuery.Where(o => o.CustomerId == 3);

// Are these executed in a different way?
int result1 = filteredOrders.Count();
int result2 = filteredOrdersQuery.Count();

これらの拡張メソッドのIQueryableバージョンは、実行されると同じことを行う式を作成するだけですか?私の主な質問は、AsQueryableを使用する実際の使用例は何ですか?

90
Ocelot20

主な用途がいくつかあります。

  1. 他の回答で述べたように、メモリ内のデータソースを使用してクエリ可能なデータソースをモックすることで、列挙不可能なベースIQueryableで最終的に使用されるメソッドをより簡単にテストできます。

  2. インメモリシーケンスまたは外部データソースに適用できるコレクションを操作するためのヘルパーメソッドを作成できます。ヘルプメソッドを記述してIQueryableを完全に使用する場合は、すべての列挙型でAsQueryableを使用してそれらを使用できます。これにより、非常に一般化されたヘルパーメソッドの2つの別個のバージョンを記述することを回避できます。

  3. これにより、クエリ可能オブジェクトのコンパイル時タイプを、より派生したタイプではなく、IQueryableに変更できます。実質的にIQueryableAsEnumerableを使用するのと同時に、IEnumerableで使用します。 IQueryableを実装するオブジェクトがありますが、インスタンスSelectメソッドもあります。その場合、LINQ Selectメソッドを使用する場合は、オブジェクトのコンパイル時の型をIQueryableに変更する必要があります。キャストすることもできますが、AsQueryableメソッドを使用すると、型推論を利用できます。これは、ジェネリック引数リストが複雑な場合は単純に便利です。実際には、ジェネリック引数のいずれかが匿名型の場合はnecessaryです。

56
Servy

AsQueryableで最も有効なケースは、単体テストです。次のやや不自然な例があるとします

public interface IWidgetRepository
{
   IQueryable<Widget> Retrieve();
} 

public class WidgetController
{
   public IWidgetRepository WidgetRepository {get; set;}


   public IQueryable<Widget> Get()
   {
      return WidgetRepository.Retrieve();
   }
}

そして、コントローラーがリポジトリーから返された結果を返すことを確認するための単体テストを作成したいと思います。次のようになります。

[TestMethod]
public void VerifyRepositoryOutputIsReturned()
{    
    var widget1 = new Widget();
    var widget2 = new Widget();
    var listOfWidgets = new List<Widget>() {widget1, widget2};
    var widgetRepository = new Mock<IWidgetRepository>();
    widgetRepository.Setup(r => r.Retrieve())
      .Returns(listOfWidgets.AsQueryable());
    var controller = new WidgetController();
    controller.WidgetRepository = widgetRepository.Object;

    var results = controller.Get();

    Assert.AreEqual(2, results.Count());
    Assert.IsTrue(results.Contains(widget1));
    Assert.IsTrue(results.Contains(widget2));
}

本当に、AsQueryable()メソッドでできることは、モックを設定するときにコンパイラを満足させることだけです。

ただし、これがアプリケーションコードで使用される場所に興味があります。

10
chris

Sanjuroが指摘したように、AsQueryable()の目的は Linq To ObjectsおよびLinq To SQLでのAsQueryableの使用 で説明されています。特に、記事には、

これは、エンティティの特定のメソッドがTのIQueryableを返し、一部のメソッドがListを返すという実際のWordシナリオで優れた利点を提供します。ただし、コレクションがTのIQueryableまたはTのIEnumerableとして返されるかどうかに関係なく、すべてのコレクションに適用する必要があるビジネスルールフィルターがあります。パフォーマンスの観点から、データベースでビジネスフィルターの実行を活用したい場合それ以外の場合、コレクションはIQueryableを実装し、Linqを使用してデリゲートのオブジェクト実装にメモリ内のビジネスフィルターを適用します。

8
Scott

AsQueryable()の目的については、この記事で詳しく説明しています Linq To ObjectsおよびLinq To SQL でAsQueryableを使用する

MSDN Queryable.AsQueryableメソッドの備考セクションから:

ソースのタイプがIQueryableを実装している場合、AsQueryable(IEnumerable)はそれを直接返します。それ以外の場合は、QueryableのメソッドではなくEnumerableの同等のクエリ演算子メソッドを呼び出してクエリを実行するIQueryableを返します。

それはまさに上記の記事で言及され使用されているものです。あなたの例では、orderRepo.GetAllが返すもの、IEnumerableまたはIQueryable(Linq to Sql)に依存します。 IQueryableを返す場合、Count()メソッドはデータベースで実行されます。それ以外の場合は、メモリで実行されます。参照記事の例を注意深く見てください。

3
sanjuro

インターフェイスIQueryable引用ドキュメント:

IQueryableインターフェイスは、クエリプロバイダーによる実装を目的としています。

そのため、.NETでdatastractureをクエリ可能にするつもりの場合、そのデータ構造不要なものを列挙したり、有効な列挙子を持つことができます

IEnumeratorは反復処理のためのインターフェースですデータのストリーム代わりに。

2
Tigran