默认事务超时
Posted
技术标签:
【中文标题】默认事务超时【英文标题】:Default Transaction Timeout 【发布时间】:2010-11-23 19:23:02 【问题描述】:我曾经使用 TransactionOptions.Timeout 设置事务超时,但为了便于维护,我决定使用配置方法:
<system.transactions>
<defaultSettings timeout="00:01:00" />
</system.transactions>
当然,在放入之后,我想测试它是否正常工作,因此将超时时间减少到 5 秒,然后运行一个持续时间比这更长的测试 - 但事务似乎没有中止!如果我调整测试以将 TransactionOptions.Timeout 设置为 5 秒,则测试按预期工作
经过调查,我认为问题似乎与 TransactionOptions.Timeout 有关,即使我不再使用它。
我仍然需要使用TransactionOptions,所以我可以设置IsolationLevel,但是我不再设置Timeout值,如果我在创建它后查看这个对象,超时值是00:00:00,相当于无穷大。这是否意味着我在配置文件中设置的值被忽略了?
总结一下:
是否无法混合配置 设置和使用 交易选项 如果没有,是否有 任何方式来提取配置设置 在运行时,并使用它来设置 超时属性 [Edit] OR 设置默认隔离级别而不使用 TransactionOptions【问题讨论】:
你在哪个配置文件中设置了这个?在哪个配置部分? 在您可以使用 TransactionManager.DefaultTimeout
从配置中获取(已验证的)默认超时。
TransactionOptions
是一个封装了超时和隔离级别的结构体。当使用默认构造函数初始化struct 时,它总是将结构成员初始化为其默认值:
TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.Timeout == default(TimeSpan); // TimeSpan.Zero
transactionOptions.IsolationLevel == default(IsolationLevel); // IsolationLevel.Serializable
如果要指定IsolationLevel
并使用默认超时:
new TransactionOptions()
IsolationLevel = IsolationLevel.Serializable, // Use whatever level you require
Timeout = TransactionManager.DefaultTimeout
;
【讨论】:
这是我一直希望的答案!不幸的是,我给你答案为时已晚,但 TransactionManager.DefaultTimeout 绝对是我想要的。感谢发帖! 我还注意到微软也在他们的documentation on System.Transactions 中使用了这种技术。感谢您提供有用的信息! ILSpy 中的 FWIW Groveling 在 .NET 4.0 中显示默认值为 1 分钟,请记住这是一个实现细节,因此可能会发生变化,请使用上述内容再次检查此值!:// System. Transactions.Configuration.DefaultSettingsSection [ConfigurationProperty("timeout", DefaultValue = "00:01:00"), TimeSpanValidator(MinValueString = "00:00:00", MaxValueString = "10675199.02:48:05.4775807")] @aolszowkaTransactionManager.DefaultTimeout
的值是配置文件中指定的值。默认值确实是1分钟,可以通过编辑DefaultSettingsSection.Timeout来修改,所以不需要复查!
machineSettings 或 TransactionManager.DefaultTimeout
?【参考方案2】:
您可以混合使用 system.transaction 配置设置和 TransactionOption
类的使用,但您需要注意一些事项。
如果您使用
TransactionOption
和 指定一个Timeout
值,该值 将用于 system.transactions/defaultTimeout 价值。
以上是我认为您的问题的症结所在。您正在使用 TransactionOption
指定 isolation 级别,并且作为副作用,您将获得 infinite 超时值,因为无限是 TransactionOption
的默认超时值如果未指定。不过,我不太清楚为什么会这样……默认为默认事务超时是有意义的。
您可以实现自己的 TransactionOptions 助手类,其中包括从 app.config 读取的默认值(如果找到)或默认为可以使用的 TransactionOption 类的合理值。
无论如何,您仍然可以通过使用 system.transaction/machineSettings/maxTimeout 值来限制这一点。这是一个管理设置,只能通过 machine.config 进行配置。如果你从 app/web.config 尝试,你会得到一个 ConfigurationException。
<system.transactions>
<machineSettings maxTimeout="00:00:30" />
</system.transactions>
maxTimeout 设置后,无论您指定什么超时值,最大值都将限制为 maxTimeout 值。默认的 maxTimeout 是 00:10:00,即 10 分钟,因此您实际上不会在事务中遇到无限超时。
您还可以在事务中使用的数据库连接上显式设置事务 IsolationLevel。像这样?
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();
在您的测试中,您可能需要确保重新生成 app.config 以便重新生成。在我的测试中,我似乎需要终止 *.vshost.exe 进程才能让它接受 system.transaction 配置设置更改——尽管我觉得这可能是侥幸。仅供参考..
【讨论】:
【参考方案3】:对于Reflector来说,使用TransactionScope
的构造函数设置事务超时的基本规则如下:
DefaultTimeOut 由下面满足的第一条规则确定:
如果构造函数有TimeSpan
参数,DefaultTimeout 就是那个参数
如果构造函数有TransactionOption
参数,则DefaultTimeout为transactionOption.TimeOut
如果构造函数有TransactionScopeOption
参数,则DefaultTimeout为scopeOption.TimeOut
如果构造函数没有超时参数,则 DefaultTimeout 是在应用程序或 Web 配置文件中指定的值。
否则,DefaultTimeOut 为 1 分钟。
MaxTimeOut 为 10 分钟,除非在 machine.config 中指定了另一个值。
事务的有效超时小于大于零的 MaxTimeOut 和 DefaultTimeOut。如果 MaxTimeOut 和 DefaultTimeOut 都为零,则有效超时是long.MaxValue
表示的滴答数(无穷大)。
如果TransactionScope
实例没有创建新事务,要么是因为事务被传递到其构造函数中,要么是因为事务范围选项不需要它(例如,当存在环境事务并且需要 TransactionScopeOption 时) ,但是在构造函数中仍然传递了timeOut
参数,启动了一个计时器。当超时时间过去时,底层事务的TimeOut()
方法被调用。在这种情况下不使用 DefaultTimeOut 和 MaxTimeOut 属性。
如果是transactionScopeOption == TransactionScopeOption.Supress
,超时被忽略,没有任何作用。
如果 machine.config 中的相关部分被覆盖(注意 allowDefintion 和 allowExeDefinition 属性的值),也可以在 app/web 配置文件中定义 MaxTimeOut:
<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>
为了快速参考,这里是 TransactionScope 构造函数:
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】:使用 TransactionOptions 时忽略配置文件设置。在大多数情况下,创建 TransactionScope 将创建 CommittableTransaction 的实例。 CommittableTransaction 的无参数构造函数将使用配置文件设置作为其默认超时。采用 TransactionOptions 或 TimeSpan 的 TransactionScope 构造函数将调用 CommittableTransaction 类的重载之一,而不是无参数版本。因此,如果您想使用该值,您必须自己从配置文件中获取它。
当我遇到这个问题时,我将以下代码放在一个小的 TransactionOptionsFactory 类中。
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;
【讨论】:
抱歉,迈克,我会奖励你赏金的,但我显然比赏金截止日期晚了一个小时。该解决方案将非常巧妙地解决我的问题。遗憾的是,API 对此并不清楚。 不用担心,马特。我很高兴它有所帮助。【参考方案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
【讨论】:
正如弗朗茨简明扼要地指出的那样——你可以通过反射来做到这一点。我在这篇 MSDN 文章的 cmets 中找到了另一种方法:blogs.msdn.com/b/ajit/archive/2008/06/18/… - 不幸的是,我公司的 DevOps 情况并不是我需要的,所以我没有简单的方法来更新 machine.config处理节点并将设置应用于未来的节点。使用反射对我来说是一个很好的解决方法。【参考方案6】:把我现在的想法记下来:
无法混合配置设置和使用TransactionOptions 在运行时提取配置设置的唯一方法是将 app.config 读取为 XML 文件 默认隔离级别只能通过事务选项完成,或者在 WCF 中使用属性在服务级别完成【讨论】:
至第二个要点:有一个用于访问配置文件的 api。请参阅我的答案中的代码以了解一种方法。您不必求助于将其作为 xml 文件读取。以上是关于默认事务超时的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate:相对于自定义 @Transactional(timeout) 的默认事务超时