我有一个基于 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());
}
}
}
}
在此示例中,PersistenceFrameworkContext.Current.Items 访问一个 IList<object>,如果不在 Web 上下文中,则该 ThreadStatic 或在 HttpContext.Current.Items 中(以避免线程池问题)。对属性的第一次调用从存储的工厂实例中实例化 ISession,随后的调用只是从存储中检索它。锁定会稍微减慢速度,但不如锁定 appdomain 范围的静态 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> 实现的扩展实现,并在域中类型本身我使用new覆盖静态Repository属性
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication [在实际产品中被称为不那么时髦的东西] 是一个外观类,负责通过 Unity 提供 Repository 实例,因此您无需依赖域类中的特定 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 中被调用。