いくつかのWebアプリで使用されているNHibernate上に構築された永続性フレームワークがあります。IRepository
これは、Unityによって提供される具体的なインスタンスを使用してNH実装をandインターフェイスの背後に隠しIRepository<T>
ます(したがって、理論的にはNHibernateをたとえばEntity Frameworkとかなり簡単に交換できます)。
Unityは依存性注入自体であるもの以外のコンストラクターパラメーターの受け渡しをサポートしていない(または少なくとも私が使用しているバージョンはサポートしていない)ため、既存のNHISessionを渡すことはできません。しかし、UOW内のすべてのオブジェクトが同じISessionを共有することを望んでいます。
スレッドごとにISessionへのアクセスを管理する制御リポジトリクラスを用意することで、これを解決します。
public static ISession Session
{
get
{
lock (_lockObject)
{
// if a cached session exists, we'll use it
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
}
else
{
// must create a new session - note we're not caching the new session here... that's the job of
// BeginUnitOfWork().
return _factory.OpenSession(new NHibernateInterceptor());
}
}
}
}
この例では、Webコンテキストにない場合、またはWebコンテキストにある場合(スレッドプールの問題を回避するため)に格納されているにPersistenceFrameworkContext.Current.Items
アクセスします。プロパティへの最初の呼び出しは、保存されたファクトリインスタンスからインスタンス化し、その後の呼び出しは、ストレージからそれを取得するだけです。ロックすると速度が少し遅くなりますが、appdomainスコープの静的インスタンスをロックするほどではありません。IList<object>
ThreadStatic
HttpContext.Current.Items
ISession
ISession
次に、UOWを処理する方法がBeginUnitOfWork
ありEndUnitOfWork
ます。ネストされたUOWは、率直に言って管理が面倒だったため、特に許可していません。
public void BeginUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
EndUnitOfWork();
ISession session = Session;
PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
}
}
public void EndUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
session.Flush();
session.Dispose();
}
}
}
最後に、2つのメソッドがドメインタイプ固有のリポジトリへのアクセスを提供します。
public IRepository<T> For<T>()
where T : PersistentObject<T>
{
return Container.Resolve<IRepository<T>>();
}
public TRepository For<T, TRepository>()
where T : PersistentObject<T>
where TRepository : IRepository<T>
{
return Container.Resolve<TRepository>();
}
(ここでPersistentObject<T>
は、IDとEqualsのサポートを提供する基本クラスです。)
したがって、特定のリポジトリへのアクセスはパターンになります
NHibernateRepository.For<MyDomainType>().Save();
次に、これをファサードして使用できるようにします
MyDomainType.Repository.Save();
特定のタイプに特殊なリポジトリがある場合(つまり、から取得できる以上のものが必要な場合) 、実装から継承する拡張実装であるIRepository<T>
から派生するインターフェイスを作成し、ドメインタイプ自体で静的プロパティをオーバーライドします。IRepository<T>
IRepository<T>
Repository
new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication
[実際の製品ではあまりうなずくものと呼ばれます]は、Repository
Unityを介してインスタンスを提供するファサードクラスであるため、ドメインクラス内の特定のNHibernateリポジトリ実装に依存しません。)
これにより、リポジトリの実装のためのUnityを介した完全なプラグイン可能性、フープを飛び越えることなくコードでリポジトリに簡単にアクセスできること、および透過的なスレッドごとのISession
管理が可能になります。
上記のコードよりもはるかに多くのコードがあります(そして、サンプルコードを大幅に簡略化しました)が、一般的な考え方は理解できます。
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble@wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically
私のWebアプリには、リクエストごとのセッションがあるのでBeginUnitOfWork
、それぞれとEndUnitOfWork
で呼び出されます。BeginRequest
EndRequest