tom*_*dox 11 c# entity-framework dependency-injection ef-core-3.1 ef-core-5.0
我正在尝试使用EF Core 文档的 DbContext 配置部分中DbContextFactory
讨论的新模式。
我已经DbContextFactory
在我的 Blazor 应用程序中成功启动并运行,但我想保留DbContext
直接注入实例的选项,以保持我现有的代码正常工作。
但是,当我尝试这样做时,出现以下错误:
System.AggregateException:无法构造某些服务(验证服务描述符“ServiceType: Microsoft.EntityFrameworkCore.IDbContextFactory
1[MyContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory
1[MyContext]”时出错:无法使用范围服务“Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory
1[MyContext]”。) ---> System.InvalidOperationException:验证服务描述符“ServiceType:Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory
1[MyContext]”时出错:无法使用范围服务“Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory
1[MyContext]”。---> System.InvalidOperationException:无法使用范围服务“Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory
1[MyContext]”。
在试验时,我还设法在某一时刻得到了这个错误:
无法从根提供程序解析范围服务“Microsoft.EntityFrameworkCore.DbContextOptions`1[MyContext]”。
它是理论上可以同时使用AddDbContext
和AddDbContextFactory
在一起?
tom*_*dox 14
是的,这完全是关于了解游戏中各种元素的生命周期并正确设置它们。
默认情况下,DbContextFactory
由AddDbContextFactory()
扩展方法创建的具有单例生命周期。如果您使用AddDbContext()
带有默认设置的扩展方法,它将创建一个DbContextOptions
具有Scoped
生命周期的对象(请参阅此处的源代码),并且由于Singleton
不能使用Scoped
生命周期较短的东西,因此会引发错误。
为了解决这个问题,我们需要将 的生命周期DbContextOptions
也改为“单例”。这可以通过显式设置DbContextOptions
参数的范围来完成AddDbContext()
services.AddDbContext<FusionContext>(options =>
options.UseSqlServer(YourSqlConnection),
contextLifetime: ServiceLifetime.Transient,
optionsLifetime: ServiceLifetime.Singleton);
Run Code Online (Sandbox Code Playgroud)
从这里开始的 EF 核心 GitHub 存储库上对此进行了非常好的讨论。也值得一看DbContextFactory
这里的源代码。
或者,您还可以DbContextFactory
通过在构造函数中设置 ServiceLifetime 参数来更改 的生命周期:
services.AddDbContextFactory<FusionContext>(options =>
options.UseSqlServer(YourSqlConnection),
ServiceLifetime.Scoped);
Run Code Online (Sandbox Code Playgroud)
这些选项的配置应与普通 DbContext完全一样,因为这些选项将在工厂创建的 DbContext 上设置。
Sim*_*ver 14
很重要的一点:
两者AddDbContextFactory
都使用AddDbContext
内部注册DbContextOptions<T>
共享私有方法。(来源)AddCoreServices
TryAdd
这实际上意味着代码中第一个出现的那个就是被使用的那个。
所以你实际上可以这样做以获得更清晰的设置:
services.AddDbContext<RRStoreContext>(options => {
// apply options
});
services.AddDbContextFactory<RRStoreContext>(lifetime: ServiceLifetime.Scoped);
Run Code Online (Sandbox Code Playgroud)
我用以下内容向自己证明它确实具有这样的功能:
services.AddDbContextFactory<RRStoreContext>(options =>
{
throw new Exception("Oops!"); // this should never be reached
}, ServiceLifetime.Scoped);
Run Code Online (Sandbox Code Playgroud)
不幸的是,我有一些不是线程安全的查询拦截器(这就是我想用工厂创建多个实例的全部原因),所以我想我需要创建自己的上下文工厂,因为我对 Context 和 Context 有单独的初始化.ContextFactory。
编辑:我最终创建了自己的上下文工厂,以便能够为创建的每个新上下文创建新选项。唯一的原因是允许非线程安全拦截器,但如果您需要它或类似的东西,那么这应该可以工作。
public class SmartRRStoreContextFactory : IDbContextFactory<RRStoreContext>
{
private readonly IServiceProvider _serviceProvider;
public SmartRRStoreContextFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public virtual RRStoreContext CreateDbContext()
{
// need a new options object for each 'factory generated' context
// because of thread safety isuess with Interceptors
var options = (DbContextOptions<RRStoreContext>) _serviceProvider.GetService(typeof(DbContextOptions<RRStoreContext>));
return new RRStoreContext(options);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:我只有一个上下文需要它,因此我在我的CreateDbContext
方法中对新上下文进行硬编码。另一种方法是使用反射 - 像这样的DbContextFactorySource。
然后在我的 Startup.cs 中我有:
services.AddDbContext<RRStoreContext>(options =>
{
var connection = CONNECTION_STRING;
options.UseSqlServer(connection, sqlOptions =>
{
sqlOptions.EnableRetryOnFailure();
});
// this is not thread safe
options.AddInterceptors(new RRSaveChangesInterceptor());
}, optionsLifetime: ServiceLifetime.Transient);
// add context factory, this uses the same options builder that was just defined
// but with a custom factory to force new options every time
services.AddDbContextFactory<RRStoreContext, SmartRRStoreContextFactory>();
Run Code Online (Sandbox Code Playgroud)
我将以警告结束。CreateDbContext
如果除了“正常”注入的 DbContext 之外还使用工厂 ( ),请特别确保不要混合实体。例如,如果您在错误的上下文中调用 SaveChanges,那么您的实体将不会被保存。
从 EF Core 6 (.NET 6) 开始,在大多数情况下您不需要同时使用两者,因为AddDbContextFactory
还将上下文类型本身注册为作用域服务。
Singleton
因此,在具有生命周期注入的服务中IDbContextFactory<MyDbContext>
,以及在具有生命周期的服务Scoped
(例如MVC或API控制器)中MyDbContext
直接注入。
DbContextOptions
如果您想在每种情况下使用不同的或者如果Singleton
生命周期DbContextOptions
不符合您的需求,您必须采用其他答案提供的解决方案。
另请参阅:
相关 Github 增强问题
归档时间: |
|
查看次数: |
3425 次 |
最近记录: |