如何使用linq到sql同时增加页面视图计数

本文关键字:视图 增加 linq 何使用 sql | 更新日期: 2023-09-27 17:58:40

我有一个ASP。NET MVC 3 Web应用程序使用Linq到SQL作为我的数据访问层。每次调用Details操作时,我都试图增加Views字段,但我在db上收到一个"Row not found or changed"错误。SubmitChanges(),如果两个人碰巧同时执行操作。

public ActionResult Details(int id)
{
    DataClassesDataContext db = new DataClassesDataContext();
    var idea = db.Ideas.Where(i => i.IdeaPK == id).Single();
    idea.Views++;
    db.SubmitChanges();
    return View(new IdeaViewModel(idea));
}

我可以在.dbml(数据模型)中将Views字段的UpdateCheck设置为"Never",这样可以消除错误,但随后可以使用相同的Views计数更新两次idea记录。即

First instance of Details action gets idea record with Views count of 1.
Second instance of Details action gets idea record with Views count of 1.
First instance increments Views to 2
First instance commits
Second instance increments Views to 2
Second instance commits
Result: Views field is 2 
Expected Result: Views field should be 3

我研究过使用TransactionScope,但从两个调用中的一个调用中得到了以下死锁错误:

事务(进程ID 54)为在锁定资源上死锁另一个过程,已被选为死锁受害者。重新运行交易

当我将我的操作更新为:

public ActionResult Details(int id)
{
    DataClassesDataContext db = new DataClassesDataContext();
    using (var transaction = new TransactionScope()){
        var idea = db.Ideas.Where(i => i.IdeaPK == id).Single();
        idea.Views++;
        db.SubmitChanges();
        return View(new IdeaViewModel(idea));
    }
}

我还尝试使用TransactionScopeOptions增加TransactionScope超时,但这似乎没有帮助(但我可能也必须在其他地方设置它)。我可能可以通过使用db在单个SQL命令中执行增量来解决这个例子。ExecuteQuery,但我试图弄清楚如何实现这一点,这样我就知道在更复杂的场景中该怎么做了(我想在一个事务中执行多个命令)。

如何使用linq到sql同时增加页面视图计数

我认为您应该创建一个存储过程,它将原子地递增您想要的字段,并通过LINQ2SQL调用它。

另一种选择是将您的操作封装到具有适当隔离级别的事务中。

您不应该需要事务或存储过程。只需使用DataContext.ExecuteCommand:

db.ExecuteCommand("UPDATE Ideas SET Views = Views + 1 WHERE IdeaPK = {0}", id);

这将把它作为一个SQL语句执行,因此是原子的。

我会尝试捕获Row not Found异常并触发整个操作的重试。重新查询视图行并更新它,然后再次调用submit changes。请确保使用计数器来确保只重试该操作五次左右,这样就不会陷入无限循环。

我强烈建议您按照@Dmitry的建议考虑使用存储过程,并将增量打包并选择为一个操作。这将给您带来两个好处:1)它将消除争用问题;2)它将把整个操作放入对数据库的一个调用中。以下是基本思想:

CREATE PROCEDURE spIdeasRetrieveAndLog
    @IdeaPK int
AS
BEGIN
    UPDATE Ideas SET Views = Views + 1 WHERE IdeaPK = @IdeaPK
    GO
    SELECT * FROM Ideas WHERE IdeaPK = @IdeaPK
END
GO

我建议您使用其中一种消息传递框架(例如,NServiceBus,但还有其他选项-MassTransit、Rhino Service Bus)。他们将帮助你以非常简单和优雅的方式解决这个问题。