2012-11-01 5 views
3

Pour des raisons pas vraiment pertinentes à la question, je veux utiliser des génériques dans mes classes TRemotable. J'ai trouvé que Soap.OPToSOAPDomConv.pas a quelques problèmes avec ceci. Il utilise l'ancien RTTI qui, je suppose, ne peut pas gérer les génériques, donc les classes ne sont pas sérialisées en XML.Delphi XE2 génériques dans les classes TRemotable

J'ai réussi à changer Soap.OPToSOAPDomConv.pas pour qu'il fonctionne avec les génériques. Ma question principale est de savoir s'il est acceptable de faire des changements dans les fichiers source Delphi? Si ce n'est pas le cas, y a-t-il une meilleure façon de faire? Tant que c'est juste moi qui l'utilise, je suppose qu'il n'y a pas de gros problèmes, mais il est difficile de distribuer la source à d'autres, et il y a aussi des changements futurs à considérer dans Delphi. Le reste de ce post lenghty est juste des détails sur ce que je suis en train de faire :-)

J'ai changé cela en Soap.OPToSOAPDomConv.pas (ligne 3759)

if SerializeProps then 
begin 
    { Serialized published properties } 
    Count := GetTypeData(Instance.ClassInfo)^.PropCount; 
    if Count > 0 then 
    begin 
    CheckedElemURI := False; 
    GetMem(PropList, Count * SizeOf(Pointer)); 
    try 
     GetPropInfos(Instance.ClassInfo, PropList); 

A: (non la plus jolie mise en œuvre, je suppose)

de nouvelles variables dans la procédure:

Context: TRttiContext;  
RttiProperty: TRttiProperty; 

Ligne 3759:

if SerializeProps then 
begin 
    { Serialized published properties } 
    Count := 0; 
    for RttiProperty in Context.GetType(Instance.ClassInfo).GetProperties do 
    begin 
    if RttiProperty.Visibility = mvPublished then //The old method only read published 
     Count := Count + 1;       //RTTI scoping [mvPublished] requires changes to 
    end;           //soap.InvRegistry 
    begin 
    CheckedElemURI := False; 
    GetMem(PropList, Count * SizeOf(Pointer)); 
    try 
     I := 0; 
     for RttiProperty in Context.GetType(Instance.ClassInfo).GetProperties do 
     if RttiProperty.Visibility = mvPublished then 
     begin 
      PropList[I] := TRttiInstanceProperty(RttiProperty).PropInfo; 
      I := I + 1; 
     end; 

Quelques détails sur ce que je fais est probablement utile. L'arrière-plan est que le wsdl importé du service Web SOAP génère une unité énorme, composée d'environ 2000 classes et 300k lignes de code. La conception du service Web est hors de mon contrôle. L'importateur WSDL rend toutes ces classes visibles dans RTTI, qui consomme l'espace RTTI et l'unité ne compilera pas.

J'ai refacturé le code ici et là et j'ai maintenant une implémentation fonctionnelle. Tout en refactorisant j'ai trouvé que je pourrais couper quelques 50000 lignes de code redondant en utilisant des génériques. Puisque Delphi ne compilera pas le wsdl importé "tel quel" de toute façon, je devrai maintenir l'unité manuellement chaque fois que de nouvelles méthodes seront disponibles dans le service web, donc je veux que ce soit aussi lisible que possible.

Fondamentalement, je change selon ci-dessous. L'exemple a des classes très simplifiées, et l'échantillon a réellement plus de lignes dans le code refactorisé, mais vu que les classes originales ont beaucoup de procédures etc. cette méthode rend l'unité beaucoup plus lisible, et il est aussi plus facile de composer les classes.

TLCar = class(TRemotable) 
private 
    FEngine: string; 
    FName: string; 
published 
    property Name: string read FName write FName; 
    property Engine: string read FEngine write FEngine; 
end; 

