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

时间:2015-07-29 14:35:18

标签: c# asp.net-mvc linq entity-framework unit-testing

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

实体:这些是我的类,它们映射到数据库中的表,如User,Transaction,Order等。这些是POCO对象(我使用Code-First方法)。

域模型:这是业务逻辑应该存在的地方。我花了很长时间才知道领域模型与EF的内容不同。

服务层:我发现首先不需要服务层。它可以用来带来一些东西,但一般来说,业务逻辑应该在模型中。所以最好让它出来

存储库层:好的,我们可以使用CRUD编写存储库 - 像IEnumerable GetUsers()这样的方法可以让测试更容易,但另一方面我们会失去了整个LINQ功能,而且写得更多。为了测试,我也可以模拟EF框架,所以对我来说,存储库层已经出来了。

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

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

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

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

交易:不应再使用,因为它们有很多问题。而是坚持使用工作单元(内部使用事务?!)并以这种方式为您的架构建模。

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

问题1:模型=实体?

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

问题2:如何获取DbContext?

当我添加实体时,我不想添加像

这样的基础设施
order.Lines.Add(new OrderLine(product, qty, text));

而不是

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

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

1 个答案:

答案 0 :(得分:-1)

问题1

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

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

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

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

问题2

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

...