リポジトリがIEnumerable <T>、IQueryable <T>またはList <T>を返しますか?

アプリケーションをできるだけ柔軟にしたいのですが、私のインターフェースをあまりにも具体的にすることで自分自身を穴に掘り下げることはできません。

リポジトリに最適なオブジェクトタイプは何ですか? IEnumerable、IQueryable、またはList?

私が使用しようとしている技術は

  • Azureアプリケーションファブリックキャッシング

  • Entity Framework 4.1

  • おそらくWindows Server AppFabric

13

4 答え

これは、エンティティに対して今後実行されるクエリを実行するかどうか、およびそれらがメモリ内にあるかどうかによって異なります。

  • 今後のクエリが必要な場合、DBはIQueryableを返します。
  • 将来のクエリがあり、それがメモリ内で実行される場合、IEnumerableを返します。
  • それ以上のクエリがなく、すべてのデータを読み込む必要がある場合は、IList、ICollectionなどを返します。
8
追加された
この過去のDALに基づいてクエリを発行することは、どこで問題ありませんか?コントローラにIQueryableを渡す必要がありますか?景色?
追加された 著者 CHI Coder 007,
このような質問はパフォーマンスチューニングに関係します。この場合、パフォーマンスは、すべての考慮されたオプション(および最もパフォーマンスが良いもの)またはアーキテクチャ上の考慮事項を考慮して測定する必要があります。後者の場合、IQueryableをコントローラに公開することをお勧めします(ドメインオブジェクトのコントローラ知識は問題ありません)ので、ビュー/ビューモデルはダムで、そのようなことの知識はありません...しかし、これは私の意見です!
追加された 著者 Rich O'Kelly,
IEnumerable を使用すると、クエリをデータベースで実行させることができます。 IQueryable のドキュメントには、この区別をしないようにクエリプロバイダを実装することが示されています。コレクション型を使用すると、間違って未評価のクエリではなく、実体化された結果が返されるようになるため、データベースをクエリするとIEnumerableを使用しない方がよいです。
追加された 著者 millimoose,
@ makerofthings7:厳密に階層化されたアーキテクチャでは、DAL以外の場所でDBバッククエリーを発行することはできません。メモリ内のクエリを実行してデータ構造をどこにでも変換することができます。また、すべてのコレクションをメモリ内で照会できるため、それを防ぐことはできません。
追加された 著者 millimoose,
IQueryable は返されるべきではないと思います。回答を投稿しました。理由をカバーしています。
追加された 著者 Neo,

私はあなたのDALをIQueryableを使ってビルドし、それを渡して、あなたのオブジェクトのcontects lifetimeがリクエストであることを確認します。この方法では、遅延実行の利点を得ることができますが、データベースの非効率的なクエリのリスクにさらされます。

次に、アプリケーションのパフォーマンスをテストして(またはトラフィックを獲得する可能性が最も高い部分を少なくとも)確認し、データアクセスのパターンを確認します。 DALに特殊なメソッドを作成して、完全にマテリアライズされたオブジェクトを取得し、これらのクエリを事前にコンパイルされたクエリとして作成します。

リポジトリインタフェースのサンプルは次のようになります

  public interface IDataContext
  {
        void Add(T entity) where T : BaseEntity;
        void Delete(T entity) where T : BaseEntity;
        IQueryable Find(Expression> where) where T : BaseEntity;
   }

BaseEntityはすべてのクラスの基本クラスですが、このクラスはdb内のどのテーブルにもマッピングされていません

public abstract class BaseEntity
{
        public int Id { get; set; }
        public DateTime CreateDateTime { get; set; }
        public string CreateUser { get; set; }
        public DateTime ModDateTime { get; set; }
        public string ModUser { get; set; }
        public byte[] RowVersion { get; set; }
}

Expression> would pass the whole expression to your repository instead of just Func, since EF works on expression to generate sql query, a typical use would be

ICollection wgGroups = this.dataContext.Find((w) => true).ToList();

WFGroupはBaseEntityから派生したクラスですが、私は通常遅延読み込みとプロキシを使用して、オブジェクトをコンテキストにデタッチ/アタッチしません。

