C# 使用Dapper.IDbConnection的BeginTransaction的正确方法
在Dapper中使用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
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);
}
}