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
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. –
@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
Au lieu de remettre à distance TGenericList, vous devez distancer une chaîne JSON de TLCARS stockée dans un paramètre de chaîne. –