实体中的业务逻辑并注入 DbContext

Posted

技术标签:

【中文标题】实体中的业务逻辑并注入 DbContext【英文标题】:Business logic in entity and inject DbContext 【发布时间】:2015-10-20 14:51:31 【问题描述】:

我知道围绕这个主题有很多线程,但我没有找到任何让我满意并真正解释如何在实体框架内开发业务逻辑的帖子。所以在这篇文章中,我想总结一下我通过阅读其他各种帖子所得到的信息,然后请你帮我清理一下思路。

实体:这些是我的类,它们映射到数据库中的表,如用户、事务、订单等。这些是 POCO 对象(我将它们与 Code-First 方法一起使用)。

域模型:这是业务逻辑应该在的地方。我花了很长时间才发现域模型与 EF 的实体不同。

服务层:我发现一开始就不需要服务层。它可以用来将一些东西带入其中,但通常业务逻辑应该在模型中。所以最好把它说出来

Repository-Layer:好的,我们可以编写一个带有 CRUD-Methods 的存储库,例如 IEnumerable GetUsers(),这使测试更容易,但另一方面,我们将失去整个 LINQ 功能,而且要编写更多内容。为了测试,我还可以模拟 EF 框架,所以对我来说,存储库层已经出来了。

工作单元: DbContext 本身就是一个工作单元。所以我不必在这里编写任何特殊的代码,我只需将 DbContext 传递给我的所有方法并在完成后调用 SaveChanges。

延迟加载:有时我确实使用它,有时我确实使用 Eage Loading。但与此同时,我发现,当您想要执行工作单元并保持代码干净时,延迟加载是必须的。当您在一个方法中并从另一个方法中获取一些传递给该方法的代码时,又从另一个方法中获取了它……您只想访问属性。你不在乎数据是否在那里。它必须自动加载。所以我想知道如何在没有任何延迟加载的情况下做到这一点。

DbContextScopes: 正如在其他帖子中讨论的那样,我们不应该在应用程序实例上使用 DbContext,我们也不应该在每个请求中都使用它。相反,我们应该为当前任务创建一个 DbContext 并将其传递给所有需要的方法。使用 [DbContextScopeFactory][1] 可以使这变得更容易。

依赖注入: 我总是应该使用 DI 在构造函数中注入需要的东西。这是有道理的,因为当我们进行单元测试时,我们可以放入模拟资源。我还读到属性注入不太好,不应该使用。

事务:不应再使用,因为它们有很多问题。而是坚持使用 Unit-Of-Work(它在内部使用事务?!)并以这种方式为您的架构建模。

所以现在我想知道如何真正使用这些东西。

问题 1:模型 = 实体?

我们应该创建某种单独的域模型还是可以将其包含在实体模型中?我认为一个额外的域模型似乎是很多代码的方式。为什么不将逻辑写入实体?有什么问题?

问题2:如何获取DbContext?

当我添加一个实体时,我不想添加基础设施的东西,比如

order.Lines.Add(new OrderLine(product, qty, text));

而不是

order.Lines.Add(new OrderLine(dbcontext, product, qty, text));

也许属性依赖注入是一种解决方案,但正如所说的那样,这也不是一个好的模式......

【问题讨论】:

【参考方案1】:

问题 1

您的域模型应该代表您的数据库,并且应该简洁明了,以便将数据从数据库轻松传输到应用程序的任何部分。如果您向模型添加逻辑,它们很快就会变得臃肿和沉重,因此每次您为每个域模型创建一个实例时,无论是否使用,您的逻辑都会随之而来!

如果您开始将所有逻辑放入域模型中,您很快就会意识到您想要访问其他域模型的属性,然后您必须开始将它们相互传递,并且很快就会变得混乱。它唯一有用的是您只编写一次代码,但这就是将您的逻辑分离到服务层的美妙之处。

拥有服务层的好处在于,每个服务都可以包含在 DTO 和(轻量级)域模型上执行特定功能或功能组的逻辑,它们只需要编写一次并且可以使用相同的代码在整个应用程序中多次。您可以将服务接口出来,并在需要时将每个服务作为依赖项添加到您的控制器中,以便您只将它们暴露给每个控制器实际需要的服务中逻辑的特定部分,而不是对于您之前可能已经编写到域模型中的每条逻辑,都需要更长的时间。

拥有一个服务层也应该会显着减少控制器中的代码量,因为您的业务逻辑将在您的服务中执行,而控制器操作只会将值传递给您的服务以进行处理和映射它们返回到您的视图模型。

问题 2

根据您正在执行的操作(如果您不在一个请求中使用多个线程),通常每个 HTTP 请求都应该有一个新的 DbContext - 这可以使用大多数 DI 容器进行配置。我会get yourself an Interface for the DbContext 并在您的 MVC 应用程序层中配置它并将其向上注入您的服务层。

...

【讨论】:

对不起,这完全错了。 DDD 传播不代表数据库的丰富模型。这就是所谓的持久性模型。应该注意的是,使用 ORM 或持久性模型的富域是一个有点困难的话题。具有丰富域的 DDD 最适用于事件溯源(和 CQRS)。拥有只携带数据而没有逻辑的模型会导致服务臃肿且难以维护,其中模型很容易陷入不一致的状态 这并没有错,因为它与特定的方法不匹配。这个问题真的不适合 SO,因为它不具体而且非常主观。不过感谢您的 2 美分,我什至没有听说过 DDD。

以上是关于实体中的业务逻辑并注入 DbContext的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 应用程序中的原则实体和业务逻辑

架构整洁之道-架构设计二

实体框架数据集映射

在业务逻辑层使用实体框架生成的类

使用实体框架时对将业务逻辑放在哪里感到困惑

autofac管理dbcontext