将 Dbcontext 配置为瞬态
Posted
技术标签:
【中文标题】将 Dbcontext 配置为瞬态【英文标题】:Configuring Dbcontext as Transient 【发布时间】:2017-06-14 21:30:08 【问题描述】:在 ASP.NET Core / EntityFramework Core 中,services.AddDbContext 方法会将指定的上下文添加为范围服务。据我了解,这是 Microsoft 建议的 dbcontext 生命周期管理。
但是,我们的工程师部门对此有很多争论,许多人认为需要尽快处理上下文。那么,将 dbcontext 配置为 Transient 并保持通常使用的相同存储库模式(即将上下文直接注入存储库的构造函数)以及支持灵活的单元测试的最佳方法是什么?
【问题讨论】:
嗯,Transient DbContext 的一个缺点是,你失去了工作单元功能,除非你自己实现它。默认情况下,DbContext 是瞬态的,因此在请求期间有效,所有服务(不仅是控制器)都将收到它的相同实例。如果出现问题,您可以将其回滚/不发出 SaveChanges 命令。使用瞬态你会失去它,每个服务都会有自己的 DbContext 实例。 另外我认为它没有多大价值,因为您通过构造函数注入 DbContext 并且您必须确保在您的服务中不会在处理后调用它。如果你真的需要 DbContext 的生命周期很短,最好创建一个像包装器这样的工厂来返回瞬态工厂并将默认的 DbContext 留给作用域 我观察到的技术是开发人员将 dboptions 注入控制器并将其传递给各种服务。然后,相应的服务从服务内(使用提供的选项)实例化一个新的上下文,并将其传递给服务内的各种方法。从我的角度来看,这与使 dbcontext 瞬态没有什么不同,但也许我错过了这种实现的有益细微差别。 手动注入 dboptions 并将其传递给调用服务与首先拥有 DI/IoC 系统的目的相比。您可以在瞬态工厂中抽象所有这些并注入工厂,然后在您的代码中调用它,例如using(var context = transientDbContextFactory.Create()) ...
,以解决您需要瞬态上下文并将其保留在应用程序其余部分的范围内的情况。
@Tseng 您的第一条评论不太正确,您说“默认情况下 DbContext 是瞬态的”,而实际上它是默认范围的。然后,您继续将其描述为“因此在请求期间有效”,这是对 scoped 作用的描述!所以我认为第一部分只是一个意外。只是在我阅读您的评论时将其放在那里,这让我在几分钟内完全怀疑我对范围与瞬态的了解????
【参考方案1】:
生命周期是AddDbContext<>()
上的一个参数。见例子:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")),
ServiceLifetime.Transient);
这会将它添加到具有瞬态生命周期的服务集合中。
【讨论】:
如果控制器/服务使用 Scoped 生命周期,这会起作用吗?当 Microsoft.Extensions.DependencyInjection 仅适用于构造函数注入时,您将如何请求此 DbContext?还是我错过了什么? 抱歉回复晚了。根据经验,您不应该在另一个服务中使用生命周期较短的服务。在这些情况下,您的服务可以接受瞬态服务作为方法参数。控制器应该是瞬态的,无论如何每个请求只实例化一个 :) 注意使用 dotnet core 内置容器时的副作用 - 它会跟踪所有创建的 DbContext 实例,从而导致内存泄漏:github.com/aspnet/DependencyInjection/issues/456 如果您从为请求创建的范围中获取实例,它们将在请求结束时释放范围。 正如@IvanSamygin 所写,请注意微软的 DI 容器保留对所有一次性服务的引用,以便它们可以在请求结束时被释放。但是,如果将服务托管在 Windows 服务中,则根范围的存在时间与服务存在时间一样长。因此,最安全的方法是始终对 DbContext 使用 Scoped,或者确保将它们解析为非一次性的(包装在Func<T>
中),或者始终在解析 DbContext 的任何地方创建一个新的子范围。所以第一条经验法则通常是最简单的:不要使用瞬态。【参考方案2】:
在我看来,将DbContext
注册为临时依赖项的一个很好的用例是在注册为单例的工作服务中。您不能在单例依赖项中使用范围依赖项。因此,您唯一的选择是将 DbContext 注册为单例或瞬态。需要记住的是,注入的 DbContextOptions 类生命周期也需要更新。您可以通过如下方式指定服务生命周期来实现这两者。
services.AddDbContext<DataContext>(options =>
options.Usemysql(configurationRoot.GetConnectionString("DefaultConnection"));
options.UseLazyLoadingProxies();
, ServiceLifetime.Transient, ServiceLifetime.Transient);
第三个参数是 DbContextOptions 实例的服务生命周期。
【讨论】:
【参考方案3】:您还可以创建具有 idbcontext 构造函数参数的存储库类和接口。让所有控制器构造函数在其构造函数中使用此接口。这可以通过 addtransient 添加。这样,Microsoft 仍然可以控制它认为合适的 dbcontext。上下文将由运行时管理,并在创建控制器时创建存储库实例时注入。
【讨论】:
这很模糊,没有代码示例。问题是明确不让微软仍然控制它认为合适的dbcontext。以上是关于将 Dbcontext 配置为瞬态的主要内容,如果未能解决你的问题,请参考以下文章