So... TransactionScope
...
If you have worked with the TransactionScope
in the past you might have noticed that it can be tricky, and even dangerous if you don't use it correctly.
So anyways, I have tried to change the TransactionScope
timeout property at runtime and the thing still goes back to the default timeout of 10 minutes.
This is because the 'TransactionScope' uses the following object to get the maximum timeout for a transaction: TransactionManager.MaximumTimeout
. If you decompile it you will see the following:
if (!TransactionManager._cachedMaxTimeout)
{
lock (TransactionManager.ClassSyncObject)
{
if (!TransactionManager._cachedMaxTimeout)
{
TransactionManager._maximumTimeout = TransactionManager.MachineSettings.MaxTimeout;
TransactionManager._cachedMaxTimeout = true;
}
}
}
Pay special attention to: TransactionManager.MachineSettings.MaxTimeout
It always reads from the machine settings defined on the machine.config (C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config)
And this specific section is defined as follows:
<sectionGroup name="system.transactions" type="System.Transactions.Configuration.TransactionsSectionGroup, System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null">
<section name="defaultSettings" type="System.Transactions.Configuration.DefaultSettingsSection, System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null"/>
<section name="machineSettings" type="System.Transactions.Configuration.MachineSettingsSection, System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null" allowDefinition="MachineOnly" allowExeDefinition="MachineOnly"/>
</sectionGroup>
Note that it's marked with: allowExeDefinition="MachineOnly"
that's why we cannot override it in our config files - shame :(
So basically I can think in two options to increase the maximum transaction timeout:
Updating the machine.config file
You could do it although I wouldn't recommend it so I won't bother to show you how... well take a quick look
Hacking - using reflection
Or do it the Ninja way...
private void ConfigureTransactionTimeout(TimeSpan value)
{
//initializing internal stuff
// ReSharper disable once NotAccessedVariable
var timespan = TransactionManager.MaximumTimeout;
//initializing it again to be sure
// ReSharper disable once RedundantAssignment
timespan = TransactionManager.MaximumTimeout;
SetTransactionManagerField("_cachedMaxTimeout", true);
SetTransactionManagerField("_maximumTimeout", value);
Condition.Ensures(TransactionManager.MaximumTimeout, "TransactionManager.MaximumTimeout").IsEqualTo(value);
}
private void SetTransactionManagerField(string fieldName, object value)
{
var cacheField = typeof (TransactionManager).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static);
Condition.Ensures(cacheField, "cacheField").IsNotNull();
cacheField.SetValue(null, value);
}
And use it like:
ConfigureTransactionTimeout(TimeSpan.FromMinutes(20));
var timespan = TransactionManager.MaximumTimeout;
var transactionOptions = new TransactionOptions
{
IsolationLevel = isolationLevel,
Timeout = timespan
};
var ts = new TransactionScope(transactionScopeOption, transactionOptions);
There you go