TLBicycle = class(TRemotable) 
private 
    FPedals: string; 
    FName: string; 
published 
    property Name: string read FName write FName; 
    property Pedals: string read FPedals write FPedals; 
end; 

TListCarRequest = class(TRemotable) 
private 
    FreturnedTags: TLCar; 
published 
    property returnedTags: TLCar read FreturnedTags write FreturnedTags; 
end; 

TListBiCycleRequest = class(TRemotable) 
private 
    FreturnedTags: TLBicycle; 
published 
    property returnedTags: TLBicycle read FreturnedTags write FreturnedTags; 

Pour:

TCommonReturnedTags = class(TRemotable) 
private 
    FName: string; 
published 
    property Name: string read FName write FName; 
end; 

TLCar = class(TCommonReturnedTags) 
private 
    FEngine: string; 
published 
    property Engine: string read FEngine write FEngine; 
end; 

TLBicycle = class(TCommonReturnedTags) 
private 
    FPedals: string; 
published 
    property Pedals: string read FPedals write FPedals; 
end; 

TGenericListRequest<T: TCommonReturnedTags, constructor> = class(TRemotable) 
private 
    FreturnedTags: T; 
published 
    property returnedTags: T read FreturnedTags write FreturnedTags; 
end; 

TListCarRequest = class(TGenericListRequest<TLCar>) 
end; 

TListBiCycleRequest = class(TGenericListRequest<TLBicycle>) 
end; 

Cordialement,

Dan

+2

Wow. Courageux. Y a-t-il une raison pour laquelle vous ne vous contentez pas de sérialiser vos génériques et de les éloigner ensuite? Puisque le code SOAP Remoting est souvent corrigé et mis à jour par les personnes qui veulent contourner ses bogues, il n'est pas aussi difficile de le changer et de le reconstruire, comme ce serait le cas pour changer System.pas. Si c'était moi, je voudrais avoir une couverture de test unitaire pour ce code si je le changeais. –

+0

@Warren. Merci, bon point sur le code SOAP Remoting. J'ai senti que c'était un peu oser moi-même ... Je ne comprends pas vraiment ce que vous dites en sérialisant les génériques avant de les décaler (je viens juste d'en apprendre plus sur les génériques), pouvez-vous expliquer ça un peu plus loin? Je n'ai fondamentalement pas d'autre raison que de réduire la quantité de code ainsi que la portée de l'espace RTTI. – dahook

+0

Au lieu de remettre à distance TGenericList , vous devez distancer une chaîne JSON de TLCARS stockée dans un paramètre de chaîne. –

Répondre

1

Il y a deux choses à prendre en compte lors de modifications de ce genre. Tout d'abord, le changement peut-il avoir un impact sur les fonctionnalités existantes? Comme dans ce cas je dirais que c'est sûr car la fonctionnalité est nouvelle pour cette opération, donc il ne devrait pas y avoir de comportement inattendu. La deuxième partie est l'environnement de développement d'évolution. Problème avec l'évolution de l'environnement est que les liens entre les actions peuvent changer et cela peut conduire à des choses inattendues. En ce moment, il est bon de supposer que XE2 a eu sa part de mises à jour. Si ce n'est pas le cas, vous devrez garder un œil lors de la correction ou de la mise à jour. Le deuxième type de changement comme celui de XE2 à XE3 peut être mieux géré. Il suffit de mettre ce qui suit en haut de Soap.OPToSOAPDomConv.pas:

{$IFNDEF VER230} 
    {$MESSAGE ERROR 'Intended to be used with XE2'} 
{$ENDIF} 

Lorsque l'erreur get pendant que vous souvenez peut-être la compilation vaguement qu'il y avait quelque chose sur ce fichier ... Donc en bref, ce n'est pas de mauvaises choses aussi loin que d'essayer d'évaluer l'impact et l'adaptation à l'environnement changements. Espérons que c'était ce que tu voulais savoir.