2017-04-10 44 views
0

假設我有一個數據庫的方法,看起來像這樣:測試不使用事務的SQL方法?

public void insertRow(SqlConnection c) 
{ 
    using (var cmd = new SqlCommand("insert into myTable values(@dt)",c)) 
    { 
     cmd.Parameters.Add(new SqlParameter("@dt",DbType.DateTime)).Value = DateTime.Now; 
     cmd.ExecuteNonQuery(); 
    } 
} 

現在假設我想測試這種方法。所以,我寫試圖包裹在一個事務中這種方法,這樣我可以測試插入的結果後回滾變化的測試用例:

public void testInsertRow() 
{ 
    SqlConnection c = new SqlConnection("connection.string.here"); 
    SqlTransaction trans = c.BeginTransaction(); 
    insertRow(); 
    // do something here to evaluate what happened, e.g. query the DB 
    trans.Rollback(); 
} 

然而,這無法工作,因爲:

當分配給命令的連接處於未決的本地事務中時,ExecuteNonQuery需要該命令進行事務。該命令的Transaction屬性尚未初始化。

有沒有辦法做到這一點,而無需重寫每一個數據庫的方法來接受一個事務,然後重寫每一個呼叫傳遞null成交易的方法?

例如,這會工作:

public void insertRow(SqlConnection c, SqlTransaction t) 
{ 
    using (var cmd = new SqlCommand("insert into myTable values(@dt)",c)) 
    { 
     if (t != null) cmd.Transaction = t; 
     cmd.Parameters.Add(new SqlParameter("@dt",DbType.DateTime)).Value = DateTime.Now; 
     cmd.ExecuteNonQuery(); 
    } 
    c.Close(); 
} 

但後來,我必須要麼重寫每一個調用數據庫的方法包括null參數,或寫覆蓋簽名爲每一個數據庫的方法自動通過null,例如

public void insertRow(SqlConnection c) { insertRow(c, null); } 

允許基於事務的數據庫調用測試的最佳方式是什麼?

+2

[TransactionScope的(https://msdn.microsoft.com/en-us/library/ee818746(V = vs.110)的.aspx) –

+0

在我看來,你回滾之前關閉您的連接。 – Lunyx

+0

@Lunyx你是對的,那不應該在那裏。刪除它當然不能解決問題,但感謝您指出 - 編輯。 – fdmillion

回答

2

可以使用TransactionScope在自動添加到交易

public void testInsertRow() 
{ 
    using(TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew)) 
    { 
     SqlConnection c = new SqlConnection("connection.string.here"); 
     insertRow(c); 
     // do something here to evaluate what happened, e.g. query the DB 

     //do not call scope.Complete() so we get a rollback. 
    } 
} 

連接現在,這會導致如果您有運行多個並行測試,測試,以阻止對方。如果您的數據庫設置爲支持它,您可以執行快照隔離,以便併發測試的更新不會互相鎖定。

public void testInsertRow() 
{ 
    using(TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, 
                 new TransactionOptions(IsolationLevel = IsolationLevel.Snapshot)) 
    { 
     SqlConnection c = new SqlConnection("connection.string.here"); 
     insertRow(c); 
     // do something here to evaluate what happened, e.g. query the DB 

     //do not call scope.Complete() so we get a rollback. 
    } 
} 
+0

似乎很好地工作。我只注意到,如果我在範圍之外聲明連接,範圍似乎不起作用。所以如果我想回滾然後執行更多操作,看起來像我必須打開一個新的連接。沒什麼大不了的,只是注意到了。 – fdmillion