C# 使用Dapper.IDbConnection的BeginTransaction的正确方法

C# 使用Dapper.IDbConnection的BeginTransaction的正确方法,c#,.net,orm,dapper,idbconnection,C#,.net,Orm,Dapper,Idbconnection,在Dapper中使用BeginTransaction()和IDbConnection的正确方法是什么 我创建了一个方法,我必须在其中使用BeginTransaction()。这是代码 using (IDbConnection cn = DBConnection) { var oTransaction = cn.BeginTransaction(); try { // SAVE BASIC CONSULT DETAIL var oPara

在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()…
的情况下处理事务的好做法是什么?我想应该有合适的方法来解决这个问题。

你不应该打电话

cn.Close();
因为正在使用的块也将尝试关闭。 对于事务部分,您也可以使用TransactionScope,因为它不是与实体框架相关的技术。 看看这个答案: 它解释了如何在事务范围中登记您的连接。
重要的方面是:连接自动登记在事务IIF中,如果您在作用域内打开连接

手动打开连接不是“坏习惯”;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
上创建类似的扩展方法,因为;这将允许:

tran.Execute(sql, args);
但这在今天并不存在

TransactionScope
是另一个选项,但具有不同的语义:这可能涉及LTM或DTC,具体取决于。。。好吧,主要是运气。在
IDbTransaction
周围创建一个不需要
try
/
catch
-更像
TransactionScope
的包装器也是很有诱惑力的;类似于(这也不存在):

看看这个解决方案,它简单但功能强大,使用存储库模式实现,并且考虑到了
Dapper事务

下面代码中的
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);
    }
}

是的,你是对的,对不起,我忘了把它取下来。因此,您提供的链接说您可以使用TransactionScope和Dapper,但您必须编写以下代码-con.Open()。那么这是一个好的做法吗?当然,在使用itFFR之前必须打开连接:这是建议的,但作为PR被拒绝:(Marc也参与了讨论。被拒绝的主要原因是同步/异步之间已经存在重复-为事务添加扩展方法将导致所有方法重复4次。@Marc Gravel-在回滚的情况下,是否必须显式调用
tran.rollback
?事务是否不回滚d自动返回到dispose?很好。对解决方案有几个问题。如果不想使用事务,比如说用于常规的select查询,该怎么办?那么,据我所知,sql将在提交()后为事务生成代码,或者什么?为什么我需要执行BeginTransaction()呢如果我不在查询中使用它?它会影响我不需要事务处理的查询的性能吗?请不要误解我。我只是想在开始在生产中使用它之前澄清所有事情。因此,我认为最好添加标志(UseTransation=false)。在这种情况下,创建unitOfWork实例时,我们可以选择我们需要的策略。我说的对吗?当您的查询只是
SELECT
时,您不需要
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);
    }
}