5
追加された
私は、マテリアライズド・クエリを必要とするキャッシング・サービスを使用しています。 IEnumerable は私が見つけることができる最も良い共通の分母です。これはIEnmerable にすべてをフラット化するIQueryable DALの上にレイヤーがあることを意味しますか?もしそうなら、は何ですか?エンティティモデル、ビュー、またはビューモデル?
追加された 著者 CHI Coder 007,
興味深いことに、1対1のテーブルからエンティティへのマッピングではどのように動作するのかわかりますが、1:多くの関係のようにもっと複雑なものをどのように扱うのか不思議です。 stackoverflow.com/q/8095728/328397
追加された 著者 CHI Coder 007,
ありがとう、私は行くべき方向のように見える。私は Expression > についてしか読んでいませんが、使い方はわかりません。もう1つのサンプルを追加できますか?また、 BaseEntityStudentsEnrolledClasses のようなものかもしれません。
追加された 著者 CHI Coder 007,
IEnumerableは、IQueryableがIEnumerableであることを保証しません。私のケースではTはEntityですが、リポジトリに実装されたIDataContextインターフェイスがあり、すべてのメソッドはTが基本エンティティに制約されている汎用Tです。
追加された 著者 np-hard,
私はサンプルインターフェイスで答えを更新しました.1:nはエンティティモデルで処理されます。これは、変更されたt4を使用してEF4に実装するのは少し難しかったが、EF4.2を使用していた。
追加された 著者 np-hard,
あなたは正しいです、私はより多くのコードサンプルで答えを更新しました
追加された 著者 np-hard,
私はこの答えは時代遅れで間違ったアドバイスだと思う。潜在的なDBの濫用の点で危険ですが、SoCともっと関係している理由のいくつかの理由で IQueryable を回避する必要があります。最近の記事を参考にして、私が何を意味するのかを説明する回答を投稿しました。
追加された 著者 Neo,

DALから IEnumerable (コレクションではありません)のカスタム実装を返す必要性はどれくらいありますか? (この質問に答えるには、あなたの以前のプロジェクトを見て、どれくらいのものか、あなたが持っている yield returns

答えが「あまり」でない場合は、単に ICollection または配列を返すだけです(クエリ結果が誤って変更されないようにしたい場合)。ピンチでは、カスタム IEnumerable を使用して "stream"クエリを実行すると、古いメソッドを呼び出して新しいメソッドを呼び出すことができ、古いクライアントとの互換性を保つために結果を具体化できます。

4
追加された

ここに非常に良い記事がありますこれを、つまり「IQueryableを返すリポジトリ」という見出しの下に示します。これはそれが言うことです:

One of the reasons we use the repository pattern is to encapsulate fat queries. These queries make it hard to read, understand and test actions in ASP.NET MVC controllers. Also, as your application grows, the chances of you repeating a fat query in multiple places increases. With the repository pattern, we encapsulate these queries inside repository classes. The result is slimmer, cleaner, more maintainable and easier-to-test actions. Consider this example:

var orders = context.Orders
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Here we are directly using a DbContext without the repository pattern. When your repository methods return IQueryable, someone else is going to get that IQueryable and compose a query on top of it. Here’s the result:

var orders = repository.GetOrders()
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Can you see the difference between these two code snippets? The only difference is in the first line. In the first example, we use context.Orders, in the second we use repository.GetOrders(). So, what problem is this repository solving? Nothing!

Your repositories should return domain objects. So, the GetOrders() method should return an IEnumerable. With this, the second example can be re-written as:

var orders = repository.GetOrders(1234);

See the difference?

この結果、私はチーム内で次のコーディング規約を追加しました。

リポジトリクラスのメソッドについては、決して IQueryable オブジェクトを返すことはありません。常に列挙または変換してください(例: ToArrayToListAsEnumerable )。

IQueryable という理由は、呼び出し元がこれを基に構築し、最終的にデータベース上で実行されるSQLクエリを変更することを可能にします。これは潜在的にDB性能に関して危険ですが、それはSoCに関するものです。呼び出し元はデータソースを気にしません。それは単にデータが必要です。

1
追加された