用实体框架和 POCO 类替换遗留数据层的策略
Posted
技术标签:
【中文标题】用实体框架和 POCO 类替换遗留数据层的策略【英文标题】:Strategies for replacing legacy data layer with Entity framework and POCO classes 【发布时间】:2011-08-29 10:53:26 【问题描述】:我们正在处理的这个项目中使用 .net C# 4.0、VS 2010、EF 4.1 和遗留代码。
我正在开发一个 win 表单项目,我已决定开始使用实体框架 4.1 来访问 ms sql db。代码库很旧,我们有一个使用数据适配器的现有数据层。这些数据适配器被广泛使用(在 web 应用程序和 win 表单应用程序中)我的计划是随着时间的推移用 EF 替换旧的 db 访问代码,并摆脱 UI 层和数据层之间的紧密耦合。
所以我的想法是或多或少地将 EF 与旧数据访问层结合起来,并慢慢地用更现代的方式替换旧数据层使用 EF。所以现在我们需要同时使用 EF 和旧的数据库访问代码。
到目前为止,我所做的是添加一个包含 edmx 文件和上下文的项目。 edmx 是使用数据库优先方法生成的。我还添加了另一个包含 POCO 类的项目(通过使用 ADO.NET POCO 实体生成器)。我或多或少地遵循了 Julia Lerman 在她的《编程实体框架》一书中关于如何拆分模型和生成的 POCO 类的方法。数据库模型已经设置多年,它不是更改表和关系、触发器、存储过程等的选项,所以我基本上坚持使用 db 模型。
我已经阅读了关于存储库模式和工作单元的信息,并且我有点喜欢这些模式,但是当我同时要处理 EF 和遗留数据库访问代码时,我很难实现它们。特别是当我没有时间用纯 EF 实现替换所有遗留的数据库访问代码时。在一个完美的世界里,我会从一个全新的数据模型重新开始,但这不是一个选择。
存储库和工作单元模式是要走的路吗?为了在我的业务层中使用 POCO 类,我有时需要同时使用 EF 和遗留数据库代码来填充我的 POCO 类。换句话说,我有时可以使用 EF 来检索我需要的部分数据,并使用旧的 db 访问层来检索其余数据,然后将数据映射到我的 POCO 类。当我想更新一些数据时,我需要从 POCO 类中挑选数据并使用遗留数据访问代码将数据存储在数据库中。因此,当我想在 UI 中显示数据时,我需要将从遗留数据访问层检索到的数据映射到我的 POCO 类,反之亦然,当我想将数据保存到数据库时。
为了使事情复杂化,我们将一些数据存储在运行时之前我们不知道名称的表中(请不要问我为什么:-))。因此,在旧的数据库访问层中,我们必须动态创建 sql 语句,根据其他表的信息插入表和列名。
我还发现 POCO 类之间的关系有点过于以数据库为中心。换句话说,我觉得我需要一个更简化的领域模型来使用。也许我应该创建一个符合要求的域模型,然后使用 POCO 类作为“DAO”来填充域模型类?
您将如何使用存储库模式和工作单元模式来实现这一点? (如果这是要走的路)
【问题讨论】:
【参考方案1】:从我们的实施情况中,我学到了以下几点。
-
POCO 和 Self Tracking 对象很难处理,好像您对里面的内容不太了解,会出现许多意外行为,这些行为可能在您之前的项目中运行良好。
改变模式并不容易,到目前为止,我们一直在管理没有工作单元和身份映射模式的简单 CRUD。现在我们过去编写的许多遗留代码都没有考虑这些新模式,因此逻辑将无法正常工作。
在我们之前的代码中,我们只是使用事务和单个插入/更新/删除语句直接发送到数据库,假设服务器端的事务将处理所有操作。
在这种情况下,我们一直在直接处理 ID,新生成的 ID 在单个插入语句后立即可用,但 EF 并非如此。
在 EF 中,我们不是在处理 ID,而是在处理导航属性,这与早期的 ADO.NET 编程方法相比是一个巨大的变化。
根据我们的经验,我们发现只有用较早的数据访问代码替换 EF 才会导致混乱。但是 EF + RIA 服务为您提供了一个全新的解决方案,您可能会获得所需的一切,并且您的 UI 将非常容易地绑定到它。因此,如果您正在考虑使用 UI + RIA 服务 + EF 进行完全重写,那么它是值得的,因为查询管理中的大量依赖性会自动减少。您将只关注业务逻辑,但这是一个重大决定,完全重写或仅替换 EF 所需的工时几乎相同。
所以我们采用了 UI + RIA Services + EF 的方式,我们开始更换一个模块。大多数情况下,EF 很容易与您现有的基础架构共存,因此不会造成任何损害。
【讨论】:
感谢您的回答!非常感谢!:)【参考方案2】:警钟正在为我敲响!不久前我们尝试做类似的事情(仅使用 nHibernate 而不是 EF4)。我们在运行 ADO.NET 和 ORM 时遇到了几个问题——数据库并发是一个大问题。
数据库模型已设置为 年,这不是一个选择 改变表和 关系、触发器、存储 程序等,所以我基本上 坚持使用 db 模型。
是的。一样!问题是我们的存储过程包含很多业务逻辑,而不是简单的 CRUD 过程,因此通过存储过程执行的各种更新来更新 ORM 一点也不容易 - 单一责任原则 - 不是一个好的打破!
我的计划是更换旧的数据库 随着时间的推移使用 EF 访问代码并获得 摆脱紧耦合 UI 层和数据层之间。
也许您可以在不需要 ORM 的情况下解耦 - 在您的 UI 层前面放置一个服务/外观层以协调与底层域的所有交互并将其隐藏在 UI 中。
如果您的数据库是“王者”并且您的应用程序是高度数据驱动的,我认为您将始终在为实现您提到的模式而进行一场艰苦的战斗。
为这个项目采用 ado.net - 在您的下一个绿地项目中使用 EF4 和 DDD 模式 :)
【讨论】:
好答案!听到那些参加过“战争”并活着讲述这个故事的人总是很高兴:) 是的,不得不承认,EF 跟风并不总是灵丹妙药。绝对值得将所有内容隐藏在服务层后面(如建议的那样),因此至少 UI 远离噪音,并且包装了业务逻辑。【参考方案3】:EDMX + POCO 类生成器生成 EFv4 代码,而不是 EFv4.1 代码,但您不必担心这些细节。 EFv4.1 提供的只是不同的 API,它们的功能完全相同(而且它只是 EFv4 API 的包装)。
根据您使用数据集的方式,您可能会遇到一些非常棘手的问题。数据集是变更集模式的表示。他们知道对数据做了哪些更改,并且能够仅存储这些更改。 EF 实体仅在它们附加到从数据库加载它们的上下文时才知道这一点。使用分离实体后,您必须努力告诉 EF 发生了什么变化 - especially when modifying relations(分离实体是 Web 应用程序和 Web 服务中的常见场景)。为此,EF 提供了另一个名为 Self-tracking entities 的模板,但它们有 another problems 和限制(例如缺少延迟加载,当具有相同键的实体附加到上下文时,您无法应用更改等)。
EF 也不支持数据集中使用的多个功能 - 例如 unique keys 和 batch updates。有趣的是,较新的 MS API 通常可以解决以前 API 的一些问题,但同时提供的功能比以前的 API 少得多,这会带来新的问题。
另一个问题可能与性能有关 - EF 比使用数据集直接访问数据要慢,并且内存消耗更高(是的,报告了一些内存泄漏)。
您可以忘记使用 EF 访问您在设计时不知道的表。 EF 不允许任何动态行为。表名和数据库服务器的类型在映射中是固定的。另一个问题可能是您使用触发器的方式 - ORM 工具不喜欢触发器,并且 EF 在处理数据库计算值时功能有限(在数据库或应用程序中填充值的可能性是分离的)。
仅使用 EF 时,从 EF + 数据集填充 POCO 的方式听起来是不可能的。 EF 有一些允许的映射模式,但是将多个表映射到单个 POCO 类的可能性非常有限和受限(如果您希望这些表可编辑)。如果您的意思是从 EF 加载一个实体,从数据适配器加载另一个实体并在它们之间进行引用,那么您应该没问题 - 在这种情况下,存储库听起来像是合理的模式,因为存储库的目的正是这样:加载或持久化数据。工作单元也可以使用,因为您很可能希望在 EF 和数据适配器之间重用单个数据库连接,以避免在保存更改期间进行分布式事务。 UoW 将负责处理此连接。
EF 映射与数据库设计有关 - 您可以引入一些面向对象的修改,但 EF 仍然密切依赖于数据库。如果您想使用一些高级域模型,您可能需要从 EF 和数据集填充的单独域类。同样,存储库有责任隐藏这些细节。
【讨论】:
谢谢!很好的答案:) 我正在考虑编写自己的映射类来将 EF + 数据集实体转换为我的域模型实体,反之亦然。这种映射应该在存储库类中完成,以便方法返回域模型实体,还是应该在使用存储库类的类中完成? 我认为这是存储库的全部责任。 我的存储库实现有点吃力。我已经将一个类 Document 声明为我的聚合根(在我的域模型中),并且我有一个 DocumentRepository以上是关于用实体框架和 POCO 类替换遗留数据层的策略的主要内容,如果未能解决你的问题,请参考以下文章
如何从DbContext中清除未插入的POCO? - 实体框架代码优先