在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()...
行,处理交易的好方法是什么?我想应该有一些正确的方法。
答案 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。