不管你是由我的書中,或是MSDN、網站處得知,LINQ to SQL之DataContext於SubmitChanges函式執行時,就算不指定Transaction,DataContext都會自動啟動一個Transaction,在許多ORM中,這算是相當常見的設計。 不過,如果我不想要這個預設的Transaction呢?
LINQ To SQL與Transaction
文/黃忠成
不管你是由我的書中,或是MSDN、網站處得知,LINQ to SQL之DataContext於SubmitChanges函式執行時,就算不指定Transaction,DataContext都會自動啟動一個Transaction,在許多ORM中,這算是相當常見的設計。
不過,如果我不想要這個預設的Transaction呢?原因有很多,可能是為了減少Lock的時間,或是效能、資源等等,反正就是不想要這個預設行為就是了!只要簡簡單單的更新資料就好了。
可能嗎?就目前的LINQ To SQL設計來說,這個行為是強制性的,且無可調整之空間,但!若要強渡關山也不是沒交通工具,DataContext開了一個小口,讓我們可以覆載SubmitChanges函式,以此為起點,我利用了大量的Reflection技巧,讓Transaction消失。
using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Collections.Generic; using System.Linq; using System.Data.Linq; using System.Text; using System.Reflection; namespace ConsoleApplication35 { class Program { static void Main(string[] args) { NorthwindDataContext context = new NorthwindDataContext(); var item = (from s1 in context.Customers where s1.CustomerID == "VINET" select s1).FirstOrDefault(); if (item != null) item.ContactName = "VINET14"; Console.ReadLine(); context.DisableTransaction = true; context.SubmitChanges(); Console.ReadLine(); } } partial class NorthwindDataContext { public bool DisableTransaction { get; set; } private static MethodInfo _checkDispose = null; private static MethodInfo _checkNotInSubmitChanges = null; private static MethodInfo _verifyTrackingEnabled = null; private static MethodInfo _acceptChanges = null; private static MethodInfo _submitChanges = null; private static FieldInfo _conflicts = null; private static FieldInfo _isInSubmitChanges = null; private static PropertyInfo _services = null; private static Type _changeProcessorType = null; private static ConstructorInfo _ci = null; static NorthwindDataContext() { _checkDispose = typeof(DataContext).GetMethod("CheckDispose", BindingFlags.NonPublic | BindingFlags.Instance); _checkNotInSubmitChanges = typeof(DataContext).GetMethod("CheckNotInSubmitChanges", BindingFlags.NonPublic | BindingFlags.Instance); _verifyTrackingEnabled = typeof(DataContext).GetMethod("VerifyTrackingEnabled", BindingFlags.NonPublic | BindingFlags.Instance); _acceptChanges = typeof(DataContext).GetMethod("AcceptChanges", BindingFlags.NonPublic | BindingFlags.Instance); _conflicts = typeof(DataContext).GetField("conflicts", BindingFlags.NonPublic | BindingFlags.Instance); _isInSubmitChanges = typeof(DataContext).GetField("isInSubmitChanges", BindingFlags.NonPublic | BindingFlags.Instance); _changeProcessorType = typeof(DataContext).Assembly.GetType( "System.Data.Linq.ChangeProcessor"); _services = typeof(DataContext).GetProperty("Services", BindingFlags.NonPublic | BindingFlags.Instance); _ci = _changeProcessorType.GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(DataContext).Assembly.GetType("System.Data.Linq.CommonDataServices"), typeof(DataContext) }, null); _submitChanges = _changeProcessorType.GetMethod("SubmitChanges", BindingFlags.NonPublic | BindingFlags.Instance); } public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode) { if (DisableTransaction) { _checkDispose.Invoke(this, null); _checkNotInSubmitChanges.Invoke(this, null); _verifyTrackingEnabled.Invoke(this, null); ((ChangeConflictCollection)_conflicts.GetValue(this)).Clear(); try { _isInSubmitChanges.SetValue(this, true); object processor = _ci.Invoke(new object[] { _services.GetValue(this, null), this }); _submitChanges.Invoke(processor, new object[] { failureMode }); _acceptChanges.Invoke(this, null); } finally { _isInSubmitChanges.SetValue(this, false); } } else base.SubmitChanges(failureMode); } } } |
當DisableTransaction屬性為False時,SQL Profiler的畫面如下:
當設定DisableTransaction為True時,你會發現Transaction未被啟動。
處理完畢,我個人是覺得,LINQ To SQL應該把Transaction以Session概念處理,如Hibernate。