以下是在SQL上實現Serializable隔離的示範: 使用Visual Studio逐行偵錯功能,在兩次讀取間設定中斷點,中斷時用SSMS試圖更動同一筆資料,就可發現UPDATE指令會擋住,要等到.NET程式完成或放棄Transaction後才會繼續執行,驗證鎖定的存在。
private void TestSqlLock()
{
string cnStr =
"Data Source=(local);Integrated Security=SSPI;Initial Catalog=Playground;";
TransactionOptions txo = new TransactionOptions();
txo.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
txo.Timeout = new TimeSpan(0, 1, 0);
using (TransactionScope tx =
new TransactionScope(TransactionScopeOption.Required, txo))
{
using (SqlConnection cn = new SqlConnection(cnStr))
{
cn.Open();
SqlCommand cmd = new SqlCommand(
"SELECT Enabled FROM PlayerRec WHERE Player='Jeffrey'", cn);
SqlDataReader dr = cmd.ExecuteReader(); //第一次讀取
dr.Read();
string s1 = dr[0].ToString();
dr.Close();
//在此設Debug中斷點,此時另開SSMS下指令
//UPDATE PlayerRec SET Enabled='Z' WHERE Player='Jeffrey',會呈現等待狀態
dr = cmd.ExecuteReader(); //再次讀取
dr.Read();
string s2 = dr[0].ToString();
Response.Write(string.Format("<li>Repeat Read: {0} => {1}", s1, s2));
tx.Complete();
//VS2010偵錯執行到這裡之後,SSMS裡的指令才會執行完成
}
}
}
我用以下的程式測試了ODP.NET的Transaction功能。程式先將資料表某筆資料的Score設成100,讀取第一次後,開另一條Connection將該筆資料Score改成500,再讀取第二次,最後比對兩次的讀取結果。其中有個bEnableTrans設定是否啟動Transaction。
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="Oracle.DataAccess" %>
<%@ Import Namespace="Oracle.DataAccess.Client" %>
string cnStr = "Data Source=XXX;User Id=YYY;Password=ZZZ";
private void SetData(int v)
using (OracleConnection cn = new OracleConnection(cnStr))
OracleCommand cmd = new OracleCommand(
"UPDATE TRNTEST SET Score = :p1 WHERE USERID='Jeffrey'", cn);
cmd.Parameters.Add("p1", OracleDbType.Decimal).Value = v;
Response.Write("<li>Set Score=" + v.ToString());
private void TestOracleTrans()
SetData(100); //一開始將Score設為100
OracleTransaction trn = null;
bool bEnableTrans = true; //設定是否要開Transaction
using (OracleConnection cn = new OracleConnection(cnStr))
if (bEnableTrans) //由旗標決定是否要開啟Transaction, 且設Serializable
trn = cn.BeginTransaction(System.Data.IsolationLevel.Serializable);
OracleCommand cmd = cn.CreateCommand();
cmd.CommandText = "SELECT Score FROM TRNTEST WHERE UserId='Jeffrey'";
OracleDataReader dr = cmd.ExecuteReader(); //第一次讀取
int s1 = Convert.ToInt32(dr[0]);
SetData(500); //另開一條連線將Score改成500
dr = cmd.ExecuteReader(); //再次讀取
int s2 = Convert.ToInt32(dr[0]);
Response.Write(string.Format("<li>Repeat Read: {0} => {1}", s1, s2));
if (trn != null) trn.Commit();
protected void Page_Load(object sender, EventArgs e)
實驗結果,未啟動Transaction時,兩次讀取結果是100, 500,待合預期! 但有趣的是啟動Serializable等級的Transaction後,兩次讀取結果是100, 100,但更改Score為500的動作沒有被擋下來,而實際上資料庫中的值在第二次讀取時已是500。換句話說,OracleTransaction的Serializable隔離只保障了前後讀取結果一致,並無保護資料不被外界更改的效果。