5

次のリポジトリがある場合:

public IQueryable<User> Users()
{
   var db = new SqlDataContext();
   return db.Users;
}

クエリが実行されたときにのみ接続が開かれることを理解しています。

public class ServiceLayer
{
   public IRepository repo;

   public ServiceLayer(IRepository injectedRepo)
   {
       this.repo = injectedRepo;
   }

   public List<User> GetUsers()
   {
       return repo.Users().ToList(); // connection opened, query fired, connection closed. (or is it??)
   }
}

この場合でも、リポジトリにIDisposableを実装させる必要がありますか?

Visual Studio Code Metricsは、確かにそうすべきだと考えています。

IQueryableを使用しているのは、サービスレイヤーへのクエリ(フィルター、ページングなど)を制御できるためです。したがって、IQueryableを使用しているという事実についてアーキテクチャ上の議論をしないでください。

ところで-SqlDataContextは、Entity FrameworkのObjectContextクラスを拡張するカスタムクラスです(POCOパーティを持つことができます)。

だから質問-私は本当にIDisposableを実装する必要がありますか?

もしそうなら、各メソッドが同じリポジトリインスタンスを共有するので、これがどのように可能かわかりません。

編集

Depedency Injection(StructureMap)を使用して、具体的なリポジトリをサービスレイヤーに注入しています。このパターンはアプリスタックをたどります-私はASP.NETMVCを使用しており、具体的なサービスがコントローラーに注入されます。

言い換えると:

  1. ユーザーがURLをリクエスト
  2. 新しいリポジトリインスタンスで作成された新しいServiceLayerインスタンスを受け取るコントローラーインスタンスが作成されます。
  3. コントローラはサービスでメソッドを呼び出します(すべての呼び出しは同じリポジトリインスタンスを使用します)
  4. リクエストが処理されると、コントローラーはなくなります。

ハイブリッドモードを使用してコントローラーに依存性を注入しています。これにより、StructureMapのドキュメントによると、インスタンスがHttpContext.Current.Itemsに格納されます。

だから、私はこれを行うことはできません:

   using (var repo = new Repository())
   {
      return repo.Users().ToList();
   }

これがDIの全体のポイントを打ち負かすので。

4

3 に答える 3

3

私はあなたが間違いなくすべきだと思います。Entity FrameworkがLinqToSql(私が使用しているもの)とは非常に異なる方法で接続を処理する場合を除いIDisposableて、接続を操作するときはいつでも実装する必要があります。トランザクションが正常に完了した後、接続が自動的に閉じることは事実かもしれません。しかし、正常に完了しない場合はどうなりますか?実装IDisposableは、接続が完了した後に接続が開いたままにならないようにするための優れた保護手段です。より単純な理由は、を実装することがベストプラクティスであるということIDisposableです。

実装は、これをリポジトリクラスに入れるのと同じくらい簡単です。

public void Dispose()
{
    SqlDataContext.Dispose();
}

次に、リポジトリで何かを行うときはいつでも(たとえば、サービスレイヤーで)、すべてをusing句でラップする必要があります。1つの句内で複数の「CRUD」操作を実行することもできるusingため、すべて完了したときにのみ破棄します。

アップデート

私のサービスレイヤー(LinqToSqlで動作するように設計しましたが、これがあなたの状況に当てはまるといいのですが)では、毎回新しいリポジトリを作成します。テスト容易性を考慮して、(リポジトリインスタンスではなく)リポジトリプロバイダーで依存性注入パスを使用しています。新しいリポジトリが必要になるたびに、usingこのように呼び出しをステートメントでラップします。

using (var repository = GetNewRepository())
{
    ...
}


public Repository<TDataContext, TEntity> GetNewRepository()
{
    return _repositoryProvider.GetNew<TDataContext, TEntity>();
}

このようにすると、すべてをモックすることができます(したがって、サービスレイヤーを個別にテストできます)が、接続を適切に破棄していることを確認してください。

単一のリポジトリで複数の操作を実行する必要がある場合は、次のようなものを基本サービスクラスに配置できます。

public void ExecuteAndSave(Action<Repository<TDataContext, TEntity>> action)
{
    using (var repository = GetNewRepository())
    {
        action(repository);
        repository.Save();
    }
}

action一連のCRUDアクションまたは複雑なクエリの場合がありますが、呼び出すとExecuteAndSave()、すべてが完了すると、リポジトリが適切に破棄されます。

于 2010-09-08T07:16:51.970 に答える
3

nhibernateで使用される一般的なアプローチは、begin_request(または他の同様のライフサイクルイベント)でセッション(ObjectContext)を作成し、それをend_requestで破棄することです。そのコードをHttpModuleに入れることができます。

ObjectContextが注入されるように、リポジトリを変更する必要があります。リポジトリは、ObjectContextのライフサイクルを管理するビジネスから抜け出す必要があります。

于 2010-09-08T12:56:51.940 に答える
2

編集-AyendeRahienから受け取ったアドバイス

Ayende Rahien(Rhino Mocks、Raven、Hibernating Rhinosの名声)からメールで返信がありました。

これは彼が言ったことです:

問題は、次のようにコンテキストを初期化することです。_genericSqlServerContext = new GenericSqlServerContext(new EntityConnection( "name = EFProfDemoEntities"));

これは、コンテキストがエンティティ接続を所有していないことを意味します。つまり、コンテキストはそれを破棄しません。一般に、コンテキストに接続を作成させることが非常に望ましいです。これを行うには、次を使用します。_genericSqlServerContext = new GenericSqlServerContext( "name = EFProfDemoEntities");

これは間違いなく理にかなっていますが、SqlServerContextを破棄すると、基になる接続も破棄されると思いましたが、私は間違っていたと思います。

とにかく、それが解決策です-今ではすべてが適切に処分されています。

だから私はもはやリポジトリで使用する必要はありません:

public ICollection<T> FindAll<T>(Expression<Func<T, bool>> predicate, int maxRows) where T : Foo
        {
            // dont need this anymore
            //using (var cr = ObjectFactory.GetInstance<IContentRepository>())
            return _fooRepository.Find().OfType<T>().Where(predicate).Take(maxRows).ToList();

そして、私のベースリポジトリでは、IDisposableを実装し、これを実行するだけです。

Context.Dispose(); // Context is an instance of my custom sql context.

それが他の人を助けることを願っています。

于 2010-09-09T02:06:28.253 に答える