由于我有一个带有DataSet QueryDB(string spName, DBInputParams inputParams)
方法的“DB util”类,我将其用于所有对数据库的调用,我想重用此方法以支持事务调用。
所以,最后我将在SqlTransaction中有一个SqlDataAdapter.Fill。这会是一种不好的做法吗?因为我很少在事务中看到DataAdapter.Fill的使用,更常见的是ExecuteReader()。有没有捕获?
Edit1:事情是我的事务内部经常需要检索一些数据(例如自动ID)...这就是为什么我想把它作为DataSet。
Edit2:奇怪的是,当我在2个不同进程的for循环(10000)中使用此方法时,我得到“事务(进程ID 55)在锁定资源上与另一个进程死锁并具有被选为死锁受害者。重新运行。“ 。这是正确的行为吗?
编辑3 :(编辑2的答案)我使用IDENT_CURRENT('XTable')
这是错误的来源。在我回到SCOPE_IDENTITY()
之后,一切都已经解决了。
答案 0 :(得分:2)
这是一种不好的做法,因为当事务处于打开状态时,您进行更改的记录/页面/表在事务持续期间被锁定。填充只会使整个过程使这些资源锁定更长时间。根据您的sql设置,这可能会阻止对这些资源的其他访问。
那就是说,如果有必要,就必须实现惩罚。
答案 1 :(得分:2)
不是一种不良做法。要记住的一件事是所有语句都将使用一个隐式事务,它们将在语句结束时自动提交。这是一个SELECT(如填充中使用的SELECT)将总是使用一个事务,问题是它是否必须自己启动它或者它将使用现有的事务。
SELECT在隐式事务中与明确事务中获取的锁的数量,类型和持续时间之间是否存在任何差异?在默认事务模型(READ COMMITTED隔离) NO 下,没有。行为是相同的,无法区分。在其他隔离级别(可重复读取,可序列化)下存在差异,但这是发生所需更高隔离级别的必要差异,并且使用显式事务是 only 方式来实现此期望的隔离必要时,水平。
此外,如果SELECT必须读取待处理(尚未提交)的事务的影响,如在您的示例中(回读生成的ID),那么没有其他方式。 SELECT 必须是生成ID的事务的一部分,否则它将无法看到这些未提交的ID!
但请注意谨慎。我相信您可以使用一个很棒的工具,可以让所有这些事务处理变得更加轻松:System.Transactions。所有ADO.Net代码都是系统事务感知的,如果您只是声明TransactionScope
,它将自动将任何连接和命令注册到待处理的事务中。也就是说,如果函数Foo声明TransactionScope
然后调用函数Bar,如果Bar执行任何 ADO.Net操作,它将自动成为Foo中声明的事务的一部分,即使Bar执行了没有明确地。 TransactionScope
挂钩到线程上下文中,Bar调用的所有ADO.Net调用将自动检查此上下文并使用它。请注意,我的意思是任何 ADO.Net调用,包括Oracle提供者调用。虽然有一个警告:using new TransactionScope() Considered Harmful:TransactionScope
的默认构造函数将创建一个可序列化的事务,这是过度的。您必须使用带有TransactionOptions
对象的构造函数,并将行为更改为ReadCommitted。与TransactionScope
的第二个问题是你必须非常小心你如何管理连接:如果你在一个范围内打开多个连接,那么他们将注册一个分布式事务,这很慢,需要MSDTC已配置,并导致所有类型的难以调试错误。但总的来说,我认为使用TransactionScope
超出问题的好处,并且结果代码总是比明确地传递IDbTransaction
更优雅。