はい、リポジトリを使用している場合でもコンテキストを破棄する必要があります。 ObjectContextをコンストラクタのパラメータとして提供しているので、リポジトリの実装があなたに与える利点ははっきりしません。
IMOリポジトリとカスタムUnitOfWorkを使用する主な理由は、ObjectContext + ObjectSet自体がリポジトリと作業単位パターンの実装であるため、上位アプリケーション層からの永続性ignorance = hidding EFコードです。
リポジトリを使用している場合、私は常にEFコード全体をラッピングしているので、リポジトリのパブリックインターフェイスはEF関連インフラストラクチャに関する情報を提供しません。その場合、ObjectContextをどのように処理するかは私の責任です。
簡単な単純CRUDシナリオのために、コンテキストの作成と各リポジトリメソッドへの処分をラップすることができます。より複雑なシナリオでは、私は追加のクラス(UnitOfWork(UoW))を使用しています。これは、コンテキストの作成と廃棄をラップし、変更をデータベースに保存します。また、すべてのリポジトリのファクトリとして機能し、作成されたコンテキストのインスタンスをリポジトリのコンストラクタに渡します。
ほとんどの場合、私はサービスやWebアプリケーションをプログラミングしているので、デタッチされたオブジェクトを扱っています。私は常にリクエスト処理のために単一のUoWインスタンスを使用しています。したがって、UoWは要求処理の開始時に作成され、要求処理の終了時に解放されます。 WinForms/WPFアプリケーションと添付オブジェクトの場合は、UoW/ObjectContextインスタンスを "フォームごとに"持たせることをお勧めします。 - articleがNHibernateセッション(EF ObjectContextと同じ)でこのアプローチをMSDNマガジンに記述しています。
たUnitOfWorkとリポジトリのパターンのいくつかの出発実装:リポジトリ
public interface IUnitOfWork
{
IRepository<MyEntity> MyEntityRepository { get; }
// Repositories for other entities
SaveChanges();
}
取り外しエンティティのリポジトリ
public interface IRepository<T> where T : class
{
IQueryable<T> GetQuery();
void Insert(T entity);
void Delete(T entity);
// In very complex scenarios with big object graphs you will probably give up
// using detached approach and you will always load your entities from DB before
// deleting or updating them. In such case you will not need Update method at all.
void Update(T entity);
}
EnitityフレームワークをラップたUnitOfWorkの使い捨て実装用
コンテキストホルダーと抽象工場
public class UnitOfWork : IUnitOfWork, IDisposable
{
private ObjectContext _context = null;
public UnitOfWork(string connectionString)
{
if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException("connectionString");
_context = new ObjectContext(connectionString);
}
private IRepository<MyEntity> _myEntityRepository;
public IRepository<MyEntity> MyEntityRepository
{
get
{
return _myEntityRepository ?? (_myEntityRepository = new GeneralRepository<MyEntity>(_context));
}
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
ベースリポジトリ実装
public class GeneralRepository<T> : IRepository<T> where T : class
{
private ObjectSet<T> _set;
private ObjectContext _context;
public GeneralRepository(ObjectContext context)
{
if (context == null) throw new ArgumentNullException("context");
_context = context;
_set = context.CreateObjectSet<T>();
}
// Override this method for example if you need Includes
public virtual IQueryable<T> GetQuery()
{
return _set;
}
// Override following methods if you are working with object graphs.
// Methods do not execute operations in database. It is responsibility of
// UnitOfWork to trigger the execution
public virtual void Insert(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
_set.AddObject(entity);
}
// These impelementations are for detached scenarios like web application
public virtual void Delete(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
_set.Attach(entity);
_set.DeleteObject(entity);
}
public virtual void Update(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
_set.Attach(entity);
_context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
}
使用データ
使用法を選択する場合!私がこれらのリポジトリに持つ唯一の問題は、 "Include" Eagerの読み込みを処理していないということです。あなたのリポジトリをどうやって熱心に読み込んでいますか? – user9969
これは難しい部分です。なぜなら、IncludeはEF ObjectQueryの機能だからです。私は通常、GeneralRepositoryから継承されたリポジトリを作成し、必要なインクルードをオーバーライドされたGetQueryに追加します。しかし、いくつかのクエリに対してのみ熱心な読み込みを有効にする必要がある場合、それはあなたを助けません。その場合は別のものが必要です。私はLinq-To-SQLからLoadOptionsのようなものを実装し、このオプションをUnitOfWorkまたはRepositoryに渡すことを想像することができます。次に、optinsを使用してすべての設定を設定します。 –
非常に深い答えですが、あなたの 'UnitOfWork'クラスは単一のローカルスコープの' IRepository'インスタンスを持っています。これは作業ユニットが作業するリポジトリを定義していることを意味します。これは正しくありません。作業単位のポイントは、複数のリポジトリ間でトランザクションを処理することです(UoWは通常、そのエージェントを介して1- *リポジトリを受け入れます)。この実装は実際にはそれをまったく処理しません。多分これはOPのために罰金です。 – RPM1984