Hangfire с Entity Framework - проблемы параллелизма DbContext - PullRequest
1 голос
/ 11 марта 2019

У меня есть служба Hangfire, отвечающая за две работы:

  • Создание объектов электронной почты с соответствующими вложениями (трудоемкий процесс)
  • Обработка и отправка Pending электронных писем.

Я использую ASP.NET MVC (5.2.3) , с StructureMap (4.5.1) для создания контейнера IoC для внедрения зависимости,вместе с Hangfire (1.6.19)

Поток:

  • Электронное письмо будет инициировано для создания из внешнего интерфейса, где Fireи задание Forget hangfire создано, помещено в базу данных со статусом 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, пробуя разные области, Transient и AlwaysUnique, но ошибка не исчезла.

Кроме того, это не всегда происходит в одной и той же строке, в основном везде, где используется контекст в задействованных репозиториях - если оба задания одновременно попадают в одну и ту же строку.

Задания повторяются, и электронные письма отправляются почти по одной в минуту, что может не сработать, если сотни сообщений будут обработаны и отправлены (что является проблемой в моем сценарии)

1 Ответ

0 голосов
/ 12 марта 2019

Проблема была решена, когда я использовал ContainerScoped LifeCycle.


Документы StructureMap :

В этом случае ContainerScoped означает, что регистрация будет создаваться один раз для каждого контейнера, так что корневой контейнер, любой дочерний элементили контейнер профиля, и каждый отдельный вложенный контейнер будет создавать свой собственный экземпляр объекта

Это означает, что для каждого задания, запускаемого из зависания огня, существует новый экземпляр DataContext (Если я правильно понимаю,Я могу говорить с исправлением)

Специально для Datacontext мне пришлось включить MARS (Multiple Active Result Sets), который позволяет SQL Server разрешать выполнение нескольких пакетов на одном соединении.

...