using System; using System.Configuration; using System.Data.SqlClient; using System.Diagnostics; using System.Transactions; using HeuristicLab.Services.Optimization.Billing.Interfaces; namespace HeuristicLab.Services.Optimization.Billing.Business { public class TransactionManager : ITransactionManager { private const int retries = 10; private readonly TimeSpan timeoutInterval; public TransactionManager() { timeoutInterval = TimeSpan.Parse(ConfigurationManager.AppSettings["LongRunningDbCommandTimeout"]); } public void UseTransaction(System.Action call, bool repeatableRead = false, bool longRunning = false) { int n = retries; while (n > 0) { TransactionScope transaction = CreateTransaction(repeatableRead, longRunning); try { call(); transaction.Complete(); n = 0; } catch (SqlException ex) { n--; // TODO: log exception Trace.WriteLine(string.Format("Exception occured, repeating transaction {0} more times: {1}", n, ex.ToString())); if (n <= 0) throw; } finally { transaction.Dispose(); } } } public T UseTransaction(System.Func call, bool repeatableRead = false, bool longRunning = false) { int n = retries; while (n > 0) { TransactionScope transaction = CreateTransaction(repeatableRead, longRunning); try { T result = call(); transaction.Complete(); n = 0; return result; } catch (SqlException ex) { n--; // TODO: log exception Trace.WriteLine(string.Format("Exception occured, repeating transaction {0} more times: {1}", n, ex.ToString())); if (n <= 0) throw; } finally { transaction.Dispose(); } } throw new Exception("This code should not be reached"); } private TransactionScope CreateTransaction(bool repeatableRead, bool longRunning) { var options = new TransactionOptions(); if (repeatableRead) { options.IsolationLevel = IsolationLevel.RepeatableRead; } else { options.IsolationLevel = IsolationLevel.ReadUncommitted; } if (longRunning) { options.Timeout = timeoutInterval; } return new TransactionScope(TransactionScopeOption.Required, options); } } }