Используя LINQ-to-Entities 4.0, есть ли правильный шаблон или конструкция для безопасной реализации "if not exists then insert"?
Например, в настоящее время у меня есть таблица, которая отслеживает "избранное пользователя" - пользователи могут добавлять или удалять статьи из своего списка избранных.
Базовая таблица не является истинным отношением "многие ко многим", но вместо этого отслеживает некоторую дополнительную информацию, такую как дата добавления фаворита.
CREATE TABLE UserFavorite
(
FavoriteId int not null identity(1,1) primary key,
UserId int not null,
ArticleId int not null
);
CREATE UNIQUE INDEX IX_UserFavorite_1 ON UserFavorite (UserId, ArticleId);
Вставка двух избранных с одинаковой парой User/Article приводит к ошибке повторяющегося ключа по желанию.
В настоящее время я реализовал логику "if not exists then insert" в слое данных с помощью С#:
if (!entities.FavoriteArticles.Any(
f => f.UserId == userId &&
f.ArticleId == articleId))
{
FavoriteArticle favorite = new FavoriteArticle();
favorite.UserId = userId;
favorite.ArticleId = articleId;
favorite.DateAdded = DateTime.Now;
Entities.AddToFavoriteArticles(favorite);
Entities.SaveChanges();
}
Проблема с этой реализацией заключается в том, что она подвержена условиям гонки. Например, если пользователь дважды щелкает ссылку "добавить в избранное", на сервер могут быть отправлены два запроса. Первый запрос завершается успешно, в то время как второй запрос (тот, который видит пользователь) терпит неудачу с UpdateException, обертывающим исключение SqlException для ошибки повторяющегося ключа.
С помощью хранимых процедур T-SQL я могу использовать транзакции с подсказками блокировки, чтобы гарантировать, что состояние гонки никогда не произойдет. Есть ли чистый метод для предотвращения состояния гонки в Entity Framework без использования хранимых процедур или слепо глотающих исключений?