如何在DBContext上同时执行多个操作?
我有以下背景服务运行:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Run(async () =>
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
DataContext dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
var tasks = new List<Task>();
tasks.Add(siteA.ProcessSchedule(SportLeagues.MLB, logger, dbContext));
tasks.Add(siteB.ProcessSchedule(SportLeagues.MLB, logger, dbContext));
//....more of same task
await Task.WhenAll(tasks);
await dbContext.SaveChangesAsync();
await Task.Delay(0, stoppingToken);
}
}
});
}
我不断获得此错误
System.InvalidOperationException:
A second operation was started on this context instance before a previous operation completed.
,我可以为每个任务进行重构并创建一个dbContext
,但是如果可能的话,我更喜欢使用单个dbcontext。
我还尝试将我的dbcontext添加为瞬态:
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseInMemoryDatabase("db");
}, ServiceLifetime.Transient);
但这无济于事。我是否以不正确的方式创建了我的dbcontext?还是为每个任务创建新的dbContext
的最佳方法?对于更多上下文,这是一个不断运行的背景任务,需要一秒钟才能完成,并且将拥有大约20个任务。每秒创建和破坏20 dbContext
,以及必须执行20 dbContext
Saves,听起来很低。
I have the following background service running:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Run(async () =>
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
DataContext dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
var tasks = new List<Task>();
tasks.Add(siteA.ProcessSchedule(SportLeagues.MLB, logger, dbContext));
tasks.Add(siteB.ProcessSchedule(SportLeagues.MLB, logger, dbContext));
//....more of same task
await Task.WhenAll(tasks);
await dbContext.SaveChangesAsync();
await Task.Delay(0, stoppingToken);
}
}
});
}
I constantly get this error
System.InvalidOperationException:
A second operation was started on this context instance before a previous operation completed.
I can refactor and create a dbContext
for each task, however I would prefer to use a single dbContext if possible.
I also tried adding my dbContext as transient:
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseInMemoryDatabase("db");
}, ServiceLifetime.Transient);
But this did not help. Am I creating my dbContext in an incorrect way? Or is the best approach to create a new dbContext
for each task? For more context, this is a constantly running background task, it takes a second to complete and it will have around 20 tasks. It sounds pretty inefficient to be creating and destroying 20 dbContext
every second, along with having to do 20 dbContext
saves.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的设计有一些问题。一方面,
dbContext
不是线程安全,因此不能安全地从多个任务或线程中使用。dbContext
也在跟踪对象(除非asnotracking()
,并且在所有查询操作中使用类似的方法)。如果您在很长一段时间内一直使用相同的dbContext
实例,那么它将需要大量内存,并且其性能也可能会降低。另一方面,实例化新的
dbContext
应该是一个便宜的操作,除非它通过包含太多表格会肿,但这将是另一种设计缺陷。所有这些考虑都指向实例化新的
dbContext
的方向,而不是重复使用实例。如果后续操作需要传达状态,则重复使用dbContext
是该目的不太有利的解决方案之一。There are a couple of issues with your design. For one thing,
DbContext
is not thread-safe, and hence cannot be used from multiple tasks or threads safely.DbContext
is also tracking objects (unlessAsNoTracking()
and similar methods are used on all query operations). If you keep using the sameDbContext
instance for a long period of time, then it will take a lot of memory, and its performance might reduce as well.On the other hand, instantiating a new
DbContext
should be a cheap operation, unless it is bloated by containing too many tables - that would be a design flaw of a different kind.All these considerations point into direction of instantiating a new
DbContext
in every operation that needs it, rather than reusing instances. If subsequent operations need to communicate state, then reusing aDbContext
is one of the less favorable solutions to that end.激活连接字符串中的火星应解决您的问题。
类似:“数据源= MSSQL1;初始目录= AdventureWorks; Integrated Security = Sspi; 多重反应viversultsetsetsetsetsetsetsets = true “
读取此Microsoft文档以获取更多信息:
https://lealen.microsoft.com/en-en-us/dotnet/framework /DATA/ADONET/SQL/启用 - 激活-Result-Sets
Activating MARS in your Connection String should solve your problem.
like: "Data Source=MSSQL1;Initial Catalog=AdventureWorks;Integrated Security=SSPI;MultipleActiveResultSets=True"
Read This Microsoft Document For More Info:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets