tl; dr 即使DbContext不是线程安全的,如何在多线程.NET Core API应用程序中使用实体框架?
我正在开发一个.NET Core API应用程序,它公开了几个RESTful接口来访问数据库并从中读取数据,同时运行多个TimedHostedServices作为后台工作线程,这些线程定期从其他Web服务中轮询数据并将其存储到数据库。
我知道DbContext不是线程安全的事实。我在Stackoverflow上阅读了许多文档,博客文章和答案,与此相关的很多(部分自相矛盾)答案,但在与DI一起工作时却没有真正的“最佳实践”。
通过ServiceLifetime.Scoped
扩展方法使用默认的AddDbContext
会由于竞争条件而导致异常。
我不想使用锁(例如信号灯),因为明显的缺点是:
不是注入MyDbContext
,而是注入DbContextOptions<MyDbContext>
,仅在需要访问数据库时才构建上下文,使用using
语句在读/写后立即将其处置大量的资源使用开销,不必要的连接打开/关闭。
我真的很困惑:如何实现?
我认为我的用例没有什么特别之处-从后台工作人员填充数据库并从Web API层查询它-因此应该有一种使用ef core做到这一点的有意义的方法。
非常感谢!
答案 0 :(得分:2)
每当TimedHostedServices
触发时,您都应该创建一个范围。
在构造函数中注入服务提供者:
public MyServiceService(IServiceProvider services)
{
_services = services;
}
然后在任务触发时创建作用域
using (var scope = _services.CreateScope())
{
var anotherService = scope.ServiceProvider.GetRequiredService<AnotherService>();
anotherService.Something();
}
有更完整的示例in the doc
答案 1 :(得分:0)
另一种创建自己的DbContextFactory并为每个查询实例化新实例的方法。
public class DbContextFactory
{
public YourDbContext Create()
{
var options = new DbContextOptionsBuilder<YourDbContext>()
.UseSqlServer(_connectionString)
.Options;
return new YourDbContext(options);
}
}
用法
public class Service
{
private readonly DbContextFactory _dbContextFactory;
public Service(DbContextFactory dbContextFactory)
=> _dbContextFactory = dbContextFactory;
public void Execute()
{
using (var context = _dbContextFactory.Create())
{
// use context
}
}
}
有了工厂,您就不再需要担心范围,并且使您的代码摆脱了ASP.NET Core的依赖。
您将能够异步执行查询,如果没有变通办法,这对于具有范围的DbContext是不可能的。
您始终对调用.SaveChanges()
时保存的数据充满信心,在使用范围限定的DbContext的情况下,某些实体在其他类中发生了更改的可能性。