4

基本クラスのゲッターとセッターをオーバーライドせずに、派生クラスに変更追跡を実装する方法はありますか?
私はエンティティフレームワークベースの多層プロジェクトを開発中であり、データアクセスとビジネスロジックは最終的にC#ベースのサーバーアプリケーションに移行しています。(データアクセスはすでに移行されています)クライアントはDelphi 2010
にあります。クライアントとサーバー間で情報を渡すためにデータ転送オブジェクトを使用しましたが、Delphiから独自の変更追跡を実装する必要がありました。私は当初、wsdlのDtoから継承し、ゲッターとセッターを「オーバーライド」することでそれを実現しました。wsdlインポートの基本クラス(これを実際に制御することはできません):

// ************************************************************************ //
  // 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;

典型的なセッターメソッド:

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;

プロパティが設定されるたびに、変更トラッカーはこのプロパティ名を変更されたプロパティのリストに追加して、サーバーに返します。これにより、更新ステートメントを対象にして効率的にすることができます。問題は、各ゲッター/セッターが派生クラスをベースと実際に互換性のないものにしていることです。つまり、ポリモーフィズムが壊れており、キャストが期待どおりに機能しなくなります。
上記のクラスは、対応するデータベーステーブル/エンティティに基づいてC#のt4テンプレートから生成されるため、100を超えるクラスを変更しても問題はありません。
誰かがこれを経験したことがありますか?
任意の提案をいただければ幸いです。

編集

Wsdlインポーターは、主に、文字列形式などを使用して、複合型に相当するDelphiを作成する定数で構成されています。次の変更を行いました。同様の定数名を使用して、変更を簡単に識別できるようにしました。また、既存のコードとの関係で変更を配置する場所も変更しました。理想的には、継承を完全に取り除き、wsdlでクラスの変更されたバージョンを使用します。これは、Webサービスがメッセージで拒否するためにDtoクラスにキャストバックしても、変更されたTdtoを送信できないためです。 「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' 
4

1 に答える 1

2

私が間違っていなければ、WSDLからDelphiへのコンバータのソースコードはDelphiエンタープライズ以降の一部です(ディレクトリで呼び出さWSDLImp.dprwsdlimporterます)。

私はそれを適応させて、あなた自身のクラスを直接生成します。

于 2012-09-18T12:25:09.367 に答える