1

由于我有一个“DB util”类,DataSet QueryDB(string spName, DBInputParams inputParams)其中包含用于所有对数据库的调用的方法,因此我想重用此方法以支持事务调用。

所以,最后我将在 SqlTransaction 中有一个 SqlDataAdapter.Fill。这会是一个坏习惯吗?因为我很少在事务中看到 DataAdapter.Fill 的用法,更常见的是 ExecuteReader()。有什么问题吗?

Edit1:问题是在我的事务中经常需要检索一些数据(例如自动ID)......这就是为什么我想将它作为DataSet 获取。

Edit2:奇怪的是,当我在来自 2 个不同进程的 for 循环(10000)中使用这种方法时,我得到“事务(进程 ID 55)与另一个进程在锁定资源上死锁,并被选为死锁受害者。重新运行事务。” . 这是正确的行为吗?

Edit3:(Edit2 的答案)我使用IDENT_CURRENT('XTable')的是错误的来源。我回去后SCOPE_IDENTITY(),一切都解决了。

4

2 回答 2

2

不是一个坏习惯。要记住的一件事是,所有语句都将使用隐式事务,当语句结束时它们将自动提交。那是一个 SELECT(就像在 Fill 使用的 SELECT 中一样)将始终使用一个事务,问题是它是必须自己启动它还是使用现有的。

SELECT 在隐式事务与显式事务中获取的锁的数量、类型和持续时间之间有什么区别吗?在默认事务模型(READ COMMITTED 隔离)下NO,没有。行为相同且无法区分。在其他隔离级别(可重复读取、可序列化)下存在差异,但这是发生所需更高隔离级别的必要差异,并且在必要时使用显式事务是实现此所需隔离级别的唯一方法。

此外,如果 SELECT 必须读取待处理(尚未提交)的事务的影响,如您的示例(读回生成的 ID),那么没有其他方法。SELECT必须是生成 ID 的事务的一部分,否则它将无法看到那些未提交的 ID!

不过要注意。我相信你有一个很棒的工具可以让所有这些事务处理变得更加容易:System.Transactions。所有 ADO.Net 代码都是系统事务感知的,如果您简单地声明一个TransactionScope. 也就是说,如果函数 Foo 声明了 aTransactionScope然后调用函数 Bar,如果 Bar 执行任何ADO.Net 操作,它将自动成为 Foo 中声明的事务的一部分,即使 Bar没有显式执行任何操作。与TransactionScope线程上下文挂钩,Bar 调用的所有 ADO.Net 调用将自动检查此上下文并使用它。请注意,我的意思是任何ADO.Net 调用,包括 Oracle 提供程序的调用。唉,虽然有一个警告:使用 new TransactionScope() 被认为是有害的:默认构造函数TransactionScope将创建一个可序列化的事务,这太过分了。您必须使用接受TransactionOptions对象的构造函数并将行为更改为 ReadCommitted。第二个问题TransactionScope是您必须非常小心如何管理连接:如果您在一个范围内打开多个连接,那么它们将被注册到分布式事务中,这很慢并且需要配置 MSDTC,并导致各种难以调试的错误。但总的来说,我认为使用的好处TransactionScope超过了问题,并且生成的代码总是比IDbTransaction显式传递更优雅。

于 2011-06-09T16:54:15.460 回答
2

这是一个不好的做法,因为当事务打开时,您对其进行更改的记录/页面/表在事务期间被锁定。填充只是使整个过程使这些资源锁定更长的时间。根据您的 sql 设置,这可能会阻止对这些资源的其他访问。

也就是说,如果有必要,那就有必要,只要意识到这样做的惩罚。

于 2011-06-09T15:54:34.227 回答