Note: Some changes have been made in this article, and some problems have been corrected. Because the original text cannot be modified, You Have To resend it.
Not long ago, I received emails from several friends about a problem they encountered when developing a WebService application as described in this article: An AV error occurred while compiling the sample program in this article using ISAPI. This article has been modified based on the improved program in this example. Note the bold content in this article.
-- 2002-8-17
In this article, we will provide a slightly complex example to implement the transmission of custom data types through soap. The function of this example is to obtain the data table content from the server through the ADO data access control, and then pass it to the client through soap for display.
Server:
1. New | WebServices | SOAP server application. For example, the icons in the upper left corner are identical to those in Delphi 6 + Update 2:
Select the web app debugger executeable type. The coclass name is wadsoapdemo2, for example:
After confirming, the system will automatically prompt whether to create an interface. For example, if you are sure to open the new interface wizard, if you want to add an interface later, you can select the SOAP server interface in new | WebServices to open the new interface Wizard:
2. In the new interface wizard, enter the interface name datatable to generate a SOAP server interface:
For other instructions on this wizard, see "C ++ Builder 6 bizsnap/soap/WebService (1) -- A Hello world! (1).
3.(Note: This part of the original text is incorrect. It is modified now)Create a new datamodule and add the following four database controls: adoconnection1, adodataset1, datasetprovider1, and clientdataset1. The attribute settings are as follows:
Adoconnection1 |
Connectionstring = "provider = sqloledb.1; persist Security info = false; user id = sa; initial catalog = northwind; Data Source = raptor/neutrino "; Loginprompt = false; |
Adodataset1 |
Connection = adoconnection1; Commandtext = "select firstname, lastname from employees "; |
Datasetprovider1 |
Dataset = adodataset1; |
Clientdataset1 |
Providername = datasetprovider1; |
The completed datamodule is as follows:
(Additional instructions on this part): "Unable load dbexpint. dll" error occurs when dbexpress is used for ISAPI, so use ADO instead. In addition, because there is no adoclientdataset control, otherwise, if it is Bde/dbexpress/ibexpress, only two controls are required. To put the data control in a separate datamodule, instead of a webmodule, see Web Application Execution Process-differences between wad, CGI, and ISAPI. This example is for reference only, we recommend that you use the SOAP server data module (for example, C ++ Builder 6 bizsnap (3) -- datasnap Database Application) in applications involving database operations.
4. saveall, unit1 is named mainwm, unit2 is named demo2dm, project1 is named demo2, and able is not renamed;
5. Add a custom class named tdatasetpack in the header file (datatable. h) of the interface unit, as shown below:
class TDataSetPack : public TRemotable {private : int FCount; AnsiString FXMLData;public : __fastcall TDataSetPack( TCustomClientDataSet * aClientDataSet ) : TRemotable(), FCount( aClientDataSet->RecordCount ), FXMLData( aClientDataSet->XMLData ) { }__published: __property int Count = { read = FCount }; __property AnsiString XMLData = { read = FXMLData };};
The custom soap data type must be derived from the tremotable class, which is the same as that of Delphi. The xmldata attribute of clientdataset is added from Delphi 6. In fact, xmldata already contains the number of records. The Count attribute is not required. This attribute is added to demonstrate the use of custom soap data types. Note: Add # include <dbclient. HPP> to this header file.
5. define and implement the getemployeetable function. The method is the same as that in (1). below is the interface header file (datatable. h) and the unit file (datatable. the interface/class definition in CPP, the method we add, and its implementation:
// Datatable. H _ interface interface_uuid ("{CF057C28-4130-4508-9F24-0BBD1C2CA5F0}") idatatable: Public iinvokable {public: Virtual tdatasetpack * getemployeetable (void) = 0; // Add method}; typedef delphiinterface
_ Di_idatatable; // datatable. cppclass tdatatableimpl: Public tinvokableclass, public idatatable {public: tdatasetpack * getemployeetable (void); // Add method/* iunknown */hresult stdmethodcalltype QueryInterface (const guid & IID, void ** OBJ) {return getinterface (IID, OBJ )? S_ OK: e_nointerface;} ulong stdmethodcalltype addref () {return tinterfacedobject: _ addref ();} ulong stdmethodcalltype release () {return tinterfacedobject: _ release ();} /* ensures that the class is not abstract */void checkvalid () {delete new tdatatableimpl () ;}; // Implementation of the new method: // if it is a CGI/ISAPI application, you need to create datamodule1. For the corresponding changes, see the description later. // open clientdataset and construct tdatasetpack, // disable clientdataset and database connection // return result tdatasetpack * tdatatableimpl: getemployeetable (void) {// for CGI/ISAPI, this sentence is required. // application-> createform (_ classid (tdatamodule1), & datamodule1); datamodule1-> clientdataset1-> open (); tdatasetpack * P = new tdatasetpack (datamodule1-> clientdataset1); datamodule1-> clientdataset1-> close (); datamodule1-> adoconnection1-> close (); Return P ;}
Except for the implementation of methods, the other parts are basically the same as those in (1. The implementation function of this method, as described in the program, is used to retrieve a dataset and convert it to the data type we define and return it.
(Additional instructions on this part): Note that for CGI/ISAPI applications, datamodule1 is a dynamic creation (the reason is as described in "Web Application Execution Process-differences between wad, CGI, and ISAPI ), therefore, the corresponding project | source (demo2cgi. CPP/demo2isapi. in CPP), The automatically created statement is removed as follows.
Application-> createform (_ classid (twebmodule1), & webmodule1 ); // do not use this sentence for CGI/ISAPI applications. // application-> createform (_ classid (tdatamodule1), & datamodule1); Application-> Run ();
6. The registration interface and its implementation class are the same as those in (1), so we will not repeat them here.
7. compile and generate: demo2.exe;
Run demo2.exe first, and then start the web app debugger after registration. Open your browser and enter http: // localhost: 1024/demo2.wadsoapdemo2 to view a standard soap application description page. Click the corresponding link to view the relevant WSDL, here we can see the description of the custom data type, as shown in the following WSDL segment:
<types> <xs:schema targetNamespace="urn:DataTable" xmlns="urn:DataTable"> <xs:complexType name="TDataSetPack"> <xs:sequence> <xs:element name="Count" type="xs:int"/> <xs:element name="XMLData" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema> </types>
Client Program:
1. New | application creates a general VCL application;
2. saveall, unit1 is named clnmain, and project1 is named client;
3. New | Web Services Importer:
Enter http: // localhost: 1024/demo2.wadsoapdemo2/WSDL/idatatable in the URL,
If you can see the correct XML document in the browser, select "Next" to generate the import result, for example:
The data type tdatasetpack, The idatatable interface, and its method getemployeetable defined on the server are available. After the selection, the interface unit is generated;
4. saveall: The idatatable unit is not renamed and saved, and then # include idatatable. h In clnmain;
5. Put a clientdataset, datasource, DBGrid, button, label, and other controls on the form. The following table lists the attributes of these controls:
Clientdataset1 |
All default |
Performance1 |
Dataset = clientdataset1; |
Dbgrid1 |
Datasource = performance1; |
Button1 |
Caption = "fetch data "; |
Label1 |
Caption = "count: 0 "; |
The completed form is as follows:
6. Double-click button1 and enter the following program:
void __fastcall TForm2::Button1Click(TObject *Sender){ TDataSetPack * p = GetIDataTable()->GetEmployeeTable(); Label1->Caption = AnsiString( "Count:" ) + IntToStr( p->Count ); ClientDataSet1->XMLData = p->XMLData;}
7. Compile and run the program. Press button1 and dbgrid1 to display the content of the dataset returned by the server, and label1 to display the number of records, as shown in (this figure is still the data when Interbase is used );
This is just a simple example of database access. You can only retrieve the dataset from the server. In C ++ Builder 6, we have already combined the data set of the Midas/datasnap and the soap/WebService, you can use soap/WebService to implement very powerful database operation capabilities, which will be described in future articles.
[Mental Studio] apr.30-02