SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
看到有人在这里提到想要强行更新数据库,遇到了困难。主要是本地的数据缓冲确实没有改变过,但是可能别的客户端改变了数据,现在想让这个本地的数据缓冲重新刷新到数据库当中。这就出现了因为本地数据没有任何改变,所以没有办法利用DataAdapter进行数据库更新的问题。

这个问题确实比较头痛,研究了半天System.Data.dll里面的相关IL,得出了一些结论。这些结论没有经过任何形式的验证,所以不保证成功,但是也许为大家提供了一个可能可行的方案。结论如下:

DataAdapter.Update一共有五个重载的版本,OleDbAdapter/SqlDbAdapter派生自DbDataAdapter,DbDataAdapter派生自DataAdapter,因此我们主要研究DataAdapter.Update,期望从中找出线索。实际上这五个版本中的四个实际最终都是调用另外一个版本。这个版本是
.method family hidebysig newslot virtual
        instance int32  Update(class System.Data.DataRow[] dataRows,
                               class System.Data.Common.DataTableMapping tableMapping) cil managed
其他的版本都是通过给出DataSet(或者加上Table的名字/string)、DataTable、DataRow,然后函数自己获得rows和tableMapping,然后调用这个(dataRows, tableMapping)的版本的。
在这个版本里面,我看到实际上是通过循环判断dataRows里面的每一个元素DataRow的get_RowState,分别进行添加、删除、更新操作,或者不进行操作。RowState的返回值可能是:Detached, Unchanged, Added, Deleted, Modified。也就是说,如果某一个Row的情况是Modified,那么就自动用UpdateCommand,其余类推。如果我们能够改变get_RowState的行为,我们就可能可以达到目的。那么这五种情况是怎么得出来的呢?我们需要看DataRow的结构。

在DataRow里面看get_RowState,这个似乎不存在一个m_RowState这样的东西,这个函数是根据两个值oldRecord和newRecord(都是int32)来进行判断,进而得出返回值。下面给出逻辑:(超级伪代码,不要介意)
if old == -1 && new == -1 then
  ret Detached
else if old == new then
  ret Unchanged
else if old == -1 then
  ret Added
else if new == -1 then
  ret Deleted
else
  ret Modified
end if

看到这么复杂的代码,大家已经想要跳过去了。对,我看了也觉得很头痛,不想研究下去了。可是不行,因为RowState这个属性并不是virtual的,没有办法override。所以没办法跳过去,只好研究一下oldRecord/newRecord是什么含意,看看能不能从这里去“黑”掉DataRow。关于oldRecord/newRecord的含义,在DataTable里面可以找到线索(SetNewRecord函数)。具体的分析我就不说了,想知道为什么就得自己看那个函数(以及相关函数)的IL。大概是这样的:
DataTable利用一个叫做RecordManager的东西管理所有的DataRow,以此管理数据版本问题。也就是说,新旧两个版本的DataRow都会保存在RecordManager里面,如果存在的话。RecordManager里面通过一个DataRow[]数组保存所有的数据,因此很明显oldRecord/newRecord的含义就是在DataRow[]数组里面的索引。很明显我们不能够随便进行什么修改,但是并不是完全不能够修改。下面是我的一些猜测,请注意是猜测,也就是没有经过仔细验证,甚至不是通过仔细看IL推导出来的结论:

1、如果你想Delete,那么修改new = -1
2、如果你想Insert,那么修改old = -1
3、如果你想Update,那么修改old != -1 && old != new, 最简单的办法就是让old = new + 1

可是这么做必须有一个前提,那就是你在进行操作之前必须首先保存原始的old/new值,并且在操作完成之后恢复,否则可能产生不可预料的后果。怎么修改呢?只好应用reflection了,虽然效率有点低下,但是那样也是没有办法的办法(如果非要通过DataAdapter来强行更新的话)。因为DataRow的RowState不能够override,而old/new值是assembly的而没有protected,因此无法通过继承获得。

如果说可以确保数据库没有进行添加/删除的操作,而仅仅是进行了修改,那么只需要强行Update就可以了,问题是如果数据库混合了添加/删除/修改操作,那怎么办?似乎会比较麻烦,也许需要通过Fill另外一张表,然后进行比较才能够获得信息,甚至也许这样也不可能获得足够的信息。如果是混合了这样的操作的话,那么我建议通过将数据库的那个Table清空然后在Insert所有DataRow来达到强行WriteBack的目的。(这个时候已经不能够说是Update了。)

切记切记,一定要保存和恢复old/new值,否则肯定会出问题的。
posted on 2004-04-27 13:36  Sumtec  阅读(2338)  评论(6编辑  收藏  举报