我有一个建立在 NHibernate 之上的持久性框架,用于一些 Web 应用程序。它使用 Unity 提供的具体实例将 NH 实现隐藏在一个IRepository
和IRepository<T>
接口后面(因此,理论上我可以很容易地将 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());
}
}
}
}
在此示例中,如果不在 Web 上下文中,或者如果它在 Web 上下文中(以避免线程池问题) ,则PersistenceFrameworkContext.Current.Items
访问IList<object>
存储在其中的对象。对属性的第一次调用从存储的工厂实例中实例化,后续调用只是从存储中检索它。锁定会稍微减慢速度,但不如锁定 appdomain 范围的静态实例。ThreadStatic
HttpContext.Current.Items
ISession
ISession
然后我有BeginUnitOfWork
方法EndUnitOfWork
来照顾 UOW - 我特别禁止嵌套 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();
}
}
}
最后,一对方法提供对特定于域类型的存储库的访问:
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