锁定 DataSet 中的所有 DataTable 以执行安全更新

Posted

技术标签:

【中文标题】锁定 DataSet 中的所有 DataTable 以执行安全更新【英文标题】:Locking all DataTables in DataSet to perform safe Update 【发布时间】:2013-09-20 23:17:27 【问题描述】:

我正在开发一个应用程序,其中有一个数据集(它代表一个 Access 数据库),其中包含多个数据表。 然后我有线程同时在各种数据表中插入/编辑/删除行。

有时我有一个线程将数据表更改提交到数据库(在每个数据表上调用更新方法)。我的问题是数据表有一些被违反的关系。让我举个例子,我在将更改提交到数据库的线程中有这个:

If DS.Tables.Contains("TableA") Then SyncLock DS.Tables("TableA") : TableADataAdapter.Update(DS.Tables("TableA")) : End SyncLock
If DS.Tables.Contains("TableB") Then SyncLock DS.Tables("TableB") : TableBDataAdapter.Update(DS.Tables("TableB")) : End SyncLock

TableA 是 TableB 的父级,因此有一个 ID 列,TableB 中的每条记录都必须在 TableA 中具有相应的值

有时在 TableA 更新后,一个线程会在 TableA 和 TableB 中插入一条记录,当我更新 TableB 时,TableA 中缺少父记录并且关系中断(引发异常)

我已经尝试锁定DataSet,看看DataSet中的所有DataTables是否都会变成锁定对象,但异常仍然存在。

SyncLock DS: Do The Updates : End SyncLock

我的问题是:有什么方法可以同时锁定所有数据表,以便我可以安全地更新数据库?

感谢您的建议

【问题讨论】:

实际对DataSet 进行更改的代码是否也在同一种SyncLock 块中? 谢谢史蒂文。是的,每当我在数据表中插入/更新/删除一行时,我都会这样做:SyncLock DS.Tables("TableA") : Insert/Update/Delete : End SyncLock 当你把它改成SyncLock DS时,你是不是也把那些地方也改成了DS 不,我不想在每次 Datatable 更改时始终锁定整个 DataSet,我只想在更新完成时锁定整个数据集。 SyncLock 不能这样工作。调用SyncLock 不会锁定你给它的对象。相反,SyncLock 会阻止封闭的代码,因此它不会与同一对象上的任何其他代码 SyncLock'同时执行。因此,您需要在任何地方对DS 进行同步锁定,或者您需要研究更高级的同步技术,例如AutoResetEventWaitHandle 等。不幸的是,我对这些技术没有太多经验,所以我不是能进一步帮助您的最佳人选。 【参考方案1】:

所以,最终的解决方案是锁定每个 DataTable 的单个对象,然后在我想更新时将它们全部锁定:

在不同线程中修改行时:

SyncLock DataTableALock
    Insert/Edit/Delete Rows
End SyncLock

SyncLock DataTableBLock
    Insert/Edit/Delete Rows
End SyncLock

当我想提交更改时:

SyncLock DataTableALock
    SyncLock DataTableBLock
        Commit Changes
    End SyncLock
End SyncLock

我认为这段代码是安全的,并且每次我想更改一行时都不会锁定整个数据集(只是单个 DataTables)。它只是丑陋,但我可以忍受。

【讨论】:

那肯定行得通。它很丑,但它又好又简单。我相信坚持你所理解的。涉足您没有完全掌握的技术是很危险的。因此,这听起来是目前最适合您的解决方案。但是,如果您要经常使用多线程,我建议您花一些时间认真尝试了解更多有关更高级同步技术的信息。 +1,虽然是一个简单的解决方案。 感谢@Steven Doggart 指出这一点。我将维护这个解决方案并最终使用这个问题来了解其他似乎更适合“复杂情况”的同步技术。【参考方案2】:

总而言之,我需要更新我的所有数据表,同时我知道关系得到尊重,为此我需要在此过程中停止/停止对数据表的任何更改。

这个问题的一个可能的解决方案,但一个糟糕的解决方案,并且有很多可能出错,是放置一个表示更新的公共标志,然后在我进行数据表更改的每个地方检查该标志值.像这样的:

If UpdateIsRunnig = True Then 
   Do While UpdateIsRunnig = True : Loop
End If
Insert/Edit/Delete Rows in Datatable

这是非常危险的,它不能保证我在将更改提交到后端数据库时不会发生更改。 所以这是一个糟糕的和部分的解决方案。 我会尝试弄清楚是否有更好的方法来做到这一点。 非常感谢任何帮助。

编辑: 正如 PHeiberg 指出的那样,我唯一真正当前的解决方案是拥有一个 Global SyncLock 对象,我在对 DataSet 所做的每次更改中都使用该对象,然后在更新中使用它。像这样:

SyncLock GlobalDataSetLock
    Insert/Edit/Delete Rows
End SyncLock

关于更新:

SyncLock GlobalDataSetLock
    Commit Changes to DataBase using "Update"
End SyncLock

这是一个解决方案,它确实解决了问题,但是性能问题是巨大的。正如我在评论中所说,我真的不需要锁定 DataTable A 来对 DataTable B 进行更改,因此锁定整个 DataSet 只是为了稍后进行更新真的很令人沮丧。

【讨论】:

哎哟!这是一个可怕的“解决方案”,弊大于利。用您问题中的 Monitor/SyncLock 替换标志,并放弃紧密循环。 感谢@PHeiberg 指出这一点,我对提议的答案进行了编辑。

以上是关于锁定 DataSet 中的所有 DataTable 以执行安全更新的主要内容,如果未能解决你的问题,请参考以下文章

dataset datatable datacolums datarow

将 DataTable 从一个 DataSet 复制到另一个

asp.net中dataset如何获得多个datatable表的集合(datatable表结构相同),以实现在页面上显示所有表?

使用 json.net 中的属性序列化 DataSet/DataTable

android中怎么使用dataset和datatable

VS 2010 调试 C#代码中的 datatable 或 dataset 报错 System.Exception: 函数计算超时。