在我的action方法中使用Lock的缺点

时间:2013-08-21 11:49:45

标签: asp.net asp.net-mvc

我有一个管理控制器类,管理任务需要在同一个操作方法调用上添加和删除实体,所以我担心当多个管理员同时访问同一个操作方法时,一些Add操作将会从第一个事务启动,而其他Add操作将从第二个事务启动。这可能会导致最终结果出现不一致的结果。由于一些Add&删除操作将从TransactionOne启动,而另一个操作将从transactionTwo启动。 例如,我有一个动作方法: -

[Authorize]
 public class SecurityRoleController : Controller
            {

    Repository repository = new Repository();
    //code goes here
    [HttpPost]
    public ActionResult AssignPermisionLevel2(ICollection<SecurityroleTypePermision> list, int id)
        {
            repository.DeleteSecurityroleTypePermisions(id);
            foreach (var c in list)
            {
                repository.InsertOrUpdateSecurityroleTypePermisions(c,User.Identity.Name);
            }
            repository.Save();
            return RedirectToAction("AssignPermisionLevel", new { id = id });
        }

调用以下repertory方法,为同一个action方法调用执行添加和删除操作: -

public void DeleteSecurityroleTypePermisions(int securityroleID)
{    
    var r = tms.SecurityroleTypePermisions.Where(a => a.SecurityRoleID == securityroleID);
    foreach (var c in r) {
        tms.SecurityroleTypePermisions.Remove(c);
    }

}

以及以下存储库方法: -

public void InsertOrUpdateSecurityroleTypePermisions(SecurityroleTypePermision role, string username)
{    
     var auditinfo = IntiateAdminAudit(tms.AuditActions.SingleOrDefault(a => a.Name.ToUpper() == "ASSIGN PERMISION").ID, tms.SecurityTaskTypes.SingleOrDefault(a => a.Name.ToUpper() == "SECURITY ROLE").ID, username, tms.SecurityRoles.SingleOrDefault(a=>a.SecurityRoleID == role.SecurityRoleID).Name, tms.PermisionLevels.SingleOrDefault(a=>a.ID== role.PermisionLevelID).Name +  " --> " + tms.TechnologyTypes.SingleOrDefault(a=>a.AssetTypeID == role.AssetTypeID).Name);
     tms.SecurityroleTypePermisions.Add(role);
     InsertOrUpdateAdminAudit(auditinfo);
}

为了避免任何意外结果,我决定在我的动作方法中加入一个锁,如下所示: -

[Authorize]
    public class SecurityRoleController : Controller
    {

        Repository repository = new Repository();
public static object REQUEST_LOCK = new object();
//code goes here
[HttpPost]
        public ActionResult AssignPermisionLevel2(ICollection<SecurityroleTypePermision> list, int id)
        {
            lock (REQUEST_LOCK)
           {

                repository.DeleteSecurityroleTypePermisions(id);
                foreach (var c in list)
                {
                    repository.InsertOrUpdateSecurityroleTypePermisions(c, User.Identity.Name);
                }
                repository.Save();
                return RedirectToAction("AssignPermisionLevel", new { id = id });
           }
        }

所以任何人都可以建议,如果我正在做的是正确的方式,并且我的行动方法中的Lock会影响性能还是有我不知道的缺点? 此致

1 个答案:

答案 0 :(得分:2)

我可以问你为什么删除然后插入新记录?为什么不更新现有记录?这将首先避免对锁的需要,因为RDBMS将通过在更新进行时锁定要更新的记录来自动处理这种情况(在真正的关系语义中,更新是删除然后重新添加新值 - 但我怀疑实际的RDBMS实现是否专门做了这一点,但它们必须保证ATOMICity与数据操作有关。)

无论如何,是的,如果您期望大量用户同时访问您的网站,这可能会对性能产生影响(我在考虑每秒数千个请求 - 纯猜想的顺序) 。从本质上讲,在调用此操作方法时,您正在将异步多线程应用程序转换为同步单线程应用程序。

换句话说,如果这个设计会对性能产生任何影响,那么这个动作方法几乎肯定会成为瓶颈。

确定性能影响的唯一方法是对网站执行负载或压力测试,尤其是每秒对此方法的数千(近)同时请求。

更新:我看到你有一个名为InsertOrUpdateSecurityroleTypePermission的方法。您应该将此方法/存储过程分为两个,一个插入,另一个更新。这样,正如我之前所说,你可以完全避免潜在的线程/ ATOMICity问题。

更新2: 好吧,我想我发现了导致不必要复杂性的设计决策。相反,您可以使用事务来设计存储过程,以按照规定的顺序处理添加/删除必要记录以完成任务。这样,你就是利用数据库来确保这一切都是在原子上发生的;并且使用事务,如果某些内容无效或无法完成(当前代码不会执行),您可以ROLLBACK,这样您就可以摆脱{{1}在你的行动方法中。此外,您仍然可以使用EF-EF来调用您在数据库中创建的存储过程。就并发用户而言,使用这种方法,您仍然会有“最后写入获胜”的情况(例如顺序“访问”)。