我必須建立在NHibernate的頂部的持久性框架,在幾個Web應用程序中使用。它將NH實現隱藏在一個接口IRepository
和IRepository<T>
接口後面,並提供了Unity提供的具體實例(因此我理論上可以相當容易地將NHibernate換成實體框架)。由於Unity不支持(或者至少我使用的版本不支持)構造函數參數的傳入,而不是那些依賴注入本身,所以傳遞一個現存的NH ISession是不可能的;但我確實希望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());
}
}
}
}
在這個例子中,PersistenceFrameworkContext.Current.Items
訪問存儲要麼ThreadStatic
如果不是在一個IList<object>
Web上下文,或者在HttpContext.Current.Items
之內(如果它位於Web上下文中)(以避免線程池問題)。對屬性的第一次調用從存儲的工廠實例中實例化ISession
,隨後的調用只是從存儲中檢索它。該鎖定會稍微減慢速度,但不會像鎖定應用程序域的靜態ISession
實例那樣。
然後我有BeginUnitOfWork
和EndUnitOfWork
方法來照顧UOW - 我已經明確地禁止嵌套的UOWs,因爲坦率地說,他們是一個痛苦的管理。
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();
}
}
}
最後,對方法提供訪問域類型特定庫:(這裏,PersistentObject<T>
是一個基類提供ID和equals支持)
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>();
}
訪問給定的存儲庫因此處於該模式中
NHibernateRepository.For<MyDomainType>().Save();
然後這是facaded了,這樣你可以使用
MyDomainType.Repository.Save();
如果給定類型有一個專門的倉庫(即需要超過它可以從IRepository<T>
獲得),那麼我創建一個接口,從IRepository<T>
,延伸實現從我IRepository<T>
實現繼承派生,並在域類型本身我重寫使用靜態Repository
財產new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication
[這就是所謂的真正的產品的東西少諾迪]是一個門面類需要supplyi的護理通過Unity的Repository
實例,所以你不需要依賴於你的領域類中具體的NHibernate存儲庫實現。)
這給了我完整的可插入性,通過統一資源庫的實現,在代碼中輕鬆訪問存儲庫而無需跳過環,並且透明,按線程ISession
管理。
還有很多比上面的代碼更多的代碼(並且我已經簡化了示例代碼),但是您會得到一般想法。
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("[email protected]");
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
。
就像你所說,這就是技術上對我的問題的答案,即使不是最佳的解決方案。我正在打勾你。 – David 2010-11-04 14:51:20