处理同一控制器的多个实例

时间:2017-11-07 16:18:16

标签: c# asp.net-mvc multithreading entity-framework

我目前正在ASP.NET CORE 2.0中开发一个应用程序

以下是我的控制器中的操作,当用户单击“提交”按钮时,该操作将被执行。

Action inside the controller

以下是被称为动作的函数

enter image description here

作为防止数据库内部重复的措施,我有这个功能 IsSignedInJob()。该功能正常工作

我的问题:

有时,当互联网连接速度很慢或服务器没有立即响应时,可以多次单击“提交”按钮。重新建立连接后,浏览器(在我的案例中为Chrome)会向服务器发送多个HttpPost请求。在这种情况下,函数(来自不同实例的相同函数)的执行时间非常接近,在数据库发生更改之前,其他实例正在进行相同的更改,而不会相互了解。

有没有办法在服务器端解决这个问题,而不是“hacky”?

谢谢

3 个答案:

答案 0 :(得分:5)

根据评论中的建议 - 这是我的首选方法 - 您可以在第一次点击时立即禁用该按钮。

另一种解决方案是在字典中添加一些内容,表明该作业已经注册,但这可能需要使用锁定,因为您需要确保一次只能读写一个线程。并发集合不会成功,因为问题不在于此操作是否是线程安全的。您拥有的IsSignedInJob方法可以在幕后执行此操作但我不会检查数据库,因为延迟可能太高。从字典中添加/删除密钥应该快得多。

答案 1 :(得分:2)

Icarus's answer is great for the user experience并且应该实施。如果您还需要确保请求仅在服务器端处理一次,那么您有几个选项。这是一个使用ReaderWRiterLockSlim class

private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();

[HttpPost]
public async SomeMethod()
{
  if (cacheLock.TryEnterWriteLock(timeout));
  {
    try
    {
      // DoWork that should be very fast
    }
    finally
    {
      cacheLock.ExitWriteLock();
    }
  }
}

这将防止重叠的DoWork代码。它不会阻止DoWork完全完成,然后再执行另一个导致DoWork的帖子。

如果要阻止帖子两次发生,请实施AntiForgeryToken,然后将令牌存储在会话中。这样的事情(没有永久使用会话)可能无法编译,但你应该明白这一点。

private const SomeMethodTokenName = "SomeMethodToken";

[HttpPost]
public async SomeMethod()
{
  if (cacheLock.TryEnterWriteLock(timeout));
  {
    try
    {
      var token = Request.Form.Get["__RequestVerificationToken"].ToString();
      var session = Session[SomeMethodTokenName ];
      if (token == session) return;
      session[SomeMethodTokenName] = token

      // DoWork that should be very fast
    }
    finally
    {
      cacheLock.ExitWriteLock();
    }
  }
}

不完美,两个不同的请求可能反复发生,您可以在会话中存储此会话的所有已使用令牌的列表。没有完美的方法,因为即使这样,有人可能在技术上导致OutOfMemoryException,如果他们想要(对于存储在会话中的许多令牌),但你明白了。

答案 2 :(得分:-1)

尽量不要使用异步处理。删除任务,等待和异步。