У меня есть метод, который ищет объект в базе данных и пытается создать его, если он не существует. Например:
public async Country FindOrCreate(string name)
{
var country = _context.Countries.FirstOrDefault(p => p.Name == name);
if (country != null)
return country;
country = new Country
{
Name = name
};
_context.Countries.Add(country);
_context.SaveChanges();
return country;
}
Проблема в том, что в фоновом режиме несколько задач одновременно вызывают FindOrCreate. Я предотвратил вставку дубликатов, создав уникальный индекс, но происходит следующее, вызывая нежелательные исключения:
Каким будет подходящий способ обработки этих сценариев параллелизма? Должен ли я работать с блокировками, используя код C#? Или я должен установить транзакцию? ТИА
Страна — это ваша совокупность. Как утверждает DDD, вам не следует беспокоиться о сохранении инвариантов между агрегатами. В конечном итоге они должны быть согласованы. В противном случае вы получите не масштабируемую монолитную систему, полную взаимоблокировок. Другой вариант — создать еще один агрегат, который сгруппирует страны, т. е. Европу, Азию, и внедрить в него inforce-инварианты. Там вы можете легко применить пессимистичный или оптимистичный параллелизм. stackoverflow.com/questions/47180508/…





Правильным подходом здесь является обработка параллелизма на уровне БД с использованием уникального индекса. Уникальный индекс гарантирует уникальность стран несмотря ни на что.
Не беспокойтесь о блокировке кода C#, он не будет работать, если у вас есть более 1 сервера, на котором запущено ваше приложение (в наши дни это вероятно). Транзакции в этом случае сложное дело, так что я бы не стал заморачиваться.
Как обрабатывать эти исключения:
Если ваша задача не удалась при создании страны, перехватите исключение и повторите попытку. Если по какой-то причине вам не удалось получить страну во второй раз, зарегистрируйте исключение и завершите работу.
Индекс гарантирует, что плохие данные не будут сохранены. Однако эта конкретная проблема не имеет ничего общего с транзакциями. ORM не предназначены для таких запросов. INSERT WHERE NOT EXISTS решит проблему, не требуя никаких транзакций и без каких-либо проблем с параллелизмом.
может быть 2 одновременных запроса INSERT WHERE NOT EXISTS, где часть NOT EXISTS будет успешной, а один INSERT завершится ошибкой
Нет, не будет. Не вставляется, но это не значит, что не получится
Как так? Я бы подумал, что это приведет к нарушению уникального индекса?
Почему? Предложение WHERE предотвратило бы любые вставки пока не не было соответствующей строки
Давайте продолжить обсуждение в чате.
Привет, @AlexBuyny. Спасибо за ответ. Ваша идея как бы «сработала», но теперь мой код ведет себя очень странно. Я супер потерянный банкомат и расследование. По какой-то причине он генерирует исключение дублирующего индекса в верхнем методе после того, как правильный объект был найден и возвращен.
Вероятно, вам следует нет вообще использовать ORM. То, что вы хотите сделать, может легко выполнить один
INSERT ... WHERE.