3
votes

For reasons not really relevant to the question I want to use generics in my TRemotable classes. I've found that Soap.OPToSOAPDomConv.pas has some problems with this. It's using the old RTTI which I guess can't handle generics so the classes is not serialized into xml.

I've managed to change Soap.OPToSOAPDomConv.pas so that it works with generics. My main question is if it is considered ok to do changes in the Delphi source files? If it isn't, is there a better way to do this? As long as it's just me using it I guess there is no big problems, but it's hard to distribute the source to others, and then there's also future changes in Delphi to consider. The rest of this lenghty post is just details about what I'm actually doing :-)

I've changed this in Soap.OPToSOAPDomConv.pas (row 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);

To: (not the prettiest implementation I guess)

New variables in the procedure:

Context: TRttiContext;       
RttiProperty:  TRttiProperty;

Row 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;

Some details as to what I am doing is probably helpful. The background is that the imported wsdl from the SOAP Web Service generates a huge unit, that consists of about 2000 classes and 300k rows of code. The web service design is out of my control. The WSDL Importer makes all of these classes visible in RTTI, which consumes the RTTI space and the unit won't compile.

I've refactored the code here and there and now have a working implementation. While refactoring I found that I could cut away some 50000 rows of redundant code by using generics. Since Delphi won't compile the imported wsdl "as is" anyway I will have to maintain the unit manually whenever new methods becomes available in the web service, so I want to make it as readable as possible.

Basically I'm changing according to below. The sample has very simplified classes, and the sample actually has more lines in the refactored code, but considering the original classes has a lot of procedures etc this method really makes the unit a lot more readable, and it's also easier to compose the 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;

To:

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;

Kind Regards,

Dan

1
Wow. Brave. Is there some reason why you don't just serialize your generics and THEN remote them? Since the SOAP Remoting code is often fixed and updated by people who want to work around its bugs it is not as problematic to change it and rebuild it, as it would be to change System.pas. If it was me, I'd want to have unit test coverage for this code if I was changing it.Warren P
@Warren. Thanks, good point about the SOAP Remoting code. I felt this was kind of daring myself... I don't really understand what you ment by serializing the generics before remoting them (I've just learned about generics), can you explain that a bit further? I basically have no reason other than just shrinking down the amount of code as well as scoping to that RTTI space does not run out.dahook
Instead of remoting TGenericList<TLCar>, remote a JSON string of TLCARS stored in a string parameter.Warren P
@Warren. OK, thanks. That's a bit over my head, I'm afraid (I wouldn't now where to start... :-) ) I think I'll stick with the "normal" class definitions and just hide the subclasses inside them. Then it's just a matter of cut and paste in the unit generated by the wsdl importer to limit the scope the subclasses. Time consuming and boring, but it seem to work.dahook

1 Answers

1
votes

There are two things to consider when making modifications such as this. First of all, can the change have impact on existing functionality. As in this case I would say that it is safe because the functionality is new to this operation, so there shouldn't be any unexpected behavior. Second part is the evolution development environment. Problem with the evolution of environment is that bindings between actions may change and that can lead to unexpected things. At this moment it is alright to assume that XE2 to has had it's share of updates. If it wouldn't, you would have to keep an eye when patching or updating. The second kind of change like the one from XE2 to XE3 can be handled better. Just put the following in top of Soap.OPToSOAPDomConv.pas:

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

When get error while compiling you might remember vaguely that there was something about that file... So in short, it is not bad things as far as try to evaluate impact and adapt to environment changes. Hopefully this was what you wanted to know.