Среда сущности области транзакции parallel.invoke

В моей программе EF4 у меня есть таблица Applicant и Application. Несколько экземпляров программы периодически запускаются для создания приложений для заявителей на основе некоторой бизнес-логики. В таблице приложений у меня не может быть более одной записи Submitted/BeingSubmitted для заявителя.

Итак, вот фрагмент кода, который проверяет наличие приложения Submitted/BeingSubmitted и вставляет его. Он запускается внутри цикла foreach для списка кандидатов.

public Application SaveApplication(Int32 applicantId)
    {
        using (TransactionScope txScope = new TransactionScope(TransactionScopeOption.RequiresNew))
        {
            if (ApplicantHasPendingApplication(applicantId))
                return null;

            Application app = null;
            try
            {
                app = new Application()
                {
                    // Create the object...
                };

                _unitOfWork.DisclosureApplications.Add(app);
                _unitOfWork.Commit();
                _unitOfWork.Refresh(app); // We save and refresh it to get the Id.

                txScope.Complete();
            }
            catch (UpdateException ex)
            {
                // We get an Update exception here when multiple instances tries to insert Application.
            }

            return app;
        }
    }

Приведенный выше фрагмент кода предотвращает вставку повторяющихся записей, за исключением того факта, что он создает исключение UpdateException при запуске нескольких экземпляров программы. Если я проглочу это исключение и продолжу, то все в порядке.

Тем не менее, я попытался протестировать/запустить приведенный выше код параллельно, но он вставляет повторяющиеся записи в базу данных.

Parallel.Invoke(
            () => CreateApplications("Parallel Instance 1"),
            () => CreateApplications("Parallel Instance 2"));

private void CreateApplications(String dummyInstanceName)
{
   var unitOfWork = new SqlUnitOfWork();
   var applicants = unitOfWork.Applicants.FindAll().Take(100).ToList();

   var facade = new ProviderFacade(unitOfWork, new Log4NetLogger(dummyInstanceName));

   foreach (Applicant applicant in applicants)
            {
                facade.ApplicationProvider.SaveApplication(applicant.applicantID);
            }
}

В приведенном выше фрагменте кода он выдает UpdateException и вставляет несколько строк приложения для заявителя.

Обратите внимание, что таблица имеет только суррогатный первичный ключ и никаких других уникальных ограничений.

Мой вопрос: почему TransactionScope вставляет повторяющиеся строки, запуская его в Parallel.Invoke, но не когда я запускаю несколько экземпляров программы? Каким будет разумный подход к ее достижению?

Обновление: ctor SqlUnitOfWork

public SqlUnitOfWork()
    {
        _context = new MyEntities();
    }

Ctor MyEntities генерируется EF -

    public const string ConnectionString = "name=Entities";
    public const string ContainerName = "Entities";

    public TPIEntities() : base(ConnectionString, ContainerName)
    {
        this.ContextOptions.LazyLoadingEnabled = true;
    }

Спасибо.


person user1520015    schedule 12.07.2012    source источник
comment
Когда вы вызываете фасад.ApplicationProvider.SaveApplication, какой _unitOfWork используется? Я не вижу, как туда передается SqlUnitOfWork, созданный в CreateApplications. Если ваши два экземпляра CreateApplications эффективно используют один и тот же SqlUnitOfWork, это может быть связано с вашей проблемой.   -  person Jonathan Rupp    schedule 12.07.2012
comment
Пожалуйста, смотрите обновление. Также добавил строку, где я создаю фасад. Когда я создаю новый SqlUnitOfWork внутри CreateApplications, какова вероятность того, что один и тот же UnitOfWork будет использоваться при вызовах параллельных методов? (Я новичок в параллельном программировании.)   -  person user1520015    schedule 12.07.2012


Ответы (1)


Это классическое состояние гонки. Обе транзакции проверяют, существует ли уже существующий Application. Оба обнаруживают, что его нет. Затем оба пытаются вставить.

Вам необходимо синхронизировать проверку, либо заблокировав приложение, либо используя SQL Server UPDLOCK, HOLDLOCK при проверке существования Application.

person usr    schedule 12.07.2012
comment
Я использую здесь EF, так как мне указать Sql Server выполнять UPDLOCK и HOLDLOCK? - person user1520015; 12.07.2012
comment
Вам необходимо использовать ExecuteStoreCommand для отправки (простого) оператора SQL (msdn.microsoft.com/en-us/library/), - person usr; 12.07.2012
comment
Спасибо, это то, что я искал. - person user1520015; 12.07.2012