使用BeginTransaction和Dapper.IDbConnection的正确方法

时间:2014-07-09 10:03:29

标签: c# .net orm 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()...行,处理交易的好方法是什么?我想应该有一些正确的方法。

4 个答案:

答案 0 :(得分:52)

手动打开连接不是"不良做法&#34 ;; 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上的扩展方法工作方式类似,因为a transaction always exposes .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();
}

答案 1 :(得分:6)

你不应该打电话

cn.Close();

因为使用块也会尝试关闭。 对于事务部分,是的,您也可以使用TransactionScope,因为它不是与实体框架相关的技术。 看看这个SO答案:https://stackoverflow.com/a/6874617/566608 它解释了如何在事务范围中登记您的连接。 重要的方面是:连接会自动登记在事务IIF中,您打开范围内的连接

答案 2 :(得分:3)

查看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);
    }
}

答案 3 :(得分:0)

我们实现了这种Uow模式,但我们遇到异步调用问题。有时在_transaction.Dispose()我们收到连接不支持MultipleActiveResultSets。