【问题标题】:SqlDataAdapter.Fill() within a SqlTransaction - is this a bad practice?SqlTransaction 中的 SqlDataAdapter.Fill() - 这是一个不好的做法吗?
【发布时间】:2011-09-11 20:12:27
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 提供您的编码,然后有可能解决您的问题。

标签: c# sql-server ado.net transactions dataadapter


【解决方案1】:

不是不好的做法。要记住的一件事是,所有语句都将使用隐式事务,它们将在语句结束时自动提交。那是一个 SELECT(如 Fill 使用的 SELECT)将始终使用事务,问题是它是否必须自己启动它还是将使用现有的。

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

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

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

【讨论】:

  • 非常感谢您的回答!好吧,我是 TransactionScope 的新手,所以我应该更多地调查它。您阅读了我的想法以及 Oracle ......虽然他们的 ODP.Net 可能仍然存在 TransactionScope 的问题,因为我已经阅读了一些论坛......迟早我们也需要将其迁移到 Oracle。你对我的“edit2”有什么线索吗?我只做了一个简单的测试程序,开始一个事务,并在内部调用 10000 一个 proc 来插入 x(fill),获取 id_x(fill),插入 y(fill-including id_x)。当我启动应用程序 2 次(这在 onFormLoad 中)时,它抛出了前任。
  • 打开一个新问题。捕获死锁图 (msdn.microsoft.com/en-us/library/ms190465.aspx) 并将其附加到问题(作为 XML,而不是图的图片!),并添加对表 索引的精确描述。
  • 好的,谢谢。我现在看看能不能重现它,如果不能......明天。
  • 好吧,它在我的私人机器 (SQLServer2008) 上运行良好,同时在 SqlTransaction 中使用 ExecuteReader() 和 Fill() ......虽然在工作时 (SQLServer2005) 它失败了。除此之外,我要感谢您让我相信 TransactionScope。也许我最终会去争取它......但我需要更多地检查它,以便更好地理解它。多谢!你是数据库的主人! :)
  • 嗨莱姆斯。我有这个问题:stackoverflow.com/questions/6308951/…。您是否知道解决我的问题的好链接?提前谢谢你。
【解决方案2】:

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

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

【讨论】:

  • [[填充只是让整个过程保持这些资源锁定的时间更长。]] 为什么?
  • 事务中的任何更改都会锁定特定资源(表/页面/记录)。如果您在 udpate 之间进行填充,则第一个 udpate 会锁定资源,并且必须等待填充发生并等待更长时间,直到您提交或回滚事务。最佳做法是在创建事务之前排列所有数据,然后尽快执行所有更改。
  • 似乎它甚至不等待,而是抛出异常进入死锁(见我的编辑2)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
相关资源
最近更新 更多