Sql server 重构ADO.NET-SqlTransaction与TransactionScope
我“继承”了一个小小的C#方法,它创建了一个ADO.NET SqlCommand对象,并在要保存到数据库的项目列表上循环(SQL Server 2005) 现在,使用传统的SqlConnection/SqlCommand方法,为了确保一切正常,这两个步骤(删除旧条目,然后插入新条目)被包装到ADO.NET SqlTransaction中Sql server 重构ADO.NET-SqlTransaction与TransactionScope,sql-server,ado.net,transactions,transactionscope,Sql Server,Ado.net,Transactions,Transactionscope,我“继承”了一个小小的C#方法,它创建了一个ADO.NET SqlCommand对象,并在要保存到数据库的项目列表上循环(SQL Server 2005) 现在,使用传统的SqlConnection/SqlCommand方法,为了确保一切正常,这两个步骤(删除旧条目,然后插入新条目)被包装到ADO.NET SqlTransaction中 using (SqlConnection _con = new SqlConnection(_connectionString)) { using (Sq
using (SqlConnection _con = new SqlConnection(_connectionString))
{
using (SqlTransaction _tran = _con.BeginTransaction())
{
try
{
SqlCommand _deleteOld = new SqlCommand(......., _con);
_deleteOld.Transaction = _tran;
_deleteOld.Parameters.AddWithValue("@ID", 5);
_con.Open();
_deleteOld.ExecuteNonQuery();
SqlCommand _insertCmd = new SqlCommand(......, _con);
_insertCmd.Transaction = _tran;
// add parameters to _insertCmd
foreach (Item item in listOfItem)
{
_insertCmd.ExecuteNonQuery();
}
_tran.Commit();
_con.Close();
}
catch (Exception ex)
{
// log exception
_tran.Rollback();
throw;
}
}
}
现在,我最近读了很多关于.NET TransactionScope类的文章,我想知道,这里的首选方法是什么?我是否会通过切换到使用来获得任何东西(可读性、速度、可靠性)
using (TransactionScope _scope = new TransactionScope())
{
using (SqlConnection _con = new SqlConnection(_connectionString))
{
....
}
_scope.Complete();
}
你喜欢什么,为什么
MarcMicrosoft建议使用事务范围: 基本思想是,事务范围将为您管理“环境事务上下文”。您首先与一个数据库对话,您有一个sql事务,然后与第2个数据库对话,事务被提升为分布式事务
using (SqlConnection _con = new SqlConnection(_connectionString))
{
using (SqlTransaction _tran = _con.BeginTransaction())
{
try
{
SqlCommand _deleteOld = new SqlCommand(......., _con);
_deleteOld.Transaction = _tran;
_deleteOld.Parameters.AddWithValue("@ID", 5);
_con.Open();
_deleteOld.ExecuteNonQuery();
SqlCommand _insertCmd = new SqlCommand(......, _con);
_insertCmd.Transaction = _tran;
// add parameters to _insertCmd
foreach (Item item in listOfItem)
{
_insertCmd.ExecuteNonQuery();
}
_tran.Commit();
_con.Close();
}
catch (Exception ex)
{
// log exception
_tran.Rollback();
throw;
}
}
}
事务范围确实适合您,因此您可以专注于系统的功能,而不是管道
编辑
当您使用事务范围时,该范围内的所有内容都将被事务覆盖。因此,保存一行代码,将命令连接到事务。这是一个可能的错误源,例如,如果有1000分之一的机会这一行被忽略,你会错过多少 编辑2
同意下面关于Triynko的评论。然而,我们使用实体框架,EF将自动关闭并重新打开连接,以便在事务中登记它。它并没有物理上关闭连接,更像是将其释放到连接池并获得一个新的连接,它可以是相同的连接,也可以是不同的连接 将现有代码切换到使用
TransactionScope
不会立即获得任何好处。由于它提供的灵活性,您应该在将来的开发中使用它。这将使将来在事务中包含ADO.NET调用以外的内容变得更容易
using (SqlConnection _con = new SqlConnection(_connectionString))
{
using (SqlTransaction _tran = _con.BeginTransaction())
{
try
{
SqlCommand _deleteOld = new SqlCommand(......., _con);
_deleteOld.Transaction = _tran;
_deleteOld.Parameters.AddWithValue("@ID", 5);
_con.Open();
_deleteOld.ExecuteNonQuery();
SqlCommand _insertCmd = new SqlCommand(......, _con);
_insertCmd.Transaction = _tran;
// add parameters to _insertCmd
foreach (Item item in listOfItem)
{
_insertCmd.ExecuteNonQuery();
}
_tran.Commit();
_con.Close();
}
catch (Exception ex)
{
// log exception
_tran.Rollback();
throw;
}
}
}
顺便说一句,在您发布的示例中,
SqlCommand
实例应该在中使用
块。请注意,使用事务范围有时我们会遇到很多问题,因为我们必须在服务器中执行许多设置,例如设置DTC,防火墙等。所以我建议使用SqlTransaction更节省实施成本。我更喜欢TransactionScope。它并不是在所有场景中都能完美工作,但在您描述的场景中,它是更好的解决方案
我的理由是:
此外,当DAL中有许多嵌套方法时,透明事务注册尤其有用——尽管您必须注意不要意外地将事务转换为需要DTC的分布式事务,如果使用多个SqlConnections,可能会发生这种情况,即使它们指向相同的分贝。好吧,也许现在就这样太晚了。。。但无论如何,我会写下来给那些感兴趣的人
因为我现在有了一个更好的图景,在我当前基于
SqlTransaction
的方法遇到了很多困难之后,我可能会改为使用TransactionScope
,正如我所见。。。TransactionScope
的主要优点是它可以很容易地在业务层使用 也很晚了。。。即使数据库不支持嵌套事务,您也可以轻松地在业务层中拥有“嵌套”事务。NET控制嵌套并最终使用一个数据库事务(至少在SQL Server 2008+的情况下)。这使得在原始意图之外重用数据访问代码,作为更大事务的一部分变得更加容易。OK,。谢谢你。但是,如果我重构一切(不仅仅是这一个示例),使用TransactionScope()而不是ADO.NET嵌入式事务,我会从中受益吗?只要问一下付出的努力是否值得——我从中获得了什么?“当您使用事务范围时,该范围内的所有内容都会被事务覆盖。”不,所有内容都不会被覆盖。只有在作用域中登记的连接上发出的命令才受作用域的影响。如果在作用域中打开连接,则会自动在作用域中登记连接,否则,在通过调用SqlConnection.EnclestTransaction创建作用域后,需要在作用域中手动登记已打开的连接。例如,如果打开连接,则创建事务范围。。。您的任何命令都不会参与该事务。@Triynko:您对相应MSDN文章的评论非常好!好的,谢谢John-是的,你是对的-这个例子在using()块中没有SqlCommand(但是!)-这是正在进行的工作:-)re:SqlCommand在using中,除了垃圾收集之外还有其他原因吗,例如:SQLConnection的Dispose()调用Close()方法,Dispose()也是如此调用任何SQLCommand方法?如果一个类实现了IDisposable
,并且您创建了这个类的实例,那么您应该对它调用Dispose。最简单的方法是使用块。我发现最好养成一个习惯,总是执行一个。
using (SqlConnection _con = new SqlConnection(_connectionString))
{
using (SqlTransaction _tran = _con.BeginTransaction())
{
try
{
SqlCommand _deleteOld = new SqlCommand(......., _con);
_deleteOld.Transaction = _tran;
_deleteOld.Parameters.AddWithValue("@ID", 5);
_con.Open();
_deleteOld.ExecuteNonQuery();
SqlCommand _insertCmd = new SqlCommand(......, _con);
_insertCmd.Transaction = _tran;
// add parameters to _insertCmd
foreach (Item item in listOfItem)
{
_insertCmd.ExecuteNonQuery();
}
_tran.Commit();
_con.Close();
}
catch (Exception ex)
{
// log exception
_tran.Rollback();
throw;
}
}
}