2

我正在尝试掌握 NHibernate、Fluent NHibernate 和 Spring。

遵循领域驱动的设计原则,我正在编写一个标准的分层 Web 应用程序,其中包括:

  • 表示层 (ASP.Net)
  • 一个业务层,包括:
    • 应用层(基本上是一组对 UI 层可见的方法)
    • 存储库接口和域组件(由应用层使用)
  • 持久层(基本上是业务层中定义的存储库接口的实现)

我想帮助确定一种实例化 NHibernate ISession 的方式,以便它可以在对业务层的单个请求的整个生命周期内由多个存储库共享。具体来说,我想:

  • 允许在存储库实现之外控制 ISession 实例和任何事务(可能通过 IOC 框架的某些方面,一个拦截器?)

  • 允许 ISession 实例以测试友好的方式可用于存储库(可能通过注入或通过一些共享的“上下文”抽象)

  • 避免创建任何不必要的事务(即仅执行只读操作时)

  • 允许我编写使用 SQLLite 的测试

  • 允许我使用 Fluent NHibernate

  • 允许存储库实现对主机环境保持无知。我还不知道业务层是否会与表示层一起在进程内运行,或者将在 WCF 下(在 IIS 中)单独托管,所以我不想将我的代码过于紧密地绑定到 HTTP 上下文(例如)。

我第一次尝试解决这个问题是使用注册表模式。将 ISession 实例存储在 ThreadStatic 属性中。然而,随后的阅读表明这不是最好的解决方案(因为 ASP.Net 可以在页面生命周期内切换线程,我相信)。

任何想法、零件解决方案、模式名称、最新示例(NHibernate 2)的指针将不胜感激。

4

2 回答 2

1

我没有使用过 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 用于依赖注入
于 2009-10-20T20:41:41.903 回答
1

您所描述的内容由 Spring.NET 框架支持,几乎是开箱即用的。仅对于 FluentNHibernate,您需要向 Spring.NET 添加自定义 SessionFactory(代码不多,请看这里:在 Spring.NET 中使用 Fluent NHibernate)。

每个存储库都可以使用相同的 ISession,只需在存储库中注入 SessionFactory 并使用 Spring.NET 的事务服务。

试试看,他们有非常详尽的文档恕我直言。

于 2009-10-29T09:01:39.683 回答