结合 ORM、工作单元和存储库模式的主从视图

Posted

技术标签:

【中文标题】结合 ORM、工作单元和存储库模式的主从视图【英文标题】:Master-Detail view in combination with ORM, Unit of Work and Repository patterns 【发布时间】:2012-03-30 00:29:23 【问题描述】:

我不确定如何结合 ORM 最好地实现主详细信息视图。 该应用程序将 WPF 与 MVVM 结合使用,并显示所有可用对象的网格以及当前选定对象的详细信息。

视图的 ViewModel 非常简单,它有:

ObservableCollection<ItemViewModel> Items 其中ItemViewModel 是应在视图中显示的域对象的视图模型。此属性绑定到网格。 绑定到网格的SelectedItemItemViewModel CurrentItem 属性。 ICommand 用于“添加新项目”、“删除选定项目”和“保存对选定项目的更改”。

我的应用程序使用 NHibernate 作为其 ORM 工具,但我不想在整个代码库中泄漏 NHibernate,因此我使用“工作单元”和“存储库”模式将其抽象出来。

这些模式的 NHibernate 特定实现是这样的,“工作单元”在开始时打开一个新的 NHibernate 会话和事务,最后提交事务并处理会话。因此,工作单元、会话和事务的生命周期是相同的。工作单元有一个属性Repository,它使用与工作单元相同的会话,并且具有相同的生命周期。

这就是问题所在: 我想在MasterDetailViewModel 的构造函数中填充Items 集合。目前,我需要创建一个新的工作单元并填充集合。为了没有长时间运行的事务,UoW 将在之后直接处置,同时处置基础会话。

现在,当用户想要保存对当前项目的更改时,我需要打开另一个 UoW,从数据库中检索实体,使用 ItemViewModel 的当前值更新它,将其保存到数据库中并处置 UoW。 但是,这种方法有几个重要的缺点:

    我的代码中到处都是using(var uow = uowFactory.StartNew())。 没有发生乐观锁定。如果其他人更改了数据库中的相同项目,他的更改将被默默地覆盖。 更新项目需要两次数据库命中而不是一次。

这使我得出结论,即我的 UoW 的实施存在缺陷。

我考虑过更改实现,以便颠倒 UoW 和存储库之间的关系。这意味着 IoC 会将 Repository 注入 ViewModel 而不是 UnitOfWorkFactory。存储库现在是 NHibernate 的ISession 的功能等效项。存储库可以启动一个新的 UoW,它现在与数据库事务相同。 这很适合我的主/细节场景,但它不支持通常用作工作单元同义词的“业务事务”的概念,例如跨越多个数据库事务和用户请求的逻辑事务。

问题是:如何最好地实现工作单元和存储库这两种模式以在两种情况下都可用?

【问题讨论】:

我没有使用抽象,但我打开了一个长时间运行的会话,它填充网格并驱逐所有不再显示的项目(显示项目周围的框架除外)并使用 transaction.Begin() , transaction.Commit() 围绕每个业务操作“添加新”、“更改”等。 @Firo:感谢您再次向我保证,在这种情况下,长时间运行的会话是一件好事。 【参考方案1】:

更改开始业务交易的地点并不一定会改变其粒度。

不确定我是否完全理解了您的示例,但我看不出在每个存储库的方法(每次查询持久存储时)中启动一个新的 UoW 与在MasterDetailViewModel 会填充其项目,并且每次保存其中一项时都会填充另一项。

但是,正如您所建议的,我将存储库注入 ViewModel 本身,因为 UoW 拥有存储库属性似乎很奇怪 - UoW 不直接操作存储库,而是一些在 UoW 的上下文中使用 Repository 的对象。

除此之外,我认为您几乎指出了与短期、细粒度的业务交易相关的缺点。您对此无能为力。

不过还有很多其他的会话策略。您会在此article 中找到一些建议。

另见

What is your session management strategy for NHibernate in desktop applications?

What should be the lifetime of an NHibernate session?

【讨论】:

以上是关于结合 ORM、工作单元和存储库模式的主从视图的主要内容,如果未能解决你的问题,请参考以下文章

只读数据库视图如何适应存储库模式?

MySQL主从复制必须了解的性能视图

带有存储库的 Laravel View Composer 不工作;

MVC3 EF 工作单元 + 通用存储库 + Ninject

在代码存储库中存储 couchDB 视图

如何将 Propel orm 视为查询类?