[英]Proper way of using BeginTransaction with Dapper.IDbConnection
在 Dapper 中使用BeginTransaction()
和IDbConnection
的正確方法是什么?
我創建了一個方法,我必須在其中使用BeginTransaction()
。 這是代碼。
using (IDbConnection cn = DBConnection)
{
var oTransaction = cn.BeginTransaction();
try
{
// SAVE BASIC CONSULT DETAIL
var oPara = new DynamicParameters();
oPara.Add("@PatientID", iPatientID, dbType: DbType.Int32);
..........blah......blah............
}
catch (Exception ex)
{
oTransaction.Rollback();
return new SaveResponse { Success = false, ResponseString = ex.Message };
}
}
當我執行上述方法時 - 我遇到了異常 -
無效操作。 連接已關閉。
這是因為您無法在連接打開之前開始事務。 所以當我添加這一行時: cn.Open();
,錯誤得到解決。 但是我在某處讀到手動打開連接是不好的做法!! Dapper 僅在需要時打開連接。
在實體框架中,您可以使用TransactionScope
。
所以我的問題是在Dapper 中不添加行cn.Open()...
情況下處理事務的好做法是什么? 我想應該有一些適當的方法。
手動打開連接並不是“壞習慣”; dapper 使用打開或關閉的連接是為了方便,僅此而已。 一個常見的問題是人們的連接保持打開、未使用、太長時間沒有將它們釋放到池中——但是,在大多數情況下這不是問題,你當然可以這樣做:
using(var cn = CreateConnection()) {
cn.Open();
using(var tran = cn.BeginTransaction()) {
try {
// multiple operations involving cn and tran here
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
}
注意 dapper 有一個可選參數可以傳入交易中,例如:
cn.Execute(sql, args, transaction: tran);
我實際上很想在IDbTransaction
上創建類似工作的擴展方法,因為事務總是公開.Connection
; 這將允許:
tran.Execute(sql, args);
但這在今天並不存在。
TransactionScope
是另一種選擇,但具有不同的語義:這可能涉及 LTM 或 DTC,具體取決於……好吧,主要是運氣。 圍繞IDbTransaction
創建一個不需要try
/ catch
的包裝器也很誘人——更像是TransactionScope
工作原理; 類似的東西(這也不存在):
using(var cn = CreateConnection())
using(var tran = cn.SimpleTransaction())
{
tran.Execute(...);
tran.Execute(...);
tran.Complete();
}
你不應該打電話
cn.Close();
因為 using 塊也會嘗試關閉。 對於事務部分,是的,您也可以使用 TransactionScope,因為它不是與實體框架相關的技術。 看看這個 SO 答案: https : //stackoverflow.com/a/6874617/566608它解釋了如何在事務范圍內登記您的連接。 重要的方面是:如果您在作用域內打開連接,連接會自動登記在事務 IIF 中。
看看Tim Schreiber解決方案,它簡單而強大,使用存儲庫模式實現,並考慮了Dapper Transactions
。
下面代碼中的Commit()
顯示了它。
public class UnitOfWork : IUnitOfWork
{
private IDbConnection _connection;
private IDbTransaction _transaction;
private IBreedRepository _breedRepository;
private ICatRepository _catRepository;
private bool _disposed;
public UnitOfWork(string connectionString)
{
_connection = new SqlConnection(connectionString);
_connection.Open();
_transaction = _connection.BeginTransaction();
}
public IBreedRepository BreedRepository
{
get { return _breedRepository ?? (_breedRepository = new BreedRepository(_transaction)); }
}
public ICatRepository CatRepository
{
get { return _catRepository ?? (_catRepository = new CatRepository(_transaction)); }
}
public void Commit()
{
try
{
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
finally
{
_transaction.Dispose();
_transaction = _connection.BeginTransaction();
resetRepositories();
}
}
private void resetRepositories()
{
_breedRepository = null;
_catRepository = null;
}
public void Dispose()
{
dispose(true);
GC.SuppressFinalize(this);
}
private void dispose(bool disposing)
{
if (!_disposed)
{
if(disposing)
{
if (_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
if(_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
_disposed = true;
}
}
~UnitOfWork()
{
dispose(false);
}
}
我們實現了這種Uow模式,但我們遇到異步調用問題。 有時在_transaction.Dispose()我們收到連接不支持MultipleActiveResultSets。
有兩種使用 Dapper 交易的預期方式。
將您的IDbTranasction
傳遞給您的普通 Dapper 調用。
前:
var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"});
后:
var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"}, transaction=tx);
使用 Dapper 添加到IDbTransaction
本身的新.Execute
擴展方法:
tx.Execute(sql, new {CustomerName = "Mark"});
注意:變量tx
來自IDbTransaction tx = connection.BeginTransaction();
這就是你應該如何使用 Dapper 的事務; 它們都不是 TransactionScope。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.