【问题标题】:Implementing repositories using NHibernate and Spring.Net使用 NHibernate 和 Spring.Net 实现存储库
【发布时间】:2009-10-20 15:49:44
【问题描述】:

我正在尝试掌握 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) 的指针。

【问题讨论】:

    标签: nhibernate fluent-nhibernate domain-driven-design spring.net ddd-repositories


    【解决方案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

    【讨论】:

      【解决方案2】:

      Spring.NET 框架几乎开箱即可支持您所描述的内容。只有 FluentNHibernate 需要在 Spring.NET 中添加一个自定义的 SessionFactory(代码不多,看这里:Using Fluent NHibernate in Spring.NET)。

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

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

      【讨论】:

      猜你喜欢
      • 2011-09-03
      • 2019-05-02
      • 1970-01-01
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-20
      相关资源
      最近更新 更多