Параллелизм в EF4 — как условно создать сущность

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

Раньше я всегда справлялся с этим, выполняя простой if (!UserSet.Any(...)) перед моим AddToUserSet(...). Однако это не параллельное решение и сломается при большой нагрузке.

Я изучал транзакции, но, насколько мне известно, мне также нужно было бы установить UPDLOCK для SELECT, но EF4 не поддерживает это.

Как все остальные справляются с этим?


person Lawrence Wagerfield    schedule 27.04.2011    source источник
comment
Единственное разумное решение — ввести уникальный индекс или ограничение на поле электронной почты и сбой в приложении.   -  person Sam Saffron    schedule 27.04.2011


Ответы (2)


Вы можете принудительно заблокировать, включив SELECT в транзакцию:

using (var scope = new TransactionScope())
{
    // Create context
    // Check non existing email
    // Insert user
    // Save changes
}

Это будет использовать сериализуемую транзакцию, которая вам нужна, если вы хотите параллельное решение для вставок - UPDLOCK недостаточно, чтобы гарантировать, что новая запись не будет добавлена ​​​​во время вашей транзакции.

Это может быть довольно серьезным узким местом, поэтому я согласен с @paolo: просто поместите уникальное ограничение в базу данных и перехватите исключение во время вставки, если электронное письмо не уникально.

Сериализуемая транзакция из книг онлайн:

Указывает следующее:

    Statements cannot read data that has been modified but not yet  
    committed by other transactions.

    No other transactions can modify data that has been read by the  
    current transaction until the current transaction completes.

    Other transactions cannot insert new rows with key values that
    would fall in the range of keys read by any statements in the current
    transaction until the current transaction completes.

Блокировки диапазона размещаются в диапазоне значений ключа, соответствующих условиям поиска каждого оператора, выполняемого в транзакции. Это блокирует другие транзакции от обновления или вставки любых строк, которые подходят для любого из операторов, выполняемых текущей транзакцией. Это означает, что если какие-либо операторы в транзакции выполняются во второй раз, они будут считывать один и тот же набор строк. Блокировки диапазона удерживаются до завершения транзакции. Это самый ограничивающий уровень изоляции, поскольку он блокирует целые диапазоны ключей и удерживает блокировки до завершения транзакции. Поскольку параллелизм ниже, используйте этот параметр только при необходимости. Этот параметр имеет тот же эффект, что и установка HOLDLOCK для всех таблиц во всех операторах SELECT в транзакции.

person Ladislav Mrnka    schedule 27.04.2011
comment
Я почти уверен, что это не сработает. 2 одновременных пользователя могут выполнить «Проверить несуществующую электронную почту» и оба прочитать «Электронная почта доступна». Затем они попытаются добавить запись одновременно. Это связано с тем, что SELECT не использует UPDLOCK. - person Lawrence Wagerfield; 27.04.2011
comment
SELECT является частью транзакции, поэтому он использует блокировку, соответствующую уровню изоляции транзакции. - person Ladislav Mrnka; 27.04.2011
comment
Хорошо, я попробовал это, и, похоже, это работает. Однако я предполагаю, что EF выдаст исключение при столкновении. Как мне справиться с этим? Продолжать повторять запрос? Я получаю сообщение 1205 при тестировании запросов в Management Studio. - person Lawrence Wagerfield; 28.04.2011
comment
Какой тип столкновения вы имеете в виду? - person Ladislav Mrnka; 28.04.2011
comment
Management Studio выдает сообщение "Msg 1205" для одной транзакции, если две транзакции выполняются одновременно. Я предполагаю, что EF тоже выдаст аналогичную ошибку? - person Lawrence Wagerfield; 28.04.2011

в дополнение к проверке вы можете добавить уникальное ограничение в адрес электронной почты поле прямо в БД

person Paolo Falabella    schedule 27.04.2011