我目前正在ASP.NET CORE 2.0中开发一个应用程序
以下是我的控制器中的操作,当用户单击“提交”按钮时,该操作将被执行。
以下是被称为动作的函数
作为防止数据库内部重复的措施,我有这个功能 IsSignedInJob()。该功能正常工作
我的问题:
有时,当互联网连接速度很慢或服务器没有立即响应时,可以多次单击“提交”按钮。重新建立连接后,浏览器(在我的案例中为Chrome)会向服务器发送多个HttpPost请求。在这种情况下,函数(来自不同实例的相同函数)的执行时间非常接近,在数据库发生更改之前,其他实例正在进行相同的更改,而不会相互了解。
有没有办法在服务器端解决这个问题,而不是“hacky”?
谢谢
答案 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)
尽量不要使用异步处理。删除任务,等待和异步。