我有一个几乎同时收到几个请求的Web服务。
如果发生这种情况,两个请求的事务处理死锁,我必须回滚它们的重新运行。
using (var context = new DbContext())
using (var dbContextTransaction = context.Database.BeginTransaction(IsolationLevel.Serializable))
{
var subscription = context.Subscriptions.FirstOrDefault(x => x.Provider == provider);
if (subscription == null)
{
*/Insert*/
}
else
{
/*Update*/
}
context.SaveInTransaction(null);
dbContextTransaction.Commit();
}
据我所知,两个请求都进入了交易并阅读了订阅表。在准备提交时,它们会死锁,因为它们无法在另一个事务(IsolationLevel.Serializable)读取的数据范围中插入值。
如果我没有使TransactionLevel Serializeable,其中一个请求无法满足提供者列的唯一约束并获得回滚。
我该怎么做才能防止死锁?
答案 0 :(得分:2)
如果我没有使TransactionLevel Serializeable,其中一个请求无法满足提供者列的唯一约束并获得Rollbacked。
你可以:
1)处理PK违规/死锁并继续更新现有行。请注意,您应该使用READ COMMITTED并处理PK违规,因为您知道发生故障的会话可以继续更新。使用SERIALIZABLE,您可以在INSERT或UPDATE上获得死锁。
2)使用带有锁定提示的原始SQL使一个会话等待另一个会话。
正确的锁定提示是(UPDLOCK,SERIALIZABLE)。如果您使用MERGE,则锁定提示仍然需要 。见https://blogs.msdn.microsoft.com/dbrowne/2013/02/25/why-is-tsql-merge-failing-with-a-primary-key-violation-isnt-it-atomic/
3)在事务开始时调用sp_getapplock以确保只有一个会话具有锁定。请注意,CLR锁定仅在您拥有应用程序的单个实例时才有效。对于客户端/服务器应用程序或Web场,CLR锁定将不起作用。
您可以在DbContext中添加一个方法,例如:
public void GetAppLock(string lockName)
{
this.Database.ExecuteSqlCommand("exec sp_getapplock @name, 'exclusive'", new System.Data.SqlClient.SqlParameter("@name",lockName));
}