RO39 – 在一个事务中实现多个ClientDataSets 更新

来源:互联网 发布:秦时明月惊鲵是谁知乎 编辑:程序博客网 时间:2024/05/24 06:55

从两层迁移到三层第一个要面临的窍门就是摆脱在客户端控制事务.客户端不应该开始和提交事务.事实上客户端应该不知道任何事物.所有的事务逻辑应该放在服务端.
本文向你展示如何在RemObjects DataSnap服务中创建一个方法,用以传递一个ClientDataSet. Delta集合并在单独的事务中向数据库中更新,以便于在更新时发生异常时回滚事务.
这个方法只适用于你使用非嵌套(主从关系)的ClientDataSets 的情况,因为DataSnap默认情况下已经在单独事务中使用嵌套(主从关系)数据集.
你现在需要一个RO DataSnap server,由于我不打算介绍如何创建这种服务,所以我们使用一个已经存在的服务.
首先,你需要在服务端RODL中创建一下数据类型.使用RO Service Builder,创建一个包含ProviderName (string) 和 Delta (binary)成员的DeltaToApply结构体:

然后创建DeltaToApply类型的数组:


接下来,在服务端创建接收这个数组的函数.我将这个函数添加到包含所有Provider的同一个服务中,因为我们要使用这个Provider更新数据:

方法实现如下: 
procedure TNewService.ApplyUpdates(var ADeltaArray: DeltaArray);
var
 I: Integer;
 Provider: TDataSetProvider;
 ErrorCount: Integer;
begin
 // Put your code to start transaction
 try
    for I := 0 to ADeltaArray.Count - 1 do
    begin
      Provider := FindProvider(ADeltaArray[I].ProviderName);
      if not Assigned(Provider) then
        raise Exception.Create('Provider not found: ' + ADeltaArray[I].ProviderName);
 
      Provider.ApplyUpdates(VariantFromBinary(ADeltaArray[I].Delta), 0, ErrorCount);
      if ErrorCount > 0 then
        // Put your code to handle errors
        raise Exception.Create('Errors during applyupdates: ' + Provider.Name);
    end;
    // Put your code to commit the transaction
 except
    // Put your code to rollback the transaction
    raise;
 end;
end;
我已经创建了一个帮助函数去根据名字获取一个Provider:
function TNewService.FindProvider(ProviderName: string): TDataSetProvider;
var
 Component: TObject;
begin
 Component := FindComponent(ProviderName);
 if Component is TDataSetProvider then
    Result := Component as TDataSetProvider
 else
    Result := nil;
end;
服务端完成.在客户端,你需要创建一个方法将所有的ClientDataSet.Delta保存在数组并将其发送到服务端:
procedure TClientForm.ApplyUpdates(ClientDataSets: array of TClientDataSet);
var
 Deltas: DeltaArray;
 Delta: DeltaToApply;
 I: Integer;
begin
 Deltas := DeltaArray.Create;
 try
    for I := Low(ClientDataSets) to High(ClientDataSets) do
    begin
      if ClientDataSets[I].ChangeCount = 0 then
        Continue;
 
      Delta := Deltas.Add;
      Delta.ProviderName := ClientDataSets[I].ProviderName;
      Delta.Delta := BinaryFromVariant(ClientDataSets[I].Delta);
    end;
    CoNewService.Create(ROMessage, ROChannel).ApplyUpdates(Deltas);
 finally
    Deltas.Free;
 end;
end;
VariantFromBinary 和 BinaryFromVariant 方法在uROBinaryHelpers单元内.
最后,你只需要在客户端调用这个方法传递所有的你需要在同一个事务中更新的ClientDataSet:
ApplyUpdates([ClientDataSet1, ClientDataSet2, ClientDataSet3]);
好了!我希望这能对你有帮助.如果你发现代码存在问题或有可以改进的地方请尽快通知我以便于修正.

原创粉丝点击