Utilizzo di LINQ-to-Entities 4.0, esiste un modello o un costrutto corretto per l'implementazione sicura "se non esiste quindi inserire"?Implementazione di if-not-exists-insert utilizzando Entity Framework senza condizioni di gara
Ad esempio, attualmente dispongo di una tabella che tiene traccia dei "preferiti utente" - gli utenti possono aggiungere o rimuovere articoli dal proprio elenco di preferiti.
La tabella sottostante non è una vera relazione molti-a-molti, ma invece tiene traccia di alcune informazioni aggiuntive come la data in cui è stato aggiunto il preferito.
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);
Inserendo due preferiti con la stessa coppia Utente/Articolo si ottiene un errore di duplicazione della chiave, come desiderato.
ho attualmente implementato la logica "se non esiste quindi inserire" nello strato di dati utilizzando C#:
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();
}
Il problema di questa implementazione è che è suscettibile a condizioni di gara. Ad esempio, se un utente fa doppio clic sul collegamento "aggiungi ai preferiti", due richieste potrebbero essere inviate al server. La prima richiesta ha esito positivo, mentre la seconda richiesta (quella che l'utente vede) non riesce con un UpdateException che racchiude una SqlException per l'errore della chiave duplicata.
Con le stored procedure T-SQL, è possibile utilizzare le transazioni con i suggerimenti di blocco per garantire che non si verifichi mai una condizione di competizione. Esiste un metodo pulito per evitare le condizioni di competizione in Entity Framework senza ricorrere a stored procedure o ad escludere ciecamente le eccezioni?
hai dato un'occhiata a 'using (var tran = new TransactionScope())'? – RPM1984