web-dev-qa-db-ja.com

IQueryable <T>を返す、またはIQueryable <T>を返さない

LINQ to SQL Data Contextをラップするリポジトリクラスがあります。リポジトリクラスは、すべてのデータ層ロジック(およびキャッシュなど)を含むビジネスラインクラスです。

これが私のレポインターフェースのv1です。

public interface ILocationRepository
{
    IList<Location> FindAll();
    IList<Location> FindForState(State state);
    IList<Location> FindForPostCode(string postCode);
}

ただし、FindAllのページングを処理するために、IListではなくIQueryable <ILocation>を公開してページングなどの状況でのインターフェイスを簡略化するかどうかを検討しています。

データリポジトリからIQueryableを公開することの長所と短所は何ですか?

どんな助けでも大歓迎です。

73
CVertex

長所;構成可能性:

  • 発信者はフィルターを追加できます
  • 発信者はページングを追加できます
  • 発信者は並べ替えを追加できます

短所;非テスト性:

  • リポジトリは適切に単体テストできなくなりました。信頼できないa:機能する、b:何をする機能する;
    • 呼び出し元は、翻訳不可能な関数を追加できます(つまり、TSQLマッピングはありません。実行時に中断します)。
    • 発信者は、犬のように機能するフィルター/ソートを追加できます
  • 呼び出し元はIQueryable<T>構成可能であるため、構成不可能な実装を除外します-または、独自のクエリプロバイダーを作成するように強制します
  • dALを最適化/プロファイルできないことを意味します

安定性のために、私はnotを公開してIQueryable<T>またはExpression<...>自分のリポジトリ。これは、リポジトリがどのように動作するかを知っており、上位層が「実際のリポジトリがこれをサポートしているか」を心配することなくモックを使用できることを意味します。 (統合テストの強制)。

私はまだIQueryable<T> etcリポジトリの内部-境界を超えていない。私はいくつか投稿しました このテーマについてのより多くの考えはここにあります 。リポジトリー・インターフェースにページング・パラメーターを配置するのも同じくらい簡単です。 (インターフェースで)拡張メソッドを使用してoptionalページングパラメーターを追加することもできるので、具象クラスは実装するメソッドが1つだけですが、2つまたは3つのオーバーロードが利用できる場合があります発信者に。

90
Marc Gravell

前の回答で述べたように、IQueryableを公開すると、呼び出し元がIQueryable自体で遊ぶことができます。これは、危険になるか、危険になる可能性があります。

ビジネスロジックの最初の役割は、データベースの整合性を維持することです。

IListを公開し続けることができ、次のようにパラメーターを変更することができます。これが私たちのやり方です...

public interface ILocationRepository
{
    IList<Location> FindAll(int start, int size);
    IList<Location> FindForState(State state, int start, int size);
    IList<Location> FindForPostCode(string postCode, int start, int size);
}

サイズ== -1の場合、すべてを返します...

別の方法...

それでもIQueryableを返したい場合は、関数内でListのIQueryableを返すことができます。たとえば...

public class MyRepository
{
    IQueryable<Location> FindAll()
    {
        List<Location> myLocations = ....;
        return myLocations.AsQueryable<Location>;
        // here Query can only be applied on this
        // subset, not directly to the database
    }
}

最初の方法は、すべてではなく少ないデータを返すため、メモリよりも優れています。

7
Akash Kava

IEnumerableの代わりにIListを使用することをお勧めします。これにより、柔軟性が向上します。

これにより、リポジトリで追加の作業を行わなくても、実際に使用するデータの部分のみをDbから取得できます。

サンプル:

// Repository
public interface IRepository
{
    IEnumerable<Location> GetLocations();
}

// Controller
public ActionResult Locations(int? page)
{
    return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}

とてもきれいでシンプルです。

2