LINQ To SQL與Transaction

LINQ To SQLTransaction
/黃忠成
不管你是由我的書中,或是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的畫面如下:
LINQ To SQL與Transaction
當設定DisableTransaction為True時,你會發現Transaction未被啟動。
LINQ To SQL與Transaction
處理完畢,我個人是覺得,LINQ To SQL應該把Transaction以Session概念處理,如Hibernate。