有没有办法在派生类中实现更改跟踪而不覆盖基类的getter和setter? 我在开发中有一个基于实体框架的多层项目,数据访问和业务逻辑最终转移到基于C#的服务器应用程序. (数据访问已经迁
我在开发中有一个基于实体框架的多层项目,数据访问和业务逻辑最终转移到基于C#的服务器应用程序. (数据访问已经迁移)客户端在Delphi 2010中.
我已经使用数据传输对象在客户端和服务器之间传递信息,但是有必要从Delphi实现我自己的更改跟踪.我最初是通过继承wsdl中的Dtos并“覆盖”getter和setter来实现的.
wsdl import中的基类(无法真正控制):
// ************************************************************************ // // 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;
派生类:
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;
典型的setter方法:
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;
典型的吸气方法:
function TDtoCONTAINER_JNL.GetJNL_DATE : TDateTime; begin Result := DtoGenerics.GetDate(inherited JNL_DATE); end;
无论何时设置属性,更改跟踪器都会将此属性名称添加到已更改属性的列表中以返回到服务器.这样可以使更新语句具有针对性和高效性.问题是每个getter / setter实际上使得派生类与base不兼容,也就是说,多态性被破坏,并且cast不再按预期工作.
上面的类是根据相应的数据库表/实体从C#中的t4模板生成的,因此更改100多个类应该不是问题.
有人对这个有经验么?
任何建议将不胜感激.
编辑
Wsdl导入器主要由使用字符串格式的常量或类似的东西组成,以创建复杂类型的Delphi等价物.我做了以下更改,使用类似的常量名称来轻松识别我的更改,以及将它们放在与预先存在的代码相关的位置.
理想情况下,我会完全摆脱继承,只是在wsdl中使用我的类的修改版本,因为即使我将它转换回Dto类,我仍然无法提交修改后的Tdto,因为Web服务拒绝它带有消息比如“期待DtoObject,但得到了undtoUnit.TdtoObject”.
因此,我使用继承来处理ArrayOfObjects到TList之间的转换,反之亦然.
以下是我记忆中的最佳变化:
// 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'如果我没弄错的话,WSDL to Delphi转换器源代码是Delphi企业及更高版本的一部分(在wsdlimporter目录中称为WSDLImp.dpr).
我会调整它直接生成自己的类.