Gekko Software
Services
Portfolio
Publications
Contact
Blog
About

Creating a Delphi Component for a .net XML dataset

Wrapping up the DNdataset

The DNdataset, described in detail in another paper, can wrap up a XMLdataset as returned by a .NET webservice. The objects of this class need the tHTTPrio component to get to the webservice supplying the dataset. I will build a component  which descends from the tHHTPrio component, manages a DNdataSetobject and publishes all data relevant to other Delphi components.

The component invokes the webservice via its public Get and Update method. The component internally keeps an invocation state to guide the process

type
   tDataRequestState = (RequestLoad, RequestSave, RequestNone);

Get

Calling the Get method will set the component's state and fire the OnRequestInvocation event. In the assigned eventhandler the user of the component is requested to invoke the method returning the XML dataset. To conclude the state is set back to Request None.

procedure TGekkoDotNetDataSet.Get;
   begin
   fDataRequestState:= RequestLoad;
   if Assigned(fOnRequestGetInvocation) then
      fOnRequestGetInvocation(self);
   fDataRequestState:= RequestNone;
   end;

Intercepting the response from a webservice

The responses from the webservice can be caught in the virtual DoAfterExecute method of the tRio class.  The response contains all the XML the DNdataset needs to fill its values. I override the method, read the response to construct the object, and pass the response on to the inherited  DoAfterExecute.

procedure TGekkoDotNetDataSet.DoAfterExecute(const MethodName: string; Response: TStream);
   begin
   case fDataRequestState of
        RequestLoad : begin
                      fDNdataset:= tDNdataSet.Create(self);
                      fDNdataset.CreateFromStream(Response);
                      end;
        end;
   inherited DoAfterExecute(MethodName, Response);

   end;

Update

Calling the Update method sets, after a check if there are any updates available, the state to RequestSave and it will fire the OnRequestUpdateInvocation event. The user of the component is requested to invoke the update method. The signature of the update method is fixed. It has one parameter, an xmlDocument typed by a schema. The component will provide the actual contents of this document.

procedure TGekkoDotNetDataSet.Update;
   begin
   if fDNdataSet.IsUpdated then
      begin
      fDataRequestState:= RequestSave;
      if Assigned(fOnRequestUpdateInvocation) then
         fOnRequestUpdateInvocation(self);
         fDataRequestState:= RequestNone;
      end;
   end;

Intercepting the request to a webservice.

The request to the webservice can be intercepted in the virtual DoBeforeExecute method of tRio. The component does override this method. This takes some extra attention as Borland decided to change the signature of this method when moving from D6 to D7. In D6 the request is a string and the Response is a stream. The request also is a var parameter in D6, which implies that the method can effectively change the request. Which is what the component wants to do, it will substitute the parameter with the UpdateXML document created by the DNdataSet object.

{$IFDEF VER140} { Delphi 6 }
procedure TGekkoDotNetDataSet.DoBeforeExecute(const MethodName: string; var Request: InvString);
   begin
   case fDataRequestState of
        RequestLoad : FreeAndNil(fDNdataSet);
        RequestSave : Request := AnsiReplaceStr(Request, '<' + fParamName + '/>',
                                                fDNdataSet.UpdateXML(fParamName));
   end;

   inherited DoBeforeExecute(MethodName, Request);

   end;
{$ENDIF}

After which the inherited DoBeforeExecute is called.

There are many benefits in working with the request as a stream. Borland chose only to change the DoBeforeEvent method, not the signature of the OnBeforeExecute event of tHTTPrio componenet. To quote rio.pas :

{ Ideally we would change the signature of this event to take a Stream.
The change to stream was necessary for attachment and encoding support.
And it makes the event consistent.... However, for the sake of
backward compatibility.... }

For the sake of backward compatibility the signature of the associated event handler, OnBeforeExecute did not change. The implementation of the method changed, the fact that the OnbeforeExcute signature had a var parameter is neglected in D7.

{ NOTE: We ignore the var WideString passed in... ???? }

Which rises a version problem. I will have to create a separate override of this method for D6 and D7. Using compiler directives the right version of the method will be used when the component is build.

{$IFDEF VER150} {Delphi 7}
procedure TGekkoDotNetDataSet.DoBeforeExecute(const MethodName: string; Request: TStream);
var RequestAsString : string;
    StrStrm : TStringStream;
   begin
   case fDataRequestState of
        RequestLoad : FreeAndNil(fDNdataSet);
        RequestSave : begin
                      StrStrm := TStringStream.Create('');
                      try
                         StrStrm.CopyFrom(Request, 0);
                         Request.Position := 0;
                         RequestAsString := AnsiReplaceStr(StrStrm.DataString,
                                                          '<' + fParamName + '/>',
                                                           fDNdataSet.UpdateXML(fParamName));
                         StrStrm.Position:= 0;
                         StrStrm.WriteString(RequestAsString);
                         Request.Position:= 0;
                         Request.CopyFrom(StrStrm, 0);
                      finally
                         StrStrm.Free;
                      end;
                      end;
         end;

   inherited DoBeforeExecute(MethodName, Request);

end;
{$ENDIF}

This implementation uses a temporary tStringStream. The request is copied into this stream after which the string representation, in DataString, can be accessed. In this string the parameter is substituted with the xmlDocument generated by the DNdataSet. The resulting string is written back to the string-stream. I start writing at position 0 so the new request is copied over the original request. I make the assumption that the request always grows in size. When the request is ready it is copied back to the original request stream. Again I overwrite the original request. The inherited DoBeforeExecute will receive the changed request.

To sum this up

protected
{ Protected declarations }
{$IFDEF VER140} { Delphi 6 }
   procedure DoBeforeExecute(const MethodName: string; var Request: InvString); override;
{$ENDIF}

{$IFDEF VER150} {Delphi 7}
   procedure DoBeforeExecute(const MethodName: string; Request: TStream); override;
{$ENDIF}

procedure DoAfterExecute(const MethodName: string; Response: TStream); override;

The implementations are also enclosed in IFDEF directives. Your compiler will select the right source.

Thanks for drBob for helping me getting this to work, thanks to Alex Kovalev for making clear to me why my previous code didn't work in D7. I have to admit I got stuck in 6.02 (pro).

Installing the component

The component, the dotnetdataset class and all other helper classes are in the GekkoDotNet pacakage. This package can be opened in Delphi to build and install it. The component will install itself on the webservices page. In the next paper I will show how to use the GekkoDotNetDataSet component.

What's next ?


© Peter van Ooijen. Gekko Software, 2001-2010