大概各位在使用MIDAS的时候都会遇到这样一个问题,很是不明白。
以前我也一直不明白,直到今天,才多少有一些清除是为什么:明明没有别的用户修改数据,为什么会说数据已经被修改了呢?而当你在修改后再刷新一次就不会出现这样的问题了呢?其实,报:record has been changed by an other user一点都没有错!这个修改者不是别人,正是数据库本身!!!如果你使用SQL SERVER,你可以用事件探查器跟踪所有的SQL语句。解决这个问题的最关键是:Update产生的SQL语句的WHERE条件!如果你设定DataSetProvider的更新模式是updateWhereAll,那么你所有的字段都用来作为WHERE的搜索条件!!如果更新模式是updateKeyOnly,那么使用键值字段作为WHERE的搜索条件。关键是:如果在添加记录时候,某些字段是数据库自动产生的,最典型的是ID字段,自动增长,那么中间层可以获取新增记录的ID,但是,这个值并不会传回给ClientDataSet!!!!!!!!!!!!!!!!!然而,如果ClientDataSet的更新语句中使用了数据库自动更改的字段,那么所产生的SQL语句的WHERE部分必然是不对的!!!!ClientDataSet会和中间层的AdoDataSet(如果使用ADO)比较数据,因为AdoDataSet已经从数据库获取了数据库自动更新过的字段,但是ClientDataSet没有,所以两边比较不匹配!从而会出现“Record has been changed by another user”的错误!使用触发器也会出现这样的问题!!解决方法:我还没有想到最好的方法,但是有一点是可以肯定的:在自动产生的WHERE条件中不能含有数据库自动更新的字段。如ID由数据库自动产生,那么,就不要让他自动产生。

解决方案 »

  1.   

    楼主的分析有错啊, 为了不会误导大家指正一下1. 自动增长字段中间层可以获取新增记录的ID?
       说得不明确啊, 怎样得到? 中间层实现上在 UpdateRecord(注意是Record)后是不会知道自动 ID 的值的, 实际上是自增长ID 可以在 AfterUpdateRecord 上写到 SELECT MAX(ID) AS MAX_ID FROM TABLE 得到, 这时因为你的事务, 所以得到的值一定要是刚才加入的那条的ID, 但得到这个东东有什么意义, 看2..2. 这个值并不会传回给ClientDataSet?
       回答是可以回伟给 cds 的, 在 dsp 的 Options 中 poPropogateChanges 设成 true, ok, 在 1 中的 AfterUpdateRecord 中写以 DeltaDS.FieldByName('ID').NewValue = MaxID, 你在客户机看看, ID 是不是回传了3. "ClientDataSet会和中间层的AdoDataSet(如果使用ADO)比较数据,因为AdoDataSet已经从数据库获取了数据库自动更新过的字段" 这句说得问题最大了!!!
       你在认真观察一下在 Update 全过程中 AdoDataSet 是不是有被 Open 过...答案是没有!!!一个没有被打开的数据集怎样得到更新后的字段? Midas(现在的Datasnap) 是通过一个 TSQLResolver 分析Query, Table中的语句生成那几个(update, delete, insert)语句的, OldValue和NewValue由Delta包中带回, 所有更新的过程跟数据集没有任何关系
      

  2.   

    comanche(太可怕) 老兄讲的有道理,但是我觉得还是有疑问:在AfterUpdateRecord中写SELECT MAX(ID) FROM TABLE 对于新增加的记录应该是获得刚加入记录的ID,但是如果记录不是新添加的,而是更改的呢?会不会有错误?当添加了多条记录,然后一次提交到数据库中,它获取的ID是不是它自己的呢?
      

  3.   

    可以判断的嘛if UpdateMode = ukInsert then
    begin
    end
    else UpdateMode = ukModify ...
    ...
      

  4.   

    当添加了多条记录,然后一次提交到数据库中,它获取的ID是不是它自己的呢?BeforeUpdateRecord 和 AfterUpdateRecord 是成对的, 它们发生在每一条记录被操作前和后
      

  5.   

    根据Comanche的建议,我可以这样做:在AfterUpdateRecord事件中:var
      ssql: String;
      MaxID: Integer;
    begin
      If UpdateKind=ukInsert then
      begin
        ssql := 'SELECT MAX(ID) MAX_ID FROM TEST01';
        With AdoQry do
        begin
          Close;
          SQL.Clear;
          SQL.Add(ssql);
          Open;
        end;
        MaxID := AdoQry.FieldByName('MAX_ID').AsInteger;
        DeltaDS.FieldByName('ID').NewValue := MaxID;
      end;
    end;
    这样确实是可以获取自动增长的ID,这样可以解决record changed by ....的问题。但是我发现了一个问题:
    如果AdoConnection的KeepConnection设为False,那么在执行到 SQL.Add(ssql) 的时候,会报错:“在事务处理过程中中,连接对象不能显式的被断开”什么的,不知道是为什么。但是如果KeepConnection为True,则没有这个问题。不知道是什么原理?
      

  6.   

    不知道, 我一般用静态 sql 语句