当被不同的 dbContexts 跟踪时,我如何“应该”在 WinForms 中保持对象同步?

问题描述

我有一个 VB WinForms 项目,我们使用 Entity Framework 6 代码优先。我知道在 Web 应用程序中,限制 dbContexts 的生命周期是理想的(我们通过存储库类间接使用它),但我最近了解到,您应该在表单的生命周期内保留 dbContext,通常用于 WinForms 项目.

假设我有 ClassA 和 ClassB,所有 ClassA 对象都有一个可选的 ClassB 对象(以及相关的“ClassBId”属性供 EF6 用作外键)。我们有一个表单 ClassAForm,供用户编辑和查看 ClassA 对象,这自然会显示有关其 ClassB 对象的一些信息;这个 ClassB 应该是 ClassA 独有的,但也可以在其他形式上查看。此表单上有一个按钮可以打开另一个表单 ClassBForm,它允许用户编辑和查看 ClassB 对象。 ClassBForm 不是模态的,并且当前不会将任何内容传递给产生它的 ClassAForm,尽管 ClassAForm 确实有 ClassBForm 中的事件侦听器。

因此,根据我的理解,ClassAForm 将获得它自己的长期存在的 dbContext 对象,并在表单生命周期内保留该对象; ClassBForm 将获得自己的 dbContext 对象。因此,如果我对 ClassBForm 的 dbContext 中的 ClassB 对象进行更改,在 ClassAForm 的 dbContext 版本的 ClassB 对象中查看这些更改的理想方法是什么,其中 ClassB 可能是 ClassA 的导航属性

我可以想到一些解决此问题的方法,但我不确定最佳做法是什么;也有可能(我希望有人会提到)我的整个理解或框架是不正确的。

(可能)我想到的解决方案:

  1. 忽略 MS 建议,只为特定的数据库交互保持 dbContexts 活动;在每个更新事件和表单加载时刷新 dbContext(s),就像它是一个网络应用程序
  2. 将相同的 dbContext(s) 从一个表单传递到另一个表单(在我的存储库类的构造函数中,在我的例子中)并且很少处理它(不理想 - 数据库被几个服务更新)
  3. 在我的数据层中手动附加/分离并将更新标记为已更新
  4. 通过事件将更新的对象传递给其他表单,并在每个上下文中手动更新对象(我认为当我需要保存更改时会遇到麻烦?)

解决方法

我认为这种情况对于 ORM 来说是最糟糕的情况之一,因为它们往往会干扰您对域实体对象所做的几乎所有操作,并产生错误或不需要的更改。

遇到类似问题时,我可以想到两种不同的可能解决方案:

让每个表单都有自己的上下文,不要在它们之间交换对象

这样,当您打开 ClassBForm 时,您不会传递 ClassB,而是仅传递 Id,并且新表单使用其自己的上下文来加载对象。然后在保存时,再次使用其自己的私有副本,当您发送通知事件时,您仅通知 Id,而 ClassAForm 使用其上下文再次重新加载修改后的 ClassB。两种形式都不会交换标识符以外的数据,并尽可能保持独立。

根本不要在表单中使用实体

实现 DTOs/viewmodels/任何显示和修改数据**。在您使用 ORM 加载的数据层中,然后返回另一个类(比如 ClassADTOClassBDTO),其中将包含其各自表单操作所需的所有信息。当您添加一个额外的步骤并且需要为每个表单定义定制的类时,您确保 ORM 被排除在外,仅在它所属的 DAO 中工作。 DTO 可以安全地传递而不会产生任何不需要的结果,因为它们只是 POCO。这样,上下文只在一次查询期间存在,就像在网页中一样。

关于您在问题中提出的解决方案,我认为它们都不会完全有效,或者至少需要仔细注意边缘情况错误:

忽略 MS 建议,只为特定的 DB 交互保持 dbContexts 活动;在每个更新事件和表单加载时刷新 dbContext(s),就像它是一个网络应用程序

只要您不想使用延迟加载或保存更改(由于已处理的上下文而导致异常),这就可以正常工作。每当您想使用对象时,您可能需要手动将它们附加到上下文,并注意每个可能会爆炸的延迟加载属性。

将相同的 dbContext(s) 从一个表单传递到另一个表单(在我的存储库类的构造函数中,在我的例子中)并且很少处理它(不理想 - 数据库被几个服务更新)

这与理想情况几乎相反。通过将两种形式的更改联系在一起,一种形式的更改会影响另一种形式,您无法独立编辑两个对象,最重要的是,当您只想保存一个更改时,您最终会在两个位置保存更改。所有本来应该帮助你的 ORM 功能最终都会让你感到厌烦。

在我的数据层中手动附加/分离和标记更新为更新

这是可行的,但要跟踪每个对象的状态以及何时附加/分离是一项艰巨的工作。分离的对象会遇到与短期上下文相同的延迟加载问题。忘记附加对象并默默地以不一致的更新结束太容易了。对我来说,这似乎是一个难以重现错误的秘诀。

通过事件将更新的对象传递给其他表单,并在每个上下文中手动更新对象(我认为当我需要保存更改时会遇到麻烦?)

根据上下文的排列方式,它的工作方式会有所不同。对于共享上下文,您最终会在两种形式中获得完全相同的对象,而对于单独的对象,您最终会在下一次保存时进行不需要的更新,可能会覆盖更改。这就是为什么我建议只重新加载适当的上下文并忽略来自其他表单的数据。