1、報錯如下:Invalid Operation the connection is closed,無效操作,連接被關閉。這個錯誤是並不是每次都報,只有在復雜操作、大事務的情況下才偶然報出來。
stackOverflow上有很多關於這個問題的討論,例如這個:《System.Data.OracleClient random Invalid Operation the connection is closed》,但較零散,全掃了一遍之后,我仍然有如下疑問:
1)怎么看TransactionScope里的Timeout到底是多少,比如通過TransactionOptions.Timeout = new TimeSpan(0,0,3)設置之后,怎么知道TransactionScope里的超時就是3s?
2)默認MaxTimeout=10m,怎么才能設置任意值?
3)嵌套事務的外層超時是否包括所有內層事務的時間?
4)超時機制是怎么起作用的?
通過Reflector之類的工具查看源碼,終於有了初步的理解,不對的地方,請大家指正!
2、怎么看TransactionScope里的Timeout?
用調試器逐級展開TransactionScope實例如下:
scope->committableTransaction->base->internalTransaction->absoluteTimeout //注:這是展開過程,不是代碼
這個absoluteTimeout就是實際結果,但往往與你設置的值不匹配,比如:
TimeSpan | absoluteTimeout |
3s | 5 |
10s | 19 |
1m | 117 |
10m | 1171 |
30m | 3515 |
1h | 7031 |
看起來是經過某個算法轉換后的值,這個算法寫在TransactionTable.TimeoutTicks()里:
internal long TimeoutTicks(TimeSpan timeout){ if(timeout != TimeSpan.Zero) { return (timeout.Ticks / 10000L >> 9) + this.ticks; } return 9223372036854775807L; }
這就清楚了,再加上上面那張對照表,就可以通過absoluteTimeout清楚知道設置是否生效。
3、默認MaxTimeout=10m,怎么才能設置任意值?
1)先說一下這個10m怎么來的?
TransactionScope.ctr()->TransactionManager.ValidateTimeout()->MaximumTimeout->MachineSettingsSection.MaximumTimeout
這個屬性先讀取c:\windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config里的值://注意對應的framework的版本!
<system.transactions> <machineSettings maxTimeout="00:10:00" /> </system.transactions>
當然,我查看下來,所有版本的machine.config里默認都沒有這個節點。那么退一步,由Attribute設置默認值:
[ConfigurationProperty("maxTimeout", DefaultValue = "00:10:00")] public TimeSpan MaxTimeout
2)再說一下怎么改成任意值?
2.1)修改machine.config,優點:方便,缺點:會影響機器上的所有事務。方法是在全局的machine.config里添加對應節點,注意在自己的app/web.config里添加<machineSettings>節點會報錯。
2.2)用反射(神器)!優點:只作用於自己的事務,缺點:用反射、稍麻煩、稍慢。
static void ChangeMaximumTimeout() { Type t = typeof(TransactionManager); FieldInfo f = t.GetField("_cachedMaxTimeout", BindingFlags.NonPublic | BindingFlags.Static); f.SetValue(null, true); f = t.GetField("_maximumTimeout", BindingFlags.NonPublic | BindingFlags.Static); f.SetValue(null, new TimeSpan(2, 0, 0)); }
這么寫是因為最終大家取的都是static類里的static屬性:
public static class TransactionManager { public static TimeSpan MaximumTimeout { get { if(!_cachedMaxTimeout) { _maximumTimeout = MachineSettings.MaxTimeout; } return _maximumTimeout; }}}
貼出來,一目了然!
4、嵌套事務的外層超時是否包括所有內層事務的時間?
我理解外層超時包含所有內層事務的執行時間,測試代碼如下:
option.Timeout = new TimeSpan(0,0,3); using(var scope = new TransactionScope(TransactionScopeOption.Required, option)) { Thread.Sleep(2000); option1.Timeout = new TimeSpan(0,0,5); using(var scope1 = new TransactionScope(TransactionScopeOption.Required, option)) { Thread.Sleep(500); // 800以上基本就報超時錯誤了 }}
5、超時機制是怎么起作用的?
1)對於非根(committableTransaction == null)的事務,會構造一個scopeTimer = new Timer(timeSpan, TimeoutCallback)來觸發超時回滾;
2)對於committableTransaction != null的根事務,超時機制尚未找到,有待補充完整。