最近在控制台程序中使用EFCore,并使用了依赖注入的方式注册调用,但测试时发现不同客户端连接对应了同一个DBContext,导致并发出现问题。踩得坑记录一下。
在ASP.NET Core应用程序中使用EF Core的基本模式通常包括将自定义DbContext类型注册到依赖项注入系统中,然后通过控制器中的构造函数参数获取该类型的实例。这意味着将为每个请求创建一个新的DbContext实例。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MCContext >(
options => options.UseSqlServer(connectionString));
}
public class TiketsController : ControllerBase
{
private readonly MCContext _context;
public TiketsController (MCContext context)
{
_context = context;
}
}
但是 AddDbContext方法对应的服务生命周期是 Scoped,这样在控制台程序中使用会有一个问题,如果控制台提供一个服务,客户端不同的连接对应的都是一个Context,这样存在并发的问题,如果有个语句出错了,后面操作都将报错。
有AddSingleton、AddScoped、AddTransient 三种方式注册服务,对应的生命周期如下:
1、Transient:每次从容器 (IServiceProvider)中获取的时候都是一个新的实例
2、Singleton:每次从同根容器中(同根 IServiceProvider)获取的时候都是同一个实例
3、Scoped:每次从同一个容器中获取的实例是相同的、
所以在控制台中要用AddTransient的方法注册DbContext:
var Services = new ServiceCollection().AddTransient<DbContext>((c) =>
{
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr))
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
//如果有其他依赖的话,可以通过provider.GetService<XX>()来获取
return new DbContext(optionsBuilder.Options);
}).BuildServiceProvider();
如果用AddSingleton方法写法:
var Services = new ServiceCollection().AddSingleton<Func<DbContext>>(() =>
{
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr))
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
//如果有其他依赖的话,可以通过provider.GetService<XX>()来获取
return new DbContext(optionsBuilder.Options);
}).BuildServiceProvider();
参考:
解析 .Net Core 注入——注册服务 | 服务 (lmlphp.com)
(14条消息) EF Core之DBContext生命周期_2Ker的博客-CSDN博客_adddbcontext 生命周期
在ASP.Net Core中每个请求一次创建EF Core上下文 | (1r1g.com)