没有行为的实体框架 POCO - 需要重新设计以消除代码异味

Posted

技术标签:

【中文标题】没有行为的实体框架 POCO - 需要重新设计以消除代码异味【英文标题】:Entity Framework POCOs without behaviour - redesign needed to remove code smell 【发布时间】:2012-12-15 18:54:38 【问题描述】:

我正在使用具有 RepositoryUnit of Work 模式的实体框架模型优先,存储库返回 EF POCO。

我假设我无法将行为添加到由 Entity Framework 生成的 POCO,因此我的代码现在充满了 XyzService 之类的东西,这是一个单独的类,用于实现生成的实体框架 @987654324 的业务逻辑@。

我有以下问题:

    这有一种不好的代码味道,因为我不仅有 EF POCO,而且每个 POCO 都有服务。除了许多类之外,业务逻辑还被拆分到业务实体之外。这是贫血反模式的一个例子吗?

    如果我坚持使用 EF,有什么方法可以添加行为(即通过部分类)或其他方式?

    如果我想从 EF-MODEL -> REPOSITORY-DAL -> BIZ-ENTITY 出发,看到使用从数据层(在我们的例子中是存储库)返回业务实体的持久无知模式,我发现业务之间会有很多双向映射实体和 EF 模型 POCO。 Automapper 等实用程序能否优雅地处理我可能面临的嵌套对象的复杂关系?

    为了减少与其对应的 EF 模型实体重复的业务实体,我最好删除 EF 并为每个存储库使用 LINQ to SQL 编写自己的存储库实现吗?

    任何可以让我专注于代码的推荐方式(而不是像我一样首先将目标锁定在 EF 模型上),然后在我准备好编写持久层时仍然使用实体框架,但这样做避免了很多额外的工作和映射? EF Code-First 在这方面会更好吗?

如果我遗漏了其他有助于开发的其他技术(例如 NHibernate),请随时提及。

【问题讨论】:

您应该可以直接在 POCO 中添加行为。 【参考方案1】:
    是的,根据Fowler,这是一种反模式。我个人并不觉得这种反模式太冒犯,但有些人会。在这里使用最佳判断。如果感觉不对并且处理起来很痛苦,那就改变它。 Yes。部分课程可以帮助解决这个问题。您可以将行为放在您编写的部分中。 Yes,如果嵌套对象有映射设置,Automapper 会自动处理它们 再一次,这取决于你。如果 EF 让您发疯,请不要使用它。使用有效的方法和让您在使用时感觉良好的方法。 Code first 正是为此而构建的。

【讨论】:

【参考方案2】:
    确实如此,但这种方法并非没有优势。例如,强制分离 POCO 和业务逻辑可以允许该逻辑通过依赖注入或其他方式解耦和提供。

    请记住,部分类不能跨越多个程序集(来自MSDN):

    所有属于同一类型的部分类型定义必须在同一程序集和同一模块(.exe 或 .dll 文件)中定义。部分定义不能跨越多个模块。

    这种限制可能是不可取的,尽管您可以使用扩展方法来实现所需的行为。

    我没有使用过 Automapper,所以 Ryan 的建议在这里适用。

    & 5. 我将这些组合在一起,因为使用 Code-First 会影响您的特定存储库实现。如果您还没有尝试过 Code-First,我建议您看看它。

就存储库而言,我的偏好是,就我们正在处理的 POCO 类型和可用操作而言,存储库是完全通用的。例如,使用 LINQ,存储库可以有一个 Get 方法,该方法根据给定的 Expression<Func<T, bool>> 检索项目(例如,作为 IEnumerable<T>)。

我喜欢这种方法,因为它允许对存储库进行依赖注入,将标准 CRUD 操作与更多特定于域的操作完全分开,同时为消费者/派生类的代码重用提供了很好的机会。

【讨论】:

+1 用于提及部分类答案。我忘记了。【参考方案3】:

设计书籍、模式书籍和知名博主都可以,但是我们开发人员倾向于认为我们必须按照他们的建议开发我们的项目,即使这些书籍/文章没有为我们提供足够的上下文来理解它们适用于哪些情况当他们不这样做时。

对于所有类型的应用程序都没有像好的设计这样的东西。我看到您专注于技术点,这没关系,但首先您应该回答最重要的问题:您的要求是否适合该设计?

请记住,您有要求、约束、资源和时间以及其他考虑因素,这迫使您参与一些必须指导您的设计的权衡。

例如:

您真的需要存储库吗?您直接使用 EF 获得的实体是什么? 您需要 DataModel 和 BizModel 吗?如果您只使用 TransactionScripts 会怎样? 您需要 UoW 吗?为什么? EF 不能很好地为您处理这个问题吗?

这些问题的答案不是绝对的,它们取决于您的要求、时间、预算等。

现在,让我告诉你我对你的问题的看法:

    是的,由于某些原因,它闻起来很臭:a) POCO 对象应该是,嗯...... POCO,这是因为你有 biz-object,当然,有逻辑但是......什么样的POCO 对象有什么逻辑? 是的,应该使用部分类而不是您的 xyzService。这更清楚,因为您的 xysServices 根本不是服务! 是的,Automapper 可以处理映射问题。但是,它并不能解决您可能有很多映射的问题。同样,如果您不需要它,请避免使用它们。 您(或您的公司)是否有足够的金钱和时间来做这件事?如今,没有人在没有绝对充分理由的情况下避免使用 ORM。所以,不,不要这样做! 同上 4。

这是我的看法。

【讨论】:

【参考方案4】:

您可能想要走“代码优先” 路径。我曾经使用带有部分类的 EF 建模工具,但仍然存在重大问题,例如对生成的代码缺乏控制 - 更不用说建模器的零星问题,需要我重新创建关系。

借助“代码优先”,您可以更好地控制 POCO,并获得许多额外的好处,例如数据库迁移,以及使用 T4 模板等以您喜欢的方式构建 POCO 的能力。

我不会回到 LinqToSQL,EF 功能更丰富,未来更光明,IMO。 EF 现在是开源的,我查看了一些源代码以帮助解决我遇到的某些问题。

【讨论】:

以上是关于没有行为的实体框架 POCO - 需要重新设计以消除代码异味的主要内容,如果未能解决你的问题,请参考以下文章

是否有实体框架 7 数据库优先 POCO 生成器?

实体框架中的 POCO 是啥? [关闭]

IQueryable 实体框架 POCO 映射

使用 POCO 模板获取实体框架的连接字符串

WCF 和实体框架 4.1 POCO

实体框架 - 连接新添加的 Poco 实体并加载子对象(插入/添加)