Асинхронные проблемы с фоновыми задачами в цикле

Я столкнулся с проблемой, как инициализировать фоновые задачи в цикле в asp.net MVC.

Мой контекст связан с соревнованиями. Соревнование может начаться, как только будет достигнуто количество участников, или после задержки. Пользователь выбирает, к скольким соревнованиям он хочет присоединиться (vm.InscriptionsNumber), а затем вызывает следующий метод Compete.

Если нет существующего соревнования, к которому он может присоединиться, я должен создать его и инициализировать таймер этого нового соревнования, TryStartCompetition. При вызове этот метод проверяет, началось ли уже соревнование (потому что число участников было достигнуто), и если нет, вызывает метод StartCompetition.

[HttpPost]
public async Task<ActionResult> Compete(CompetitionsViewModel vm)
{
    for (int i = 0; i < vm.InscriptionsNumber; i++)
    {
        Competition competition = UnitOfWork.CompetitionRepo.Get(...);

        if (competition == null) // If there is no existing competition the user can join
        {
            // Create a new one
            competition = new Competition(...);
            await UnitOfWork.CompetitionRepo.AddOrUpdate(competition);

            // Initialize the start max delay
            HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
            {
                await Task.Delay(Competition.Delay);
                await TryStartCompetition(competition);
            });
         }
        else if (competitorsNumberConditionOk)
        {
            await StartCompetition(competition);
        }

        // Other stuff...
    }

    await UnitOfWork.Save();

    return PartialView("~/Views/Competitions/Index.cshtml", ViewModel);
}

private async Task TryStartCompetition(Competition compet)
{
    // Competition has not started yet
    if (!compet.StartDate.HasValue)
    {
        await StartCompetition(compet);
        await UnitOfWork.Save();
    }
}

private async Task StartCompetition(Competition compet)
{
    // Competition ranking calculation and other stuff here needing async calls
    compet.StartDate = DateTime.Now;
}

При первом вызове await в StartCompetition возникает следующее исключение. Я не знаю, как мне поступить в данном конкретном случае.

Вторая операция началась в этом контексте до завершения предыдущей асинхронной операции. Используйте «ожидание», чтобы убедиться, что все асинхронные операции завершены, прежде чем вызывать другой метод в этом контексте. Любые члены экземпляра не гарантируют потокобезопасность.

Если vm.InscriptionsNumber равно 1, то все в порядке, но как только возникает петля, появляется ошибка.

Если кто-нибудь может помочь мне понять, почему это происходит, я был бы очень благодарен.


person Flash_Back    schedule 22.04.2016    source источник
comment
stackoverflow.com/questions/34773533/   -  person d.moncada    schedule 22.04.2016
comment
Привет, сообщение, на которое вы ссылаетесь, похоже, посвящено ленивой загрузке, однако метод await, который вызывает исключение, является простым Add, я где-то выполняю ленивую загрузку?   -  person Flash_Back    schedule 22.04.2016
comment
вы могли бы ... но в целом это похоже на несколько вызовов EF. контекст EF удаляется после каждой итерации?   -  person d.moncada    schedule 22.04.2016
comment
Если это то, что вы имеете в виду, я не думаю, что контекст расположен между первой итерацией цикла for и второй. UnitOfWork инициализируется один раз в конструкторе контроллера. Должен ли я тогда удалить его и воссоздать в конце цикла for?   -  person Flash_Back    schedule 22.04.2016
comment
Да, попробуйте. Я думаю, что проблема в том, что несколько задач обращаются к одному и тому же контексту.   -  person d.moncada    schedule 22.04.2016
comment
Вы используете QueueBackgroundWorkItem для вызова UnitOfWork.Save в фоновом потоке, а также вызываете UnitOfWork.Save в потоке запроса контроллера. Контексты EF не могут использоваться одновременно несколькими потоками.   -  person Stephen Cleary    schedule 22.04.2016
comment
На самом деле это правда, но я не знаю, как я мог бы поступить иначе, поскольку мне приходится сохранять в обоих случаях. В настоящее время я пытаюсь следовать совету d.moncada, но я не совсем уверен, где мне следует размещать контекст или выполнять using, поэтому у меня много проблем с объектами, присоединенными дважды и т. д.   -  person Flash_Back    schedule 22.04.2016