Componentes.Terceros.RemObj.../internal/6.0.43.801/1/Data Abstract for Delphi/Source/uDARemoteDataAdapter.pas
2010-01-29 16:17:43 +00:00

545 lines
21 KiB
ObjectPascal

unit uDARemoteDataAdapter;
{----------------------------------------------------------------------------}
{ Data Abstract Library - Core Library }
{ }
{ compiler: Delphi 6 and up, Kylix 3 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Using this code requires a valid license of the Data Abstract }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I DataAbstract.inc}
interface
uses
Classes,
uRODynamicRequest, uROClasses, uRORemoteService,
DataAbstract4_Intf, uDADataAdapter, uDADataTable,
uDARemoteDataAdapterRequests,uDADataStreamer;
const
// from uDADataAdapter
fbNone = uDADataAdapter.fbNone;
fbRaiseException = uDADataAdapter.fbRaiseException;
fbShowReconcile = uDADataAdapter.fbShowReconcile;
fbBoth = uDADataAdapter.fbBoth;
type
TDAReconcileDialogAction = uDADataAdapter.TDAReconcileDialogAction;
type
TDARequestEvent = procedure(Sender: TObject; Request: TRODynamicRequest) of object;
{ TDARemoteDataAdapter }
TDARemoteDataAdapter = class(TDABaseDataAdapter)
private
fGetDataCall: TDAGetDataRequest;
fGetSchemaCall: TDAGetSchemaRequest;
fUpdateDataCall: TDAUpdateDataRequest;
fGetScriptsCall: TDAGetScriptsRequest;
fRemoteService: TRORemoteService;
fBeforeGetDataCall, fAfterGetDataCall,
fBeforeGetSchemaCall, fAfterGetSchemaCall,
fBeforeGetScriptsCall, fAfterGetScriptsCall,
fBeforeUpdateDataCall, fAfterUpdateDataCall: TDARequestEvent;
procedure DoGetSchemaCall;
procedure FillTableNamesParam(aTables: array of TDADataTable; aParam: TRORequestParam);
procedure SetRemoteService(const Value: TRORemoteService);
procedure FillTableParams(aTables: array of TDADataTable; aParam: TRORequestParam);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure Loaded; override;
function InternalFillSchema(var aStream: TROBinaryMemoryStream): TRODataType; override;
procedure InternalApplyUpdates(aTables,aTablesWithDetails: array of TDADataTable); override;
function InternalFillScripts(aTables: array of TDADataTable): UTF8String; override;
function InternalReadSchema: UTF8String; override;
procedure InternalFill(aTableArray: array of TDADataTable; aArray: TableRequestInfoArray; const aIncludeSchema, aAppendMode: boolean; aSavedOptions: TDATableOptionsArray);override;
{ backward compatibility: to provide access to these in the legacy events in TDADataTable}
function Get_GetSchemaCall: TDARemoteRequest; override;
function Get_GetDataCall: TDARemoteRequest; override;
function Get_UpdateDataCall: TDARemoteRequest; override;
function Get_GetScriptsCall: TDARemoteRequest; override;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure SetupDefaultRequestV3;
procedure SetupDefaultRequest;
procedure CheckProperties; override;
published
property GetSchemaCall: TDAGetSchemaRequest read fGetSchemaCall;
property GetDataCall: TDAGetDataRequest read fGetDataCall;
property UpdateDataCall: TDAUpdateDataRequest read fUpdateDataCall;
property GetScriptsCall: TDAGetScriptsRequest read fGetScriptsCall;
property RemoteService: TRORemoteService read fRemoteService write SetRemoteService;
property CacheSchema;
property DataStreamer;
property BeforeGetDataCall: TDARequestEvent read fBeforeGetDataCall write fBeforeGetDataCall;
property AfterGetDataCall: TDARequestEvent read fAfterGetDataCall write fAfterGetDataCall;
property BeforeGetSchemaCall: TDARequestEvent read fBeforeGetSchemaCall write fBeforeGetSchemaCall;
property AfterGetSchemaCall: TDARequestEvent read fAfterGetSchemaCall write fAfterGetSchemaCall;
property BeforeGetScriptsCall: TDARequestEvent read fBeforeGetScriptsCall write fBeforeGetScriptsCall;
property AfterGetScriptsCall: TDARequestEvent read fAfterGetScriptsCall write fAfterGetScriptsCall;
property BeforeUpdateDataCall: TDARequestEvent read fBeforeUpdateDataCall write fBeforeUpdateDataCall;
property AfterUpdateDataCall: TDARequestEvent read fAfterUpdateDataCall write fAfterUpdateDataCall;
end;
implementation
uses
SysUtils;
{ TDARemoteDataAdapter }
procedure TDARemoteDataAdapter.FillTableNamesParam(aTables: array of TDADataTable; aParam: TRORequestParam);
var
lArray: StringArray;
i: integer;
begin
case aParam.DataType of
rtString: begin // v3 style string
if Length(aTables) <> 1 then
raise Exception.Create('The current GetDataCall configuration does not allow fetching multiple data tables at once.');
aParam.AsString := aTables[Low(aTables)].LogicalName;
end;
rtUserDefined: begin // v4 style string array
lArray := StringArray.Create();
lArray.Resize(Length(aTables));
for i := Low(aTables) to High(aTables) do
lArray[i-Low(aTables)] := UTF8Encode(aTables[i].LogicalName);
aParam.AsComplexType := lArray;
aParam.OwnsComplexType := true;
end;
rtUTF8String: aParam.AsUTF8String := GetTableNamesAsCommaText(aTables);
end;
end;
procedure TDARemoteDataAdapter.FillTableParams(
aTables: array of TDADataTable; aParam: TRORequestParam);
var
lArray: DataParameterArray;
lParam: DataParameter;
lTable: TDADataTable;
j: integer;
lList: TStringList;
begin
lArray:=nil; // prevert "W1036 Variable 'lArray' might not have been initialized"
case aParam.DataType of
rtString: begin // v3 style string
if Length(aTables) <> 1 then raise Exception.Create('The current GetDataCall configuration does not allow fetching multiple data tables');
with aTables[Low(aTables)].Params do begin
if Count<>0 then begin
lList:= TStringList.Create;
try
for j := 0 to Count - 1 do
lList.Values[Params[j].Name]:=Params[j].AsString;
aParam.AsString:= lList.Text;
finally
lList.Free;
end;
end;
end;
end;
rtUserDefined: begin
if aParam.TypeName = 'DataParameterArray' then
lArray := DataParameterArray.Create();
lTable := aTables[Low(aTables)];
for j := 0 to lTable.Params.Count-1 do begin
lParam := lArray.Add();
lParam.Name := AnsiToUtf8(lTable.Params[j].Name);
lParam.Value := lTable.Params[j].Value;
end;
aParam.AsComplexType := lArray;
aParam.OwnsComplexType := true;
end;
end;
end;
procedure TDARemoteDataAdapter.DoGetSchemaCall;
begin
//ToDo: handle aFilter parameter?
if Assigned(fBeforeGetSchemaCall) then fBeforeGetSchemaCall(Self, GetSchemaCall);
GetSchemaCall.Execute();
if Assigned(fAfterGetSchemaCall) then fAfterGetSchemaCall(Self, GetSchemaCall);
end;
function TDARemoteDataAdapter.InternalFillScripts(aTables: array of TDADataTable): UTF8String;
var
lParam: TDARemoteRequestParam;
lResultParam: TRORequestParam;
begin
if GetScriptsCall.MethodName = '' then
raise Exception.Create('GetScriptsCall.MethodName must be configured to retrieve scripts.');
try
lParam := GetScriptsCall.Params.FindParam(GetScriptsCall.OutgoingTableNamesParameter);
if assigned(lParam) then FillTableNamesParam(aTables, lParam);
GetScriptsCall.Execute();
lResultParam := GetScriptsCall.Params.FindParam(GetScriptsCall.IncomingScriptParameter);
if not assigned(lResultParam) or not (lResultParam.DataType in [rtUTF8String, rtString]) then
raise Exception.Create('Result parameter of GetScriptsCall is not properly defined.');
if Assigned(fBeforeGetScriptsCall) then fBeforeGetScriptsCall(Self, GetScriptsCall);
GetScriptsCall.Execute();
if Assigned(fAfterGetScriptsCall) then fAfterGetScriptsCall(Self, GetScriptsCall);
Result := UTF8String(lResultParam.AsAnsiString); { TODO : .AsUtf8String ? }
finally
lParam := GetScriptsCall.Params.FindParam(GetScriptsCall.OutgoingTableNamesParameter);
if assigned(lParam) then lParam.ClearValue;
lParam := GetScriptsCall.Params.FindParam(GetScriptsCall.IncomingScriptParameter);
if assigned(lParam) then lParam.ClearValue;
end;
end;
function TDARemoteDataAdapter.InternalReadSchema: UTF8String;
var
lResultParam: TRORequestParam;
begin
CheckProperties;
lResultParam := GetSchemaCall.Params.FindParam(GetSchemaCall.IncomingSchemaParameter);
try
if not assigned(lResultParam) then
raise Exception.Create('Result parameter of GetSchemaCall is not defined.');
if not (lResultParam.DataType in [rtUTF8String,rtString]) then
raise Exception.Create('Result parameter of GetSchemaCall is not properly defined as String.');
DoGetSchemaCall();
Result := UTF8String(lResultParam.AsAnsiString); //{ TODO : .ASUtf8String ? }
finally
if Assigned(lResultParam) then lResultParam.ClearValue;
end;
end;
{$IFDEF FPC}
procedure List_Union(List1,List2: TList);
var
i: integer;
begin
if List1 = List2 then Exit;
for i := 0 to List2.Count-1 do
if List1.IndexOf(List2[i])=-1 then
List1.Add(List2[i]);
end;
{$ENDIF}
procedure TDARemoteDataAdapter.InternalApplyUpdates(aTables,aTablesWithDetails: array of TDADataTable);
var
i: integer;
lParam, lResultparam: TRORequestParam;
begin
try
{ Fill Input Parameters }
lParam := UpdateDataCall.Params.FindParam(UpdateDataCall.OutgoingDeltaParameter);
if not assigned(lParam) or (lParam.DataType <> rtBinary) then
raise Exception.Create('OutgoingDeltaParameter parameter of UpdateDataCall is not properly defined.');
lParam.ClearValue();
DataStreamer.Initialize(lParam.AsBinary, aiWrite);
try
for i := Low(aTables) to High(aTables) do
aTables[i].WriteDeltaToStream(DataStreamer);
finally
DataStreamer.Finalize;
end;
{ Make call }
if DataStreamer.DeltaCount > 0 then begin
if Assigned(fBeforeUpdateDataCall) then fBeforeUpdateDataCall(Self, UpdateDataCall);
UpdateDataCall.Execute();
if Assigned(fAfterUpdateDataCall) then fAfterUpdateDataCall(Self, UpdateDataCall);
{ Get Output Parameters }
if UpdateDataCall.IncomingDeltaParameter <> '' then begin // If the result parameter isn't set, we shouldn't check for a result.
lResultParam := UpdateDataCall.Params.FindParam(UpdateDataCall.IncomingDeltaParameter);
if not assigned(lResultParam) or (lResultParam.DataType <> rtBinary) then
raise Exception.Create('IncomingDeltaParameter parameter of UpdateDataCall is not properly defined.');
// Reads the incoming delta, including the details
if assigned(lResultParam.AsBinary) and (lResultParam.AsBinary.Size > 0) then begin
DataStreamer.Initialize(lResultParam.AsBinary, aiReadFromBeginning);
try
for i := Low(aTables) to High(aTables) do
aTables[i].ReadDeltaFromStream(DataStreamer);
finally
DataStreamer.Finalize;
end;
end;
end;
end;
finally
lResultParam := UpdateDataCall.Params.FindParam(UpdateDataCall.IncomingDeltaParameter);
if assigned(lResultParam) then lResultParam.ClearValue;
lParam := UpdateDataCall.Params.FindParam(UpdateDataCall.OutgoingDeltaParameter);
if assigned(lParam) then lParam.ClearValue;
end;
end;
constructor TDARemoteDataAdapter.Create(aOwner: TComponent);
begin
inherited;
fGetSchemaCall := TDAGetSchemaRequest.Create(self);
fGetDataCall := TDAGetDataRequest.Create(self);
fUpdateDataCall := TDAUpdateDataRequest.Create(self);
fGetScriptsCall := TDAGetScriptsRequest.Create(self);
fGetSchemaCall.Name := 'GetSchemaCall';
fGetDataCall.Name := 'GetDataCall';
fUpdateDataCall.Name := 'UpdateDataCall';
fGetScriptsCall.Name := 'GetScriptsCall';
SetupDefaultRequest;
end;
destructor TDARemoteDataAdapter.Destroy;
begin
RemoteService := nil;
FreeAndNil(fGetSchemaCall);
FreeAndNil(fGetDataCall);
FreeAndNil(fUpdateDataCall);
FreeAndNil(fGetScriptsCall);
inherited;
end;
procedure TDARemoteDataAdapter.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = RemoteService) then fRemoteService := nil;
end;
procedure TDARemoteDataAdapter.SetRemoteService(const Value: TRORemoteService);
begin
if Value <> fRemoteService then begin
if assigned(fRemoteService) then fRemoteService.RORemoveFreeNotification(self);
fRemoteService := Value;
GetSchemaCall.RemoteService := fRemoteService;
GetDataCall.RemoteService := fRemoteService;
UpdateDataCall.RemoteService := fRemoteService;
GetScriptsCall.RemoteService := fRemoteService;
if assigned(fRemoteService) then fRemoteService.ROFreeNotification(self);
end;
end;
{ Schema }
function TDARemoteDataAdapter.Get_GetDataCall: TDARemoteRequest;
begin
result := GetDataCall;
end;
function TDARemoteDataAdapter.Get_GetSchemaCall: TDARemoteRequest;
begin
result := GetSchemaCall;
end;
function TDARemoteDataAdapter.Get_GetScriptsCall: TDARemoteRequest;
begin
result := GetScriptsCall;
end;
function TDARemoteDataAdapter.Get_UpdateDataCall: TDARemoteRequest;
begin
result := UpdateDataCall;
end;
procedure TDARemoteDataAdapter.SetupDefaultRequest;
begin
GetSchemaCall.SetupDefaultRequest();
GetDataCall.SetupDefaultRequest();
UpdateDataCall.SetupDefaultRequest();
GetScriptsCall.SetupDefaultRequest();
end;
procedure TDARemoteDataAdapter.SetupDefaultRequestV3;
begin
GetSchemaCall.SetupDefaultRequestV3();
GetDataCall.SetupDefaultRequestV3();
UpdateDataCall.SetupDefaultRequestV3();
GetScriptsCall.SetupDefaultRequestV3();
end;
procedure TDARemoteDataAdapter.Assign(Source: TPersistent);
var
lSource: TDARemoteDataAdapter;
begin
inherited;
if Source is TDARemoteDataAdapter then begin
lSource := TDARemoteDataAdapter(Source);
AfterGetDataCall := lSource.AfterGetDataCall;
AfterGetSchemaCall := lSource.AfterGetSchemaCall;
AfterGetScriptsCall := lSource.AfterGetScriptsCall;
AfterUpdateDataCall := lSource.AfterUpdateDataCall;
BeforeGetDataCall := lSource.BeforeGetDataCall;
BeforeGetSchemaCall := lSource.BeforeGetSchemaCall;
BeforeGetScriptsCall := lSource.BeforeGetScriptsCall;
BeforeUpdateDataCall := lSource.BeforeUpdateDataCall;
GetDataCall.Assign(lSource.GetDataCall);
GetSchemaCall.Assign(lSource.GetSchemaCall);
GetScriptsCall.Assign(lSource.GetScriptsCall);
RemoteService := lSource.RemoteService;
UpdateDataCall.Assign(lSource.UpdateDataCall);
end;
end;
procedure TDARemoteDataAdapter.CheckProperties;
begin
inherited;
Check(not assigned(DataStreamer), Name + '.DataStreamer must be assigned.');
Check(not assigned(RemoteService), Name + '.RemoteService must be assigned.');
RemoteService.CheckProperties();
end;
procedure TDARemoteDataAdapter.Loaded;
begin
inherited;
{$IFDEF DELPHI6}
// Delphi 6 doesn't call Loaded of any of the sub components.
fGetDataCall.Loaded;
fGetSchemaCall.Loaded;
fUpdateDataCall.Loaded;
fGetScriptsCall.Loaded;
{$ENDIF}
end;
procedure TDARemoteDataAdapter.InternalFill(aTableArray: array of TDADataTable;
aArray: TableRequestInfoArray; const aIncludeSchema, aAppendMode: boolean; aSavedOptions: TDATableOptionsArray);
var
lParam, lResultParam: TRORequestParam;
lHasTableNamesParameter: Boolean;
i: integer;
ltbl: TDADataTable;
lLogicalName: string;
begin
try
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingTableNamesParameter);
lHasTableNamesParameter := assigned(lParam);
if lHasTableNamesParameter then
FillTableNamesParam(aTableArray, lParam)
else if Length(aTableArray) <> 1 then
raise Exception.Create('The current GetDataCall configuration does not allow fetching multiple data tables');
if Length(GetDataCall.OutgoingTableRequestInfosParameter) > 0 then begin
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingTableRequestInfosParameter);
if Assigned(lParam) then begin
if (lParam.DataType = rtUserDefined) and (lParam.TypeName = 'TableRequestInfoArray') then begin
lParam.AsComplexType := aArray;
end;
end;
end;
if (GetDataCall.OutgoingParamsParameter <> '') then begin // v3 style call
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingParamsParameter);
if Assigned(lParam) then
FillTableParams(aTableArray, lParam);
end;
if (GetDataCall.OutgoingMaxRecordsParameter <> '') then begin // v3 style call
if Length(aTableArray) <> 1 then
for i := 0 to High(aTableArray) do
if aTableArray[i].MaxRecords <> -1 then
raise Exception.Create('The current GetDataCall configuration does not allow fetching multiple data tables with a limited record count.');
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingMaxRecordsParameter);
if Assigned(lParam) and (Length(aTableArray) > 0) then lParam.AsInteger := aTableArray[0].MaxRecords;
end;
if (GetDataCall.OutgoingIncludeSchemaParameter <> '') then begin // v3 style call
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingIncludeSchemaParameter);
if Assigned(lParam) then lParam.AsBoolean := aIncludeSchema;
end;
if Assigned(fBeforeGetDataCall) then fBeforeGetDataCall(Self, GetDataCall);
GetDataCall.Execute();
lResultParam := GetDataCall.Params.FindParam(GetDataCall.IncomingDataParameter);
if Assigned(fAfterGetDataCall) then fAfterGetDataCall(Self, GetDataCall);
if not assigned(lResultParam) or (lResultParam.DataType <> rtBinary) then
raise Exception.Create('Result parameter of GetDataCall is not properly defined.');
if not assigned(lResultParam.AsBinary) then raise Exception.Create('The server returned a nil buffer.');
// Reads the data
DataStreamer.Initialize(lResultParam.AsBinary, aiRead);
try
// part 1 - reading schema
for i := Low(aTableArray) to High(aTableArray) do
begin
ltbl:= aTableArray[i];
if aArray[i].IncludeSchema and not (soIgnoreStreamSchema in ltbl.StreamingOptions) then begin
aSavedOptions[i].AppendMode := False;
if not lHasTableNamesParameter and (DataStreamer.DatasetCount = 1) then
DataStreamer.ReadDataset(DataStreamer.DatasetNames[0], ltbl, true, false)
else
DataStreamer.ReadDataset(ltbl.LogicalName, ltbl, true, false);
end;
if not ltbl.Active then ltbl.InitializeDataTable;
//end;
end;
if AutoFillScripts then FillScripts(aTableArray);
// part 2 - reading data
for i := Low(aTableArray) to High(aTableArray) do begin
ltbl:= aTableArray[i];
if not lHasTableNamesParameter and (DataStreamer.DatasetCount = 1) then
lLogicalName:=DataStreamer.DatasetNames[0]
else
lLogicalName:=ltbl.LogicalName;
DataStreamer.ReadDataset(lLogicalName, ltbl, false, true, aSavedOptions[i].AppendMode);
if not aAppendMode and (moAllInOneFetch in ltbl.MasterOptions) then ltbl.DoCascadeOperation(DataStreamer, moAllInOneFetch);
end;
finally
DataStreamer.Finalize;
end;
finally
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingTableRequestInfosParameter);
if Assigned(lParam) then lParam.ClearValue;
lParam := GetDataCall.Params.FindParam(GetDataCall.OutgoingParamsParameter);
if Assigned(lParam) then lParam.ClearValue;
lParam := GetDataCall.Params.FindParam(GetDataCall.IncomingDataParameter);
if Assigned(lParam) then lParam.ClearValue;
end;
end;
function TDARemoteDataAdapter.InternalFillSchema(var aStream: TROBinaryMemoryStream): TRODataType;
var
lResultParam: TRORequestParam;
begin
lResultParam := GetSchemaCall.Params.FindParam(GetSchemaCall.IncomingSchemaParameter);
if not assigned(lResultParam) then raise Exception.Create('Result parameter of GetSchemaCall is not defined.');
Result := lResultParam.DataType;
try
case Result of
rtBinary:begin
DoGetSchemaCall();
if not assigned(lResultParam.AsBinary) or (lResultParam.AsBinary.Size = 0 ) then
raise Exception.Create('Server returned an empty buffer for schema.');
aStream := TROBinaryMemoryStream.Create;
aStream.CopyFrom(lResultParam.AsBinary,0);
end;
rtString, rtUTF8String: begin
Schema;
end;
end;
finally
if Assigned(lResultParam) then lResultParam.ClearValue;
end;
end;
end.