簡體   English   中英

在 Dapper.IDbConnection 中使用 BeginTransaction 的正確方法

[英]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 交易的預期方式。

  1. 將您的IDbTranasction傳遞給您的普通 Dapper 調用。

    前:

     var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"});

    后:

     var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"}, transaction=tx);
  2. 使用 Dapper 添加到IDbTransaction本身的新.Execute擴展方法:

     tx.Execute(sql, new {CustomerName = "Mark"});

注意:變量tx來自IDbTransaction tx = connection.BeginTransaction();

這就是你應該如何使用 Dapper 的事務; 它們都不是 TransactionScope。

獎勵閱讀

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM