简体   繁体   English

带有实体框架的Hangfire-DbContext并发问题

[英]Hangfire with Entity Framework - DbContext concurrency issues

I have a hangfire service responsible for two jobs: 我有一个空中篝火服务处,负责两项工作:

  • Creating email objects with their relevant attachments (Time-consuming process) 创建带有相关附件的电子邮件对象(耗时的过程)
  • Processing and sending out Pending emails. 处理和发送Pending电子邮件。

I am using ASP.NET MVC (5.2.3) , with StructureMap (4.5.1) for creating an IoC container for Dependency Injection, together with Hangfire (1.6.19) 我正在使用ASP.NET MVC(5.2.3)StructureMap(4.5.1)来创建用于依赖项注入的IoC容器以及Hangfire(1.6.19)

The flow is: 流程为:

  • The email will be triggered to be created from the front-end, where a Fire and Forget hangfire job is created, pushing it into the database with a Pending status. 该电子邮件将被触发从前端创建,在该前端创建一个“即发即弃” hangfire作业,并将其以“ Pending状态推送到数据库中。
    • This process uses a few repository objects to create a report of the client in a PDF format (These repositories uses the DbContext to retrieve information from the database) 此过程使用一些存储库对象以PDF格式创建客户端的报告(这些存储库使用DbContext从数据库中检索信息)
  • Another recurring job, picks up all the Pending emails and composes an email for each of these using System.Net.Mail . 另一个重复性工作是,使用System.Net.Mail拾取所有Pending电子邮件,并为每个电子邮件撰写一封电子邮件。

Both of these jobs work fine, except if there are multiple jobs creating emails at the same time. 这两个工作都可以正常工作,除非有多个工作同时创建电子邮件。

For instance: If there are 10 jobs fired at the same time from the front-end to generate the emails, the job fails with the error: 例如:如果前端同时触发了10个作业以生成电子邮件,则该作业将失败并显示以下错误:

A second operation started on this context before a previous asynchronous operation completed. 在先前的异步操作完成之前,第二操作在此上下文上开始。 Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. 使用“ await”来确保在此上下文上调用另一个方法之前,所有异步操作都已完成。 Any instance members are not guaranteed to be thread safe. 不保证任何实例成员都是线程安全的。

The line that is throwing this error is using await , but I am thinking that it is because there are different threads accessing the context at the same time: 引发此错误的行正在使用await ,但我认为这是因为有不同的线程同时访问上下文:

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

I have set up the DbContext trying different scopes, Transient and AlwaysUnique , but the error persists. 我设置了DbContext尝试使用不同的范围TransientAlwaysUnique ,但是错误仍然存​​在。

Also, this doesn't always happen at the same line, basically anywhere where the context is being used in the involved repositories - if both jobs hit the same line at the same time. 而且,这并不总是在同一行上发生,基本上不会发生在所涉及的存储库中使用上下文的任何地方-如果两个作业同时在同一行上发生。

The jobs retry, and the emails are being sent out pretty much one per minute, which might not work if there are hundreds of emails to be processed and sent out (which is the problem, in my scenario) 重试作业,每分钟发送一封电子邮件,如果有数百封电子邮件要处理和发送出去,则这可能不起作用(在我的方案中,这是问题所在)

The issue was resolved when I used the ContainerScoped LifeCycle. 当我使用ContainerScoped LifeCycle时,此问题已解决。


StructureMap Docs : StructureMap文档

ContainerScoped in this case means that a registration will be built once per Container, such that the root container, any child or profile container, and every single nested container will build its own object instance 在这种情况下,ContainerScoped意味着将为每个Container构建一次注册,这样根容器,任何子容器或配置文件容器以及每个嵌套容器都将构建自己的对象实例

This means that there is a new instance of the DataContext for every job fired from hangfire (If I understand it correctly, I might speak under correction) 这意味着从hangfire触发的每个作业都有一个DataContext的新实例(如果我正确理解的话,我可能会更正地说)

Specifically for the Datacontext, I had to enable MARS (Multiple Active Result Sets) which enables SQL Server to allow the execution of multiple batches on a single connection. 特别是对于Datacontext,我必须启用MARS (Multiple Active Result Sets) ,这使SQL Server允许在单个连接上执行多个批处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM