可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I used to set Transaction timeouts by using TransactionOptions.Timeout, but have decided for ease of maintenance to use the config approach:
<system.transactions>
<defaultSettings timeout="00:01:00" />
</system.transactions>
Of course, after putting this in, I wanted to test it was working, so reduced the timeout to 5 seconds, then ran a test that lasted longer than this - but the transaction does not appear to abort! If I adjust the test to set TransactionOptions.Timeout to 5 seconds, the test works as expected
After Investigating I think the problem appears to relate to TransactionOptions.Timeout, even though I'm no longer using it.
I still need to use TransactionOptions so I can set IsolationLevel, but I no longer set the Timeout value, if I look at this object after I create it, the timeout value is 00:00:00, which equates to infinity. Does this mean my value set in the config file is being ignored?
To summarise:
- Is it impossible to mix the config
setting, and use of
TransactionOptions
- If not, is there
any way to extract the config setting
at runtime, and use this to set the
Timeout property
- [Edit] OR Set the default isolation-level without using TransactionOptions
回答1:
You can mix system.transaction configuration settings and the use of the TransactionOption
class, but there are some things you need to be aware of.
If you use the TransactionOption
and
specify a Timeout
value, that value
will be used over the
system.transactions/defaultTimeout
value.
The above is the crux of the problem in your case I think. You are using the TransactionOption
to specify the isolation level, and as a side effect you are getting an infinite Timeout value because infinite is the default Timeout value for TransactionOption
if its not specified. Though, I'm not quite sure why that is...it would make sense to default to the default Transaction Timeout.
You can implement your own TransactionOptions helper class that includes defaults that are read from app.config (if found) or default to reasonable values for a TransactionOption class that can be used.
In any case, you can still limit this by using the system.transaction/machineSettings/maxTimeout value. This is an administrative setting and can only be configured through the machine.config. You'll get a ConfigurationException if you try it from app/web.config.
<system.transactions>
<machineSettings maxTimeout="00:00:30" />
</system.transactions>
With the maxTimeout set, no matter what timeout value you specify, the maximum value will be limited to the maxTimeout value. The default maxTimeout is 00:10:00, or 10 minutes, so you wouldn't actually ever have an infinite timeout on a transaction.
You can also set the transaction IsolationLevel explicitly on the database connection you are using within the transaction. Something like this?
var connectionString = "Server=.;Database=master;Trusted_Connection=True;";
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var sqlTransaction = conn.BeginTransaction(System.Data.IsolationLevel.Serializable);
// do database work
//
sqlTransaction.Commit();
}
// do other work..
//
scope.Complete();
}
In your testing, you may need to make sure you rebuild so that the app.config is regenerated . In my testing, it appeared that I needed to terminate the *.vshost.exe process in order for it to pick up the system.transaction configuration setting change - though I feel that may have been a fluke. Just fyi..
回答2:
You can get the (validated) default timeout from the configuration using TransactionManager.DefaultTimeout
.
TransactionOptions
is a struct that encapsulates timeout and isolation level. When initializing a struct using the default constructor, it will always initialize the struct members to their default values:
TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.Timeout == default(TimeSpan); // TimeSpan.Zero
transactionOptions.IsolationLevel == default(IsolationLevel); // IsolationLevel.Serializable
If you want to specify an IsolationLevel
and use the default timeout:
new TransactionOptions()
{
IsolationLevel = IsolationLevel.Serializable, // Use whatever level you require
Timeout = TransactionManager.DefaultTimeout
};
回答3:
Per Reflector, the basic rules for setting a transaction time out with the constructors of TransactionScope
are as follows:
The DefaultTimeOut is determined by the first rule from below that is satisfied:
- if the constructor has a
TimeSpan
parameter, the DefaultTimeout is the that parameter
- if the constructor has a
TransactionOption
parameter, the DefaultTimeout is transactionOption.TimeOut
- if the constructor has a
TransactionScopeOption
parameter, the DefaultTimeout is scopeOption.TimeOut
- if the constructor does not have timeout parameter, the DefaultTimeout is the value specified in the app or web config file.
- otherwise, DefaultTimeOut is 1 minute.
The MaxTimeOut is 10 minutes unless another value is specified in the machine.config.
The effective timeout for the transaction is smaller of the MaxTimeOut and DefaultTimeOut that is greater than zero. If both MaxTimeOut and DefaultTimeOut are zero, the effective timeout is the number of ticks represented by long.MaxValue
(the infinity).
If the TransactionScope
instance does not create a new transaction either because a transaction is passed into its constructor, or because the transaction scope option does not require it (e.g., when an ambient transaction is present and the TransactionScopeOption is Required), but a timeOut
parameter is still passed in the constructor, a timer is started. When the timeout period elapses, the underlying transaction's TimeOut()
method is called. The DefaultTimeOut and MaxTimeOut properties are not used in this case.
If the transactionScopeOption == TransactionScopeOption.Supress
, the timeout is ignored and has no effect.
It is also possible to define the MaxTimeOut in the app/web config file, if the relevant section in the machine.config is overridden (note the values of the allowDefintion and allowExeDefinition attributes):
<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="MachineToApplication" allowExeDefinition="MachineToApplication"/>
</sectionGroup>
For quick reference, here are the TransactionScope constructors:
public TransactionScope(Transaction transactionToUse, TimeSpan scopeTimeout, EnterpriseServicesInteropOption interopOption);
public TransactionScope(TransactionScopeOption scopeOption, TransactionOptions transactionOptions, EnterpriseServicesInteropOption interopOption);
public TransactionScope(TransactionScopeOption scopeOption, TransactionOptions transactionOptions);
public TransactionScope(TransactionScopeOption scopeOption, TimeSpan scopeTimeout);
public TransactionScope(Transaction transactionToUse, TimeSpan scopeTimeout);
public TransactionScope(TransactionScopeOption scopeOption);
回答4:
The config file setting is ignored when TransactionOptions are used. Creating a TransactionScope will, in most cases, create an instance of CommittableTransaction. The no arg constructor of CommittableTransaction will use the config file setting as its default timeout. TransactionScope constructors that take a TransactionOptions or TimeSpan will call one of the overloads of the CommittableTransaction class and not the no arg version. So if you want to use that value you have to grab it from the config file yourself.
When I ran into this I put the following code in a little TransactionOptionsFactory class.
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSectionGroup sectionGroup = configuration.GetSectionGroup("system.transactions");
DefaultSettingsSection defaultSettings = (DefaultSettingsSection) sectionGroup.Sections["defaultSettings"];
TransactionOptions options = new TransactionOptions();
options.Timeout = defaultSettings.Timeout;
options.IsolationLevel = IsolationLevel.ReadCommitted;
回答5:
void Main()
{
var maximumTimeout = TransactionManager.MaximumTimeout;//This step is necessary to init _maximumTimeout value,
FieldInfo fieldInfo = typeof(TransactionManager).GetFields(BindingFlags.NonPublic | BindingFlags.Static).Single(item => item.Name == "_maximumTimeout");
var customMaximumTimeout = TimeSpan.FromHours(1);
fieldInfo.SetValue(null, customMaximumTimeout);
maximumTimeout = TransactionManager.MaximumTimeout;
Console.WriteLine(maximumTimeout);//01:00:00
// use TransactionScope
}
回答6:
To put my current thoughts down:
- It is impossible to mix the config setting, and use of TransactionOptions
- The only way to extract the config setting at runtime is to read the app.config as an XML file
- The default isolation-level can only be done via transaction options, or at the service-level in WCF using attributes