Транзакция не может обрабатывать параллельные команды через Task.WhenAll - PullRequest
0 голосов
/ 02 июня 2018

У меня есть некоторая основная таблица (например, Companies) и множество зависимых таблиц (например, CompanyAddresses, CompanyPaymentInfos и т. Д.) В моей базе данных Postgres:

CREATE TABLE Companies (
Id uuid NOT NULL PRIMARY KEY,
...);

CREATE TABLE CompanyAddresses(
CompanyId uuid NOT NULL PRIMARY KEY REFERENCES Companies(Id),
...);

CREATE TABLE CompanyPaymentInfos(
CompanyId uuid NOT NULL PRIMARY KEY REFERENCES Companies(Id),
...);

Я использую транзакции из стандартной библиотеки в моем C #code:

private TransactionScope GeTransactionScope()
{
    return new TransactionScope(
        TransactionScopeOption.RequiresNew,
        new TransactionOptions
        {
            IsolationLevel = IsolationLevel.ReadCommitted
        },
        TransactionScopeAsyncFlowOption.Enabled);
}

private async Task DoChange(...)
{
    using (var scope = GeTransactionScope())
    {
        await Insert(Company);

        await Task.WhenAll(
            Insert(CompanyPaymentInfo),
            Insert(CompanyAddress),
            Insert(CompanyTags),
            // so on
        );

        scope.Complete();
    }
}

Каждая команда Insert производит только выполнение кода SQL без каких-либо внутренних транзакций.

И после выполнения DoChange я получаю эту ошибку:

Npgsql.PostgresException (0x80004005): 23503: вставка или обновление таблицы "companyaddresses" нарушает ограничение внешнего ключа "companyaddresses_companyid_fkey"

И, конечно, у меня многовопросы типа:

  • Почему я получаю ошибку?
  • Почему я получаю ошибку во время вставки CompanyAddress, а не CompanyPaymentInfo?

Если я переключаю DoChange на последовательное выполнение, все работает нормально:

private void DoChange()
{
    using (var scope = GeTransactionScope())
    {
        await Insert(Company);
        await Insert(CompanyPaymentInfo);
        await Insert(CompanyAddress);
        await Insert(CompanyTags);
        // ...

        scope.Complete();
    }
}

Может быть, это поможет:

  • Я использую Net Core 2.0
  • Iиспользовать Postgres 10.4 со стандартным наборомtings (ReadCommitted в качестве уровня изоляции и т. д.).Кроме того, я добавил enlist=true в строку подключения, чтобы транзакции работали.
  • Я использую Npgsql 3.2.5 и Dapper 1.50.2 внутри моей команды Insert

1 Ответ

0 голосов
/ 03 июня 2018

Здесь нет ничего волшебного, вы получаете ошибку, потому что соединение, которое вы используете при вставке CompanyAddress, не является тем, о котором вы думаете.

Это новое соединение.Когда выполняется вставка ComapnyPaymentInfo, вы используете соединение, уже связанное с вашей транзакцией.Он ожидает новых команд, потому что вы ожидали на предыдущем шаге.

Использование Task.WhenAll () с другой стороны попытается использовать несколько потоков.Если соединение занято выполнением команды, оно не будет использоваться, а новое будет порождено.

Помните, что при использовании транзакций у вас доступно только одно соединение, вы не сможете воспользоваться параллелизмом.

...