使用实体框架和POCO类替换传统数据层的策略

7
我们正在使用.net C# 4.0,VS 2010,EF 4.1和遗留代码进行项目开发。
我正在处理一个Win Form项目,决定开始使用Entity Framework 4.1来访问MS SQL数据库。代码库相当老,我们有一个现有的数据层,使用数据适配器。这些数据适配器在各个地方都被使用(在Web应用程序和Win Form应用程序中)。我的计划是逐步用EF替换旧的数据库访问代码,并消除UI层与数据层之间的紧密耦合。
所以我的想法是将EF与遗留数据访问层更多地结合起来,并逐步用EF替换遗留数据层,使用更现代的方法。因此,现在我们需要同时使用EF和遗留的数据库访问代码。
到目前为止,我已经添加了一个包含edmx文件和上下文的项目。edmx是使用数据库优先方法生成的。我还添加了另一个项目,其中包含POCO类(使用ADO.NET POCO Entity Generator)。我基本上遵循了Julia Lerman在她的书“Programming Entity Framework”中的方法,如何拆分模型和生成的POCO类。数据库模型已经设置了多年,不改变表和关系、触发器、存储过程等不是一个选项,所以我基本上被困在了当前的数据库模型中。
我已经了解了存储库模式和工作单元,并且我喜欢这些模式,但是当我需要同时处理EF和遗留的数据库访问代码时,我很难实现它们。特别是当我没有时间用纯EF实现替换所有遗留的数据库访问代码时。在理想情况下,我会从头开始采取全新的数据模型,但这不是一个选项。
这里是否应该使用存储库和工作单元模式?为了在业务层中使用POCO类,有时我需要同时使用EF和遗留的数据库代码来填充我的POCO类。换句话说,我有时可以使用EF检索我需要的部分数据,然后使用旧的数据库访问层检索其余的数据,然后将数据映射到我的POCO类。当我想要更新某些数据时,我需要从POCO类中选择数据,并使用遗留数据访问代码将数据存储在数据库中。因此,当我想要在UI中显示数据时,我需要将从遗留数据访问层检索到的数据映射到我的POCO类,反之亦然,当我想要将数据保存到数据库时。
更复杂的是,我们在表中存储一些在运行时之前不知道名称的数据(请不要问我为什么:-))。因此,在旧的数据库访问层中,我们必须根据其他表中的信息动态创建SQL语句,其中插入表和列名称。
我发现POCO类之间的关系有点过于数据库中心化。换句话说,我觉得我需要一个更简化的领域模型来使用。也许我应该创建一个符合要求的领域模型,然后使用POCO类作为“DAO”来填充领域模型类?
如果这是正确的方法,您将如何使用存储库模式和工作单元模式来实现?
3个回答

5
警钟在我心中响起!我们之前尝试过类似的事情(只是使用nHibernate而不是EF4)。我们在ORM旁边运行ADO.NET时遇到了几个问题,数据库并发性是其中一个大问题。
数据库模型已经设置多年,改变表和关系、触发器、存储过程等都不是可选项,因此我基本上被困在现有的数据库模型中。
没错。同样的问题!问题在于我们的存储过程包含了许多业务逻辑,而不是简单的CRUD存储过程,因此使ORM跟踪存储过程执行的各种更新并不容易——单一职责原则——这不是一个好的原则要打破!
我的计划是逐步用EF替换旧的数据库访问代码,并消除UI层和数据层之间的紧密耦合。
也许您可以在不需要ORM的情况下解耦 - 如何在UI层前面放置一个服务/门面层来协调与底层域的所有交互并将其隐藏在UI中。
如果您的数据库是“王”,您的应用程序高度依赖数据,我认为您将始终在实施您提到的模式方面面临艰巨的挑战。
在此项目中接受ado.net-在您的下一个绿地项目中使用EF4和DDD模式 :)

好回答!总是很高兴听到那些曾经在“战斗”中生存下来并讲述自己故事的人的声音 :) - OKB
是的,必须同意跟随 EF (Entity Framework)并不总是一种万能解决方案。绝对值得将所有东西隐藏在服务层背后(正如建议的那样),以便至少将 UI 与噪声分开,并包装业务逻辑。 - Baldy

3
EDMX + POCO类生成器生成的是EFv4代码,而不是EFv4.1代码,但您不需要担心这些细节。EFv4.1提供了完全相同的不同API(它只是EFv4 API的封装器)。
根据数据集使用的方式,您可能会遇到一些非常困难的问题。数据集是变更集模式的表示形式。它们知道对数据进行了哪些更改,并且能够仅存储这些更改。EF实体只有在连接到从数据库加载它们的上下文时才知道这一点。一旦您使用分离的实体,您必须付出很大的努力告诉EF已发生了什么更改 - 特别是在修改关系时(分离的实体是Web应用程序和Web服务中常见的场景)。为此,EF提供了另一个称为自跟踪实体的模板,但它们具有其他问题和限制(例如缺少延迟加载,当具有相同键的实体附加到上下文时无法应用更改等)。
EF也不支持数据集中使用的一些功能,例如唯一键批量更新。新的MS API通常解决了以前API的一些问题,但同时提供的功能比以前的API少得多,这就引入了新的问题。
另一个问题可能是性能 - EF比使用数据集的直接数据访问慢,并且具有更高的内存消耗(是的,有一些内存泄漏报告)。
您可以忘记使用EF访问在设计时不知道的表。 EF不允许任何动态行为。映射中的表名和数据库服务器类型是固定的。使用触发器的方式可能会出现其他问题 - ORM工具不喜欢触发器,并且EF在处理数据库计算值时具有有限的功能(在数据库或应用程序中填充值的可能性是分离的)。
从EF +数据集中填充POCO的方式似乎在仅使用EF时将不可能。EF具有一些允许的映射模式,但将多个表映射到单个POCO类的可能性非常有限和受限制(如果您想使这些表可编辑)。如果您只是想从EF加载一个实体并从数据适配器加载另一个实体,然后只是在它们之间建立引用,那么您应该没问题——在这种情况下,存储库听起来像是合理的模式,因为存储库的目的正是:加载或持久化数据。工作单元也可以使用,因为您很可能希望在EF和数据适配器之间重复使用单个数据库连接以避免在保存更改期间进行分布式事务。UoW将负责处理此连接的位置。
EF映射与数据库设计相关-您可以引入一些面向对象的修改,但仍然与数据库密切相关。如果您想使用某些高级域模型,则可能需要从EF和数据集中填充单独的域类。同样,隐藏这些细节是存储库的责任。

谢谢!非常好的回答:)我在考虑编写自己的映射类,将EF+数据集实体转换为我的领域模型实体及其相反情况。这个映射应该在存储库类中完成,以便方法返回领域模型实体,还是应该在使用存储库类的类中完成? - OKB
我认为这完全是仓库的责任。 - Ladislav Mrnka
我在实现我的存储库时遇到了一些困难。我已经声明了一个名为Document的类作为我的聚合根(在我的领域模型中),并且我有一个DocumentRepository<Document>,它继承自一个带有标准方法(如Get、GetAll、Insert、Delete、Save)的通用Repository<t>。在DocumentRepository类内部,我使用EF,EF为我生成了持久化无知POCO实体,我计划在存储库内部使用它们(过度设计?)。你会如何实现例如DocumentRepository.GetAll(Spesification object)?GetAll()必须返回一个Document(领域对象)列表。谢谢! - OKB

0

从我们已经实现的部分来看,我学到了以下几点。

  1. POCO和自跟踪对象很难处理,如果您不了解内部情况,可能会出现许多意外行为,这些行为在以前的项目中可能运行良好。
  2. 更改模式并不容易,到目前为止,我们一直在管理简单的CRUD,没有使用工作单元和标识映射模式。现在,我们过去编写的大量遗留代码没有考虑这些新模式,逻辑将无法正确工作。
  3. 在我们以前的代码中,我们只是使用事务和单个插入/更新/删除语句,直接发送到数据库,假设服务器端的事务将处理所有操作。
  4. 在这种情况下,我们一直在直接处理ID,新生成的ID在单个插入语句后立即可用,但这在EF中并非如此。
  5. 在EF中,我们不处理ID,而是处理导航属性,这是与早期ADO.NET编程方法的巨大变化。
  6. 从我们的经验中发现,仅用早期数据访问代码替换EF将导致混乱。但是,EF + RIA服务为您提供了一个全新的解决方案,您可能会得到所需的一切,并且您的UI将非常容易地绑定到它。因此,如果您正在考虑使用UI + RIA服务+ EF进行完全重写,则值得,因为查询管理中的许多依赖关系会自动减少。您将只关注业务逻辑,但这是一个重大决定,完全重写或仅替换EF所需的人力工时几乎相同。
  7. 因此,我们采用了UI + RIA服务+ EF的方式,并开始逐个替换模块。大多数情况下,EF将很容易与您现有的基础架构共存,因此没有危害。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接