Hangfire with Entity Framework - DbContext并发问题

问题描述 投票:1回答:1

我有一个负责两项工作的篝火服务:

  • 使用相关附件创建电子邮件对象(耗时的过程)
  • 处理并发送Pending电子邮件。

我正在使用ASP.NET MVC(5.2.3)和StructureMap(4.5.1)来创建一个用于依赖注入的IoC容器,以及Hangfire(1.6.19)

流程是:

  • 将触发从前端创建的电子邮件,其中创建Fire和忘记挂起作业,将其推入具有Pending状态的数据库。 此过程使用一些存储库对象以PDF格式创建客户端报告(这些存储库使用DbContext从数据库中检索信息)
  • 另一个经常性的工作,拿起所有Pending电子邮件,并使用System.Net.Mail为每个电子邮件撰写电子邮件。

这两个作业都可以正常工作,除非有多个作业同时创建电子邮件。

例如:如果从前端同时触发10个作业以生成电子邮件,则作业将失败并显示错误:

在先前的异步操作完成之前,在该上下文上开始第二操作。使用'await'确保在此上下文上调用另一个方法之前已完成任何异步操作。任何实例成员都不保证是线程安全的。

抛出此错误的行是使用await,但我认为这是因为有不同的线程同时访问上下文:

var ids = await context.Objects
    .Where(x => x.id == clientId && !x.IsDeleted)
    .Select(x => x.id)
    .ToListAsync();

我已经设置了尝试不同范围的DbContext,TransientAlwaysUnique,但错误仍然存​​在。

此外,这并不总是发生在同一行,基本上是在所涉及的存储库中使用上下文的任何地方 - 如果两个作业同时在同一行中。

作业重试,电子邮件每分钟发送一次,如果要处理和发送数百封电子邮件,这可能无效(在我的方案中,这是问题)

c# asp.net-mvc entity-framework dbcontext hangfire
1个回答
0
投票

当我使用ContainerScoped LifeCycle时问题得到了解决。


StructureMap Docs

在这种情况下,ContainerScoped意味着每个Container将构建一次注册,这样根容器,任何子容器或配置文件容器以及每个嵌套容器都将构建自己的对象实例

这意味着每个从hangfire中解雇的工作都有一个新的DataContext实例(如果我理解正确,我可能会在纠正中发言)

特别是对于Datacontext,我必须启用MARS (Multiple Active Result Sets),它允许SQL Server允许在单个连接上执行多个批处理。

© www.soinside.com 2019 - 2024. All rights reserved.