开发者

TransactionScope nuances

开发者 https://www.devze.com 2023-02-11 13:17 出处:网络
Let\'s imagine I have two threads which execute some database-oriented code in thread-specific TransactionScopes with ReadCommitted isolation level. But there is some table which data should be shared

Let's imagine I have two threads which execute some database-oriented code in thread-specific TransactionScopes with ReadCommitted isolation level. But there is some table which data should be shared (no开发者_开发技巧 duplicates should be created).

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted))
{
   ...//some code
   if (!_someRepository.IsValueExists(value))
      _someRepository.AddData(value);
   ...//some code
   transactionScope.Complete();
}

The problem is both threads may check whether data exists at just about same time and if not - create duplicated data (constrains won't help here: I have to prevent exceptional situation to happen). I guess it is a trivial problem but how is it usually solved?

I see the following schematical solution:

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted))
{
   ...//some code
   transactionScope.IsolationLevel = IsolationLevel.ReadUncommitted; //change Isolation Level
   lock (_sharedDataLockObject)
   {
      if (!_someRepository.IsValueExists(value))
         _someRepository.AddData(value);
   }
   transactionScope.IsolationLevel = IsolationLevel.ReadCommitted; //reset IsolationLevel
   ...//some code
   transactionScope.Complete();
}

The first problem with this solution is that TransactionScope doesn't support IsolationLevel modification. But let's imagine I use ADO.NET transaction here. Nevertheless I'm not sure whether it works.


In this case I would do a double check.

First check that it does not exist, no need for a transaction here.

Then start a serializable transaction.

Check that it still does not exist

if not exists add

commit and close the transaction.


I usually solve the exact same problem with a DB constraint and by wrapping the entire transaction in a try-catch with a retry in the catch block. Of course, if it is not possible to restart the transaction for some reason this is not a valid solution (e.g. the transaction is started outside of your control - I'm not sure what you mean by "I have to prevent exceptional situation to happen").

Depending on how long time the transactions usually take you may want to wait a little before the retry or make a couple of retries but as long as the parallel transaction has completed successfully your retry will succeed.

The tricky bit is often how to determine that the exception was caused by a particular constraint violation. This usually requires some empirical testing to determine the exact exception type and some ugly string matching of the exception message.

0

精彩评论

暂无评论...
验证码 换一张
取 消