我没有使用过 Spring.NET,所以我无法对此发表评论。然而,其余的听起来很显着(或者可能不那么显着;我们几乎不是第一个实现这些东西的人;)类似于我自己的经历。我也很难找到一个真正的最佳实践,所以我尽可能多地阅读并提出自己的解释。
在我的情况下,我希望事务/会话管理在存储库外部,并防止存储库问题从它们中冒出来(即使用存储库的代码不需要知道它在内部使用 NHibernate 并且不需要了解有关 NHibernate 会话管理的任何信息)。在我的例子中,我们决定默认创建事务,以免开发人员忘记它们,所以我必须有一个只读的转义机制。我采用了带有 NHibernate ISession 实例存储的工作单元模式。调用代码(我还为 UoW 创建了一个 DSL 接口)可能类似于:
using (var uow = UoW.Start().ReadOnly().WithHttpContext()
.InNewScope().WithScopeContext(ScopeContextProvider.For<CRMModel>())
{
// Repository access
}
在实践中,这可能与UoW.Start()
已有多少上下文一样短。该HttpContext
部分是指 UoW 的存储位置,不出所料,HttpContext
在这种情况下是 。正如您所提到的,对于 ASP .NET 应用程序,HttpContext
存储东西是最安全的地方。ScopeContextProvider
基本上确保为 UoW 提供正确的数据上下文(ISession 实例到相应的数据库/服务器,其他设置)。“ScopeContext”概念还使得插入“测试”范围上下文变得容易。
走这条路线会使存储库显式依赖于 UoW 接口。实际上,您可能可以将其抽象一些,但我不确定我是否看到了好处。我的意思是,每个存储库方法都会检索当前的 UoW 实例,然后提取 ISession 对象(或者对于那些不使用 NHibernate 的方法来说只是一个 SqlConnection)来运行 NHibernate 查询/操作。这对我有用,因为它似乎也是确保当前 UoW 对于可能需要运行 CRUD 的方法不是只读的理想时间。
总的来说,我认为这是解决您所有问题的一种方法:
- 允许会话管理在存储库外部
- ISession 上下文可以被模拟或指向测试环境的上下文提供者
- 避免不必要的交易(好吧,你必须颠倒我所做的并打个
.Transactional()
电话什么的)
- 我不明白为什么你不能用 SQLite 进行测试,因为这更像是 NHibernate 的问题
- 我自己使用 Fluent NHibernate
- 允许存储库不了解宿主环境(即存储库调用者控制 UoW 存储上下文)
至于 UoW 的实施,我在部分地责怪自己在开始之前没有多环顾四周。有一个名为machine.uow的项目,据我了解它相当流行,并且与 NHibernate 配合得很好。我没怎么玩过它,所以我不能说它是否像我自己写的那样巧妙地解决了我的所有要求,但它也可能节省了开发时间。
也许我们会得到一些关于我哪里出错或如何改进的评论,但我希望这至少在某种程度上有所帮助。
作为参考,我正在使用的软件堆栈是:
- ASP.NET MVC
- 在 NHibernate 之上流利的 NHibernate
- Ninject 用于依赖注入