Can I implement change tracking for classes that come from imported wsdl classes in Delphi 2010?

Is there a way to implement change tracking in a derived class without overriding the getters and setters of the base class?
I have a multi-level, entity-based project in development with access to data and business logic, eventually moving to a server application in C #. (Access to data has already been transferred) The client is in Delphi 2010.
I used data transfer objects to transfer information between the client and server, however, it was necessary to implement my own change tracking from Delphi. I originally achieved this by inheriting from Dtos in wsdl and "overriding" getters and setters. Base class in wsdl import (there is no real control over this):

// ************************************************************************ // // XML : DtoCONTAINER_JNL, global, <complexType> // Namespace : http://k3scs.com/WCF // ************************************************************************ // DtoCONTAINER_JNL = class(DtoBase) private FJNL_ID: Integer; FJNL_TYPE_ID: Integer; FJNL_DATE: TXSDateTime; published property JNL_ID: Integer read FJNL_ID write FJNL_ID; property JNL_TYPE_ID: Integer read FJNL_TYPE_ID write FJNL_TYPE_ID; property JNL_DATE: TXSDateTime read FJNL_DATE write FJNL_DATE; 

Derived classes:

  TDtoCONTAINER_JNL = class(DtoCONTAINER_JNL, IDto) private FName : string; FReferenceCounted : boolean; _isNew : boolean; FModified : boolean; FJNL_ID : integer; FJNL_TYPE_ID : integer; FJNL_DATE : TDateTime; public property JNL_ID : integer read FJNL_ID write SetJNL_ID; property JNL_TYPE_ID : integer read FJNL_TYPE_ID write SetJNL_TYPE_ID; property JNL_DATE : TDateTime read GetJNL_DATE write SetJNL_DATE; 

typical setter method:

 procedure TDtoCONTAINER_JNL.SetJNL_DATE(const value : TDateTime); begin ChangedProperties:= DtoGenerics.ChangeTracker('JNL_DATE', value, inherited JNL_DATE, self.IsLoaded, ChangedProperties); inherited JNL_DATE := DtoGenerics.GetXsDate(value); end; 

typical getter method:

 function TDtoCONTAINER_JNL.GetJNL_DATE : TDateTime; begin Result := DtoGenerics.GetDate(inherited JNL_DATE); end; 

Whenever a property is set, the change tracker adds this property name to the list of changed properties to return to the server. This allows update operations to be targeted and efficient. The problem is that each of the getters / setters practically made the derived classes incompatible with the base, that is, the polymorphism is broken, and casting no longer works as expected.
The above classes are generated from the t4 template in C # based on the corresponding tables / database objects, so changing over 100 classes should not be a problem.
Does anyone have any experience?
Any suggestions would be appreciated.

EDIT

The Wsdl importer consists mainly of constants that use a string format or something similar to create the Delphi equivalent of complex types. I made the following changes, using similar constant names, to easily identify my changes, as well as where to place them in relation to the pre-existing code. Ideally, I would completely get rid of inheritance and just use modified versions of my classes in wsdl, because I still can not send the modified Tdto, even if I return it back to the Dto class, because the web service rejects it with a message, for example β€œwaiting for DtoObject,” but instead got undtoUnit.TdtoObject. ”So, I do inheritance to handle conversions between ArrayOfObjects and TList and vice versa. Here are the changes, as I recall:

 // In WSDLImpConst // I changed sRemoteClassDeclPas constant value of 'private' to 'proctected' // Then I added these after SUnitInit sTrackerDec = sTrackerProcPrefix+ sTrackerProcArgs + sLineBreak; sTrackerProc = sTrackerDec + 'begin' + sLineBreak + ' Result:= nProps;' + sLineBreak + ' if (_loaded) and (oVal <> pVal) then' + sLineBreak + ' begin ' + sLineBreak + ' Result := TrackChange(pName, nProps);' + sLineBreak + ' end;' + sLineBreak + 'end;' + sLineBreak ; sTracker2Dec = sTrackerProcPrefix+ sTracker2ProcArgs + sLineBreak; sTracker2Proc = sTracker2Dec + 'begin' + sLineBreak + ' Result:= nProps;' + sLineBreak + ' if (_loaded) and (oVal <> DateTimeToXSDateTime(pVal)) then' + sLineBreak + ' begin ' + sLineBreak + ' Result := TrackChange(pName, nProps);' + sLineBreak + ' end;' + sLineBreak + 'end;' + sLineBreak ; sTracker3Dec = sTrackerProcPrefix+ sTracker3ProcArgs + sLineBreak; sTracker3Proc = sTracker3Dec + 'begin' + sLineBreak + ' Result:= nProps;' + sLineBreak + ' if nProps = '''' then' + sLineBreak + ' nProps := pName' + sLineBreak + ' else' + sLineBreak + ' nProps := pName + '','' + nProps;' + sLineBreak + ' Result:= nProps;' + sLineBreak + 'end;' + sLineBreak ; sTracker4Dec = sTrackerProcPrefix+ sTracker4ProcArgs + sLineBreak; sTracker4Proc = sTracker4Dec + 'begin' + sLineBreak + ' Result:= nProps;' + sLineBreak + ' if (_loaded) and (oVal <> pVal) then' + sLineBreak + ' begin ' + sLineBreak + ' Result := TrackChange(pName, nProps);' + sLineBreak + ' end;' + sLineBreak + 'end;' //Added my own setters: sRemoteClassSetterImplPas = 'procedure %0:s.Set%1:s(const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + 'end;' + sLineBreak; sRemoteClassSetterImplPas2= 'procedure %0:s.Set%1:s(const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + ' F%1:s_Specified := True;' + sLineBreak + 'end;' + sLineBreak; sRemoteClassSetterImplPas3 = 'procedure %0:s.Set%1:s(const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' ChangedProperties:= TrackChange(''%1:s'', A%2:s, F%1:s, self.IsLoaded, ChangedProperties);' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + 'end;' + sLineBreak; sRemoteClassSetterImplPas4 = 'procedure %0:s.Set%1:s(const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' ChangedProperties:= TrackChange(A%2:s, F%1:s, ''%1:s'', self.IsLoaded, ChangedProperties);' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + 'end;' + sLineBreak; // And for indexed properties sRemoteClassSetterImplPasIdx2= 'procedure %0:s.Set%1:s(Index: Integer; const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + ' F%1:s_Specified := True;' + sLineBreak + 'end;' + sLineBreak; sRemoteClassSetterImplPasIdx3 = 'procedure %0:s.Set%1:s(Index: Integer; const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' ChangedProperties:= TrackChange(''%1:s'', A%2:s, F%1:s, self.IsLoaded, ChangedProperties);' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + 'end;' + sLineBreak; sRemoteClassSetterImplPasIdx4 = 'procedure %0:s.Set%1:s(Index: Integer; const A%2:s: %2:s);' + sLineBreak + 'begin' + sLineBreak + ' ChangedProperties:= TrackChange(A%2:s, F%1:s, ''%1:s'', self.IsLoaded, ChangedProperties);' + sLineBreak + ' F%1:s := A%2:s;' + sLineBreak + 'end;' + sLineBreak; // Added these before the SImplDecl constant: sIntfFactoryDecl = '_di_%0:s Get%0:s(bool useWSDL=false, AnsiString addr="", THTTPRIO* HTTPRIO=0);' + sLineBreak + sLineBreak; sTrackerProcPrefix = 'function TrackChange'; sTrackerProcArgs = '(const pName: string; pVal, oVal: variant; _loaded: boolean; nProps: string): string; overload'; sTracker2ProcArgs = '(const pName: string; pVal: TDateTime; oVal: txsdateTime; _loaded:boolean; nProps: string): string; overload;'; sTracker3ProcArgs = '(const pName: string; nProps: string): string; overload;'; sTracker4ProcArgs = '(pVal, oVal: TByteDynArray; const pName: string; _loaded: boolean; nProps: string): string; overload'; sIntfTrackDecl = sTrackerProcPrefix + sTrackerProcArgs+ sLineBreak; sIntfTrack2Decl = sTrackerProcPrefix + sTracker2ProcArgs+ sLineBreak; sIntfTrack3Decl = sTrackerProcPrefix + sTracker3ProcArgs+ sLineBreak; sIntfTrack4Decl = sTrackerProcPrefix + sTracker4ProcArgs+ sLineBreak; // Then in WSDLPasWriter I added my new constant arrays: SetterImpl2:array[Boolean] of string = (sRemoteClassSetterImplPas2, sRemoteClassSetterImplPasIdx2); SetterImpl3:array[Boolean] of string = (sRemoteClassSetterImplPas3, sRemoteClassSetterImplPasIdx3); SetterImpl4:array[Boolean] of string = (sRemoteClassSetterImplPas4, sRemoteClassSetterImplPasIdx4); // then modified the setter section in 'WriteComplexTypeClass' { Setter } if UseSetGets or GenSpecifiedSupport(Member) then begin if GenSpecifiedSupport(Member) then begin WriteFmt(SetterImpl2[HasIndexDecl(Member)],[WSDLType.LangName, Member.LangName, Member.DataType.LangName]) end else begin if (ContainsStr(BaseName, 'Dto')) and (not ContainsStr(Member.DataType.LangName, 'XS')) and (not ContainsStr(Member.DataType.LangName, 'ArrayOf')) then if (ContainsStr(Member.DataType.LangName, 'TByteDynArray')) then WriteFmt(SetterImpl4[HasIndexDecl(Member)],[WSDLType.LangName, Member.LangName, Member.DataType.LangName]) else WriteFmt(SetterImpl3[HasIndexDecl(Member)],[WSDLType.LangName, Member.LangName, Member.DataType.LangName]) else WriteFmt(SetterImpl1[HasIndexDecl(Member)],[WSDLType.LangName, Member.LangName, Member.DataType.LangName]); end; // at WriteInterfaceEnd I added WriteFmt(sIntfTrackDecl, []); WriteFmt(sIntfTrack2Decl, []); WriteFmt(sIntfTrack3Decl, []); WriteFmt(sIntfTrack4Decl, []); // at WriteInterfaceBegin I added WriteLn(sTrackerProc, []); WriteLn(sTracker2Proc, []); WriteLn(sTracker3Proc, []); WriteLn(sTracker4Proc, []); // finally I changed the wsdlImp dpr and replaced 'AnsiString' (or 'widestring' I can't remember) with 'string' 
+4
source share
1 answer

If I am not mistaken, the source code for the WSDL to Delphi converter is part of the Delphi enterprise and above (it is called WSDLImp.dpr in the wsdlimporter directory).

I would adapt this to create my own classes directly.

+2
source

Source: https://habr.com/ru/post/1434825/


All Articles