使用LINQ-to-Entities 4.0,是否有正确的模式或构造来安全地实现“如果不存在则插入”?
例如,我目前有一个跟踪“用户收藏”的表格--用户可以在收藏列表中添加或删除文章。
基础表不是真正的多对多关系,而是跟踪一些附加信息,例如添加收藏夹的日期。
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);根据需要,插入具有相同用户/文章对的两个收藏夹会导致重复的键错误。
我目前已经使用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();
}这个实现的问题是它容易受到竞争条件的影响。例如,如果用户双击“添加到收藏夹”链接,则会向服务器发送两个请求。第一个请求成功,而第二个请求(用户看到的请求)失败,UpdateException包装了重复键错误的SqlException。
使用T-SQL存储过程,我可以使用带有锁提示的事务,以确保不会出现争用条件。有没有一种干净的方法来避免实体框架中的竞态条件,而不求助于存储过程或盲目地吞噬异常?
发布于 2011-04-19 04:05:33
您可以尝试将其封装在一个事务中,并结合“著名的”try/catch模式:
using (var scope = new TransactionScope())
try
{
//...do your thing...
scope.Complete();
}
catch (UpdateException ex)
{
// here the second request ends up...
}发布于 2011-04-23 03:47:25
还可以编写使用sql 2005+中的一些新技巧的存储过程。
在update语句中使用您的组合唯一ID (userID + articleID),然后使用@@RowCount函数查看行数是否大于0如果它是1(或更多),则更新找到了与您的userID和ArticleID匹配的行,如果它是0,则可以插入。
例如:
更新tablex集合userID = @ userID,ArticleID = @ArticleID (您可以在这里拥有更多属性,只要where包含一个组合的唯一ID),其中UserID= @UserID和ArticleID = @ArticleID
如果(@@RowCount = 0) Begin Insert Into ...结束
最棒的是,这一切都是在一个调用中完成的,所以您不必先比较数据,然后再决定是否应该插入。当然,它将停止任何迟钝的插入,并且不会抛出任何错误(优雅地?)
https://stackoverflow.com/questions/4189954
复制相似问题