简体   繁体   English

ASP.NET Web API C#并发请求导致数据库重复

[英]ASP.NET Web API C# Concurrent Requests Causing Duplicates in Database

I have a WebApi Async controller method that calls another async method that first does a null check to see if a record exists, and if it doesn't add it to database. 我有一个WebApi异步控制器方法,该方法调用另一个异步方法,该方法首先进行空检查以查看记录是否存在,以及是否不将其添加到数据库中。 Problem is if I have say 3 requests come in at the same time all the null checks happen at once in various threads (i'm assuming) and I will get 2 duplicate entries. 问题是,如果我说同时有3个请求同时在各个线程中发生所有空检查(我假设),我将得到2个重复的条目。 For example: 例如:

 public async void DoSomething()
{
    var record = {query that returns record or null}
    if (record == null)
    {
        AddNewRecordToDatabase();
    }   
}

... This seems like a very common thing and maybe I'm missing something, but how do I prevent this from happening? ...这似乎是一件很普通的事情,也许我缺少了一些东西,但是如何防止这种情况发生呢? I have to purposely try to get it to create duplicates of course, but it is a requirement to not allow it to do so. 当然,我必须故意使它创建重复项,但是不允许这样做。

Thanks in advance, 提前致谢,

Lee 背风处

I prevent this from happening with async calls by calling a stored procedure instead. 我通过调用存储过程来防止异步调用发生这种情况。 The stored procedure then makes the check, via a "On duplicate key detection" or a similar query for MSSQL db. 然后,存储过程通过“重复键检测”或类似的MSSQL db查询进行检查。

That way, it's merely the order of the async calls that gets to determine which is a create, and which is not. 这样,只有异步调用的顺序才能确定哪个是创建的,哪个不是。

I would solve this by putting unique constraints in the data layer. 我可以通过在数据层中放置唯一约束来解决此问题。 Assuming your data source is sql, you can put a unique constraint across the columns you are querying by with "query that returns record or null" and it will prevent these duplicates. 假设您的数据源是sql,则可以使用“返回记录或null的查询”对要查询的列设置唯一约束,这样可以防止这些重复。 The problem with using a lock or a mutex, is that it doesn't scale across multiple instances of the service. 使用锁或互斥锁的问题是,它无法跨服务的多个实例扩展。 You should be able to deploy many instances of your service (to different machines), have any of those instances handle requests, and still have consistent behavior. 您应该能够将服务的许多实例部署到(不同的机器上),让这些实例中的任何一个都处理请求,并且仍然具有一致的行为。 A mutex or lock isn't going to protect you from this concurrency issue in this situation. 在这种情况下,互斥锁或锁无法保护您免受此并发问题的影响。

There are several answers to this, depending on the details and what your team is comfortable with. 对此有几个答案,具体取决于详细信息以及您的团队满意的条件。

  • The best and most performant answer it to modify your c# code so that instead of calling a CRUD database operation it calls a stored procedure that you write. 最好,最高效的答案是修改您的c#代码,以便它调用您编写的存储过程,而不是调用CRUD数据库操作。 The stored procedure would check for existence and insert or update only as needed. 存储过程将检查是否存在,并且仅在需要时插入或更新。 The specifics are completely under your control, since you write the code. 由于您编写了代码,因此细节完全在您的控制之下。

  • If you want to stick with ordinary CRUD operations, you can force the database to serialize the requests one after the other by wrapping them in a transaction and using a strict transaction isolation level. 如果要坚持使用普通的CRUD操作,则可以通过将请求包装在事务中并使用严格的事务隔离级别来强制数据库将请求依次序列化。 On SQL Server you'd want to use serializable. 在SQL Server上,您要使用可序列化的。 This will prevent any transaction from altering the state of the table in the short time between the part where you check for existence and when you insert the record. 这将防止任何事务在您检查存在的那部分与插入记录之间的短时间内更改表的状态。 See this article for a list of transaction isolation levels and how to apply them in c# code. 有关事务隔离级别以及如何在c#代码中应用它们的列表,请参见本文 If you do this there is a risk of deadlock, so you'll need to catch and swallow those specific errors. 如果这样做,则存在死锁的风险,因此您需要catch并吞下这些特定的错误。

  • If your only need it to ensure uniqueness, and the new record has a natural (not surrogate) key , you can add a uniqueness constraint on the key, which will prevent the second insert from succeeding. 如果您仅需要它来确保唯一性,并且新记录具有自然(而非替代)键 ,则可以在键上添加唯一性约束,这将阻止第二次插入成功。 This solution doesn't work so well with surrogate keys; 该解决方案不适用于代理键。 it doesn't really solve the problem, it just relocates it to the surrogate key generation process. 它并不能真正解决问题,只是将其重新定位到代理密钥生成过程中。 But if you have a decent natural key, this is very easy to implement. 但是,如果您有一个不错的自然键,这很容易实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM