gpt4 book ai didi

c# - 我应该在服务中的哪个位置创建DbContext?

转载 作者:太空宇宙 更新时间:2023-11-03 21:08:35 25 4
gpt4 key购买 nike

我的应用程序是围绕“服务”构建的,如下所示:

public class ProfileService : BaseService {

private CommService _commService;

public ProfileService(CommService commService) {
_commService = commService;
}

public ApiResponseDto GetProfile(string profileUsername) {
using (var db = new appContext()){ db.DoStuff(); }
}
}


我想做的就是将 db实例化到 BaseService中,但是我不想创建一个 dbContext并在不需要它时产生这种花费。所以我正在考虑做这样的事情:

public class BaseService {
public AppContext _db;

public AppContext db(){
return _db ?? new AppContext();
}
}


然后,我所有的方法都将通过 db().DoStuff()访问数据库。

我不喜欢到处都有括号的想法,但是我更喜欢清理服务足迹的想法。

我的问题是-如果我创建 DbContext的实例并且不使用它,会产生任何费用吗?还是只是实例化该对象以供将来使用?我不愿意在这里征求意见,因为我知道这是不允许的,但这是朝着保持干燥状态的正确方向迈出的一步吗?

最佳答案

工作单位模式

DbContext实际上是'unit of work'模式的实现-创建DbContext之后,对DbSet所做的所有更改都将在您调用SaveChanges时一次完成。

因此,为了正确回答问题,您需要回答的另一个问题是:构成您的工作单元的更改的范围是什么?换句话说-需要自动进行哪些更改集-全部成功还是全部失败?

一个实际的例子(如果是这样的例子-假设您有一个API终结点,该终结点公开了允许客户端提交订单的操作。控制器使用OrderService提交订单,然后使用InventoryService更新与订单中的项目相关的库存。如果每个服务都有其自己的DbContext,则存在OrderService将成功保留订单提交的风险,但是InventoryService将无法保留库存更新的风险。

依赖注入

为了解决这个问题,一种常见的模式是为每个请求创建一个上下文,并让您的IoC容器创建并处理该上下文,并使其可用于根据请求注入服务。 This blog post提供了一些DbContext管理选项,并提供了一个配置Ninject进行此操作的示例。

这意味着您的ctor将如下所示:

public ProfileService(CommService commService, AppContext context) {
_commService = commService;
_context = context;
}


而且您可以安全地在其中使用上下文,而不必担心它是如何创建的或来自何处。

Medhi的DbScopeFactory

但是,对于更复杂的应用程序,我首选的方法是一个出色的开源库,记录在这里: http://mehdi.me/ambient-dbcontext-in-ef6/。对于更简单的应用程序,每个请求注入DbContext效果很好,但是随着您的应用程序参与度更高(例如,每个应用程序具有多个Context,多个数据库等),他的 IDbContextScopeFactory提供的更精细的粒度控制是无价的。

编辑添加-注射与施工的利弊

在发表您的意见要求提出建议的方法的利弊之后,我要说的是,一般来说,依赖项(包括DbContext)的注入是一种更加灵活和强大的方法,并且仍然可以实现确保开发人员不会必须与dbcontext生命周期管理有关。

对于 dependency injection的所有实例,其优点和缺点通常是相同的,不仅是数据库上下文,而且在服务(甚至在基本服务中)中构造上下文存在一些具体问题:


每个服务将具有自己的dbcontext实例-这可能导致一致性问题,其中您的工作单元跨越了多个服务执行的任务(请参见上面的示例)
对服务进行单元测试将更加困难,因为它们正在构建自己的依赖关系。注入dbcontext意味着您可以 mock it in your unit tests并测试功能,而无需访问数据库
它将非托管状态引入服务-如果您使用依赖注入,则希望IoC容器管理服务的生命周期。当您的服务没有每个请求的依赖关系时,IoC容器将为整个应用程序创建该服务的单个实例,这意味着保存到私有成员的dbcontext将用于所有请求/线程-这可以 be a big problem和应该避免。


(注意:如果您不使用DI并在控制器内构造服务的新实例,那么这将不再是问题,但是在控制器级别上,您也将失去DI的优势...)

现在所有服务都被锁定为使用相同的DbContext实例-如果将来您决定拆分数据库并且某些服务需要访问不同的DbContext怎么办?您将创建两个不同的BaseServices吗?还是传递配置数据以允许基本服务切换? DI将解决这个问题,因为您只需注册两个不同的Context类,然后容器将为每个服务提供所需的上下文。
您要在任何地方返回 IQueryable吗?如果是这样,则存在一个风险,即即使DbContext超出范围, IQueryable也会导致Db命中-它可能已被垃圾收集器丢弃,将不可用。


从开发人员的角度来看,我认为没有什么比DI方法更简单的了-只需在构造函数中指定DbContext,然后让DI容器容器负责其余的工作。

如果您对每个请求使用DbContext,则甚至不必创建或处置上下文,并且可以确信IQueryables将在请求调用堆栈中的任何位置均可解析。

如果使用Mehdi的方法,则必须创建DbContextScope,但是如果沿着存储库模式路径并希望对上下文范围进行显式控制,则该方法更合适。

如您所见,我不必担心在不需要时构造DbContext的计算成本(据我所知,在您实际使用它来访问数据库之前,这是相当低的成本),而更多地关注的是有关允许进行单元测试和与依赖项脱钩的应用程序体系结构的信息。

关于c# - 我应该在服务中的哪个位置创建DbContext?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39442938/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com