Study Notes on Web Service and code generation technologies

Source: Internet
Author: User
Tags msmq

Recently, we developed a Web service tool that uses a little bit of code generation technology.
1 .. there are two main types of Web Services, asmx web service and WCF web service. the latter is the enhanced feature version of the former. for example, the former only supports the HTTP protocol and can only be built on IIS, and security also depends on IIS. The latter also supports MSMQ and Enterprise Service (which seems to be mainly for com, some additional configurations (for example, two-way message transmission can be configured), and multiple endpoints can be set, so that the same service can be accessed in different ways. it can be built on Windows Service or the console program is called self-hosting. there are several intermediate products between the two, such as asmx web service with soap extension and WSE attached.

2. the basic concept of Web Service is: the client and the server interact with each other through XML, and the XML and object are converted through serialization and deserialization at both ends. the server uses XML descriptions (such as standard WSDL) for the interfaces called by the customer. The customer generates a local proxy based on the description, sends a SOAP message to the server through the proxy, and calls the service.

3. asmx Web Service
1) configuration on IIS:
For iis6.0, you need to create a virtual directory pointing to the directory where the asmx file is located. For iis7.0, create a new site.
If ASP. NET is installed before IIS, you need to run Windows/microsoft.net/framework/v2.0.57 (assuming that you use reg_iis.exe-I under Asp.net 2.0region to register Asp.net.
If you still cannot recognize asmx, You need to associate asmx with aspnet_isapi.dll in IIS. After Restarting IIS, You can correctly parse asmx.

 

2) Basic Structure:
The server needs an asmx file. The following is an example. The content is very simple, just a direve ve:
<% @ WebService Language = "C #" codebehind = "~ /App_code/service. cs "class =" service "%>

The real implementation code is put in the service. CS file.

The following is the content of a simple service. CS file:
Using system;
Using system. Web;
Using system. Web. Services;
Using system. Web. Services. Protocols;
Using webservicelib;

[WebService (namespace = "http://tempuri.org/")]
[Webservicebinding (conformsto = wsiprofiles. basicprofile1_1)]
Public class service: system. Web. Services. WebService
{
Public Service (){

// Uncomment the following line if using designed components
// Initializecomponent ();
}

[Webmethod]
Public String testnoparam (){
Return "Hello World ";
}

[Webmethod]
Public String testsimpletype (INT intvalue, string stringvalue)
{
Return "good ";
}

[Webmethod]
Public void testcomplextype (servicelib Lib)
{
// Return Lib. testint. tostring () + "" + Lib. teststring;
}
}

It can be seen that, first, this class must inherit system. Web. Services. WebService. Second, it must have the WebService attribute, and the method called by the customer must have the webmethod attribute.

The client needs a proxy class. below is

If this proxy class is developed in Visual Studio, you can add a web referenceto automatically generate it, or use the wsdl.execommand line program to generate it. If wsdl.exe is used, the URI parameter of a wsdl is specified.

3) dynamic generation of proxy classes
Another method is to use the program to dynamically generate and dynamically generate code, mainly using the codedom and reflection namespaces.
There are two methods to dynamically generate an assembly. one is to use system. reflection. the methods in the emit namespace are converted into an assemblybuilder instance, and a modulebuilder instance is used to generate a typebuilder, that is, a class is generated, next, you can use methods in typebuilder to generate methods. obviously, this method is troublesome.
Another method is to use codedom to generate class code and then compile the code (also using the method in codedom) into assembly. codedom itself is like xmlwriter, which provides a means of manually splicing code.
In. net, there is a servicedescriptionimporter class in the system. Web. Services. Description namespace that provides support for generating proxy code. Specific implementation code (only list key parts ):

Using system. xml;
Using system. xml. Schema;
Using system. xml. serialization;
Using system. Web. Services. description;
Using system. Web. Services. discovery;
Using system. Web. Services. Protocols;

......
Servicedescriptionimporter importer = new servicedescriptionimporter ();
Importer. protocolname = "Soap ";
Importer. Style = servicedescriptionimportstyle. client;

Discoveryclientprotocol DCC = new discoveryclientprotocol ();
DCC. discoverany (wsdlurl );
DCC. resolveall ();

Foreach (Object doc in DCC. Documents. values)
{
If (Doc is system. Web. Services. description. servicedescription)
{
Importer. addservicedescription (DOC as system. Web. Services. description. servicedescription, String. Empty, String. Empty );
}
Else if (Doc is XMLSCHEMA)
{
Importer. schemas. Add (DOC as XMLSCHEMA );
}
}

If (importer. servicedescriptions. Count = 0)
{
Throw new exception ("No Web Service is found for the given address! ");
}

Codecompileunit CCU = new codecompileunit ();
CCU. namespaces. Add (New codenamespace (""); // you can add the required namespace here.
Servicedescriptionimportwarnings warnings = importer. Import (CCU. namespaces [0], CCU );
If (warnings & servicedescriptionimportwarnings. nocodegenerated)> 0)
{
Throw new exception ("An error occurred while generating the proxy class! ");
}

If code is directly generated, you can do this:

Microsoft. CSHARP. csharpcodeprovider provider = new Microsoft. CSHARP. csharpcodeprovider ();
Streamwriter Sw = file. createtext (<File Name> );
Icodegenerator codegenerator = provider. creategenerator (SW );
Codegeneratoroptions = new codegeneratoroptions ();
Codegenerator. generatecodefromcompileunit (CCU, SW, codegeneratoroptions );
Sw. writeline ();
Sw. Flush ();

The following code can be used for compilation:
Icodecompiler compiler = provider. createcompiler ();
Compilerparameters Options = new compilerparameters (
New String [] {
"System. dll ",
"System. Web. Services. dll ",
"System. xml. dll"

}, "C: // temp // test. DLL ", // This is the DLL to be generated, if you use string. empty, use a randomly generated file name in the temporary directory to generate a DLL. If you want to generate an EXE, specify options. generateexecutable = true.
False );

Options. generateinmemory = false;
Compilerresults Results = compiler. compileassemblyfromdom (options, CCU );

However, after opening the code, you can find that only the fields of methods and classes are generated, and no property is generated. You can use the following method to generate the property:

Foreach (codetypedeclaration in CCU. namespaces [0]. types)
{
Arraylist fields = new arraylist ();
Foreach (codetypemember member in codetypedeclaration. Members)
{
If (member is codememberfield)
{
Fields. Add (member );
}
}

Foreach (codememberfield field in fields)
{
Codememberproperty property = new codememberproperty ();
Property. Name = field. Name;
Property. type = field. type;
Property. Attributes = memberattributes. Public;
Property. customattributes. addrange (field. customattributes );

Field. Name = "_" + field. Name;
Field. Attributes = memberattributes. Private;
Field. customattributes. Clear ();

// Add get and set methods for Attributes
Property. getstatements. Add (
New codemethodreturnstatement (
New codefieldreferenceexpression (
New codethisreferenceexpression (), field. Name )));

Property. setstatements. Add (
New codeassignstatement (
New codefieldreferenceexpression (
New codethisreferenceexpression (), field. Name ),
New codepropertysetvaluereferenceexpression ()));

Codetypedeclaration. members. Add (property );
}

}
This method applies only to the Web Service of asmx.

4) use a program to call the agent class generated by codedom
First use reflection to find the method to be called, and then use the invoke method to call:

Public static object invokewebmethod (string proxyassembly, string methodname, object [] parameters)
{
Object returnvalue = NULL;
Assembly = assembly. LoadFile (proxyassembly );

Foreach (type in assembly. gettypes ())
{
If (! Type. issubclassof (typeof (soaphttpclientprotocol )))
{
Continue;
}

Soaphttpclientprotocol service = activator. createinstance (type) as soaphttpclientprotocol;

Type servicetype = service. GetType ();

Foreach (methodinfo method in servicetype. getmethods (
Bindingflags. Public | bindingflags. declaredonly
| Bindingflags. instance ))
{
If (method. Name = methodname)
{
Try
{
Returnvalue = method. Invoke (Service, parameters );
Return returnvalue;
}
Catch (exception ex)
{
Throw ex;
}
}
}
}
}

4. WCF Web Service
1) new concept:
Compared with asmx, some new concepts are added:
Binding: indicates the connection method. asmx only supports HTTP. This is not the concept. WCF also supports TCP, MSMQ, and enterprise service. Therefore, this concept specifies the protocol used for the connection.
Endpoint: In asmx, Web Service uses a URL for access, and does not have this concept. Therefore, WCF supports multiple access points.
Behavior: Configure connections, such as setting one-way or two-way message transmission, http get or post.
2) Hosting
On the basis of IIS hosting, WCF adds the self-hosting method:
Public class Program
{
Static void main (string [] ARGs)
{
Using (servicehost = new servicehost (typeof (service1 )))
{
Servicehost. open ();
Console. writeline ("service started ...");
Console. Readline ();
Servicehost. Close ();
}
}
}
The service1 class contains the service implementation code.
3) Basic Structure
Compared with the asmx web service, the WCF Web Service is a little more complex, with more control methods and flexibility.
The simple code of the server is as follows:
Using system;
Using system. Collections. Generic;
Using system. LINQ;
Using system. runtime. serialization;
Using system. servicemodel;
Using system. text;

Namespace testwcfservice
{
// Note: If you change the Interface Name "iservice1" here, you must also update the reference to "iservice1" in APP. config.
[Servicecontract]
Public interface iservice1
{
[Operationcontract]
String getdata (INT value );

[Operationcontract]
Compositetype getdatausingdatacontract (compositetype composite );

// Todo: add your service operations here
}

// Use a data contract as specified strated in the sample below to add composite types to service operations
[Datacontract]
Public class compositetype
{
Bool boolvalue = true;
String stringvalue = "hello ";

[Datamember]
Public bool boolvalue
{
Get {return boolvalue ;}
Set {boolvalue = value ;}
}

[Datamember]
Public String stringvalue
{
Get {return stringvalue ;}
Set {stringvalue = value ;}
}
}
}
This is the code automatically generated using the Visual Studio template. The servicecontract attribute corresponds to the WebService attribute in asmx, and operationcontract corresponds to the webmethod attribute. datacontract is a new attribute for user-defined types. in addition, the class is replaced by an interface.
Both endpoint and behavior are stored in the app. config file:
<System. servicemodel>
<Services>
<Service name = "testwcfservice. service1" behaviorconfiguration = "testwcfservice. service1behavior">
<Host>
<Baseaddresses>
<Add baseaddress = "http: // localhost: 8732/testwcfservice/service1/"/>
</Baseaddresses>
</Host>
<! -- Service endpoints -->
<! -- Unless fully qualified, address is relative to base address supplied above -->
<Endpoint address = "" binding = "wshttpbinding" Contract = "testwcfservice. iservice1">
<! --
Upon deployment, the following identity element shoshould be removed or replaced to reflect the identity under which the deployed service runs. If removed, WCF will infer an appropriate identity automatically.
-->
<Identity>
<DNS value = "localhost"/>
</Identity>
</Endpoint>
<! -- Metadata endpoints -->
<! -- The metadata exchange endpoint is used by the Service to describe itself to clients. -->
<! -- This endpoint does not use a secure binding and shoshould be secured or removed before deployment -->
<Endpoint address = "mex" binding = "mexhttpbinding" Contract = "imetadataexchange"/>
</Service>
</Services>
<Behaviors>
<Servicebehaviors>
<Behavior name = "testwcfservice. service1behavior">
<! -- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<Servicemetadata httpgetenabled = "true"/>
<! -- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<Servicedebug includeexceptiondetailinfaults = "true"/>
</Behavior>
</Servicebehaviors>
</Behaviors>
</System. servicemodel>"
The client can generate a service referencein Visual Studio or by using the svcutil.exe command line program. In addition to the proxy class, the configuration file is also generated:
<System. servicemodel>
<Bindings>
<Wshttpbinding>
<Binding name = "wshttpbinding_iservice1" closetimeout = "00:01:00"
Opentimeout = "00:01:00" receivetimeout = "00:10:00" sendtimeout = "00:01:00"
Bypassproxyonlocal = "false" transactionflow = "false" hostnamecomparisonmode = "strongwildcard"
Maxbufferpoolsize = "524288" maxcompute edmessagesize = "65536"
Messageencoding = "text" textencoding = "UTF-8" usedefaultwebproxy = "true"
Allowcookies = "false">
<Readerquotas maxdepth = "32" maxstringcontentlength = "8192" maxarraylength = "16384"
Maxbytesperread = "4096" maxnametablecharcount = "16384"/>
<Reliablesession ordered = "true" inactivitytimeout = "00:10:00"
Enabled = "false"/>
<Security mode = "message">
<Transport clientcredentialtype = "Windows" proxycredentialtype = "NONE"
Realm = ""/>
<Message clientcredentialtype = "Windows" negotiateservicecredential = "true"
Algorithmsuite = "default" establishsecuritycontext = "true"/>
</Security>
</Binding>
</Wshttpbinding>
</Bindings>
<Client>
<Endpoint address = "http: // localhost: 8732/testwcfservice/service1 /"
Binding = "wshttpbinding" bindingconfiguration = "wshttpbinding_iservice1"
Contract = "servicereference1.iservice1" name = "wshttpbinding_iservice1">
<Identity>
<DNS value = "localhost"/>
</Identity>
</Endpoint>
</Client>
</System. servicemodel>
The most important here is the endpoint, which includes the endpointaddress used for the connection, the binding protocol, and the servicereference1.iservice1.

4) proxy class code generation
To use the system. servicemodel and system. servicemodel. Description namespaces, the following code is slightly rewritten on msdn:
Arraylist endpoints = new arraylist ();
Metadataexchangeclient mexclient = new metadataexchangeclient (New endpointaddress (serviceuri); // The serviceuri parameter is the address of the WCF Web Service metadata, which is defined in the server configuration file, generally, add/MEX to the endpoint address.
Mexclient. resolvemetadatareferences = true;
Metadataset metadocs = mexclient. getmetadata ();
Wsdlimporter importer = new wsdlimporter (metadocs );
Servicecontractgenerator contractgenerator = new servicecontractgenerator ();

Collection contracts = importer. importallcontracts ();
Foreach (serviceendpoint endpoint in importer. importallendpoints ())
{

Endpoints. Add (endpoint); // store the endpoint for future use when calling a method
}
Foreach (contractdescription contract in contracts)
{
Contractgenerator. generateservicecontracttype (contract );
}
If (contractgenerator. errors. Count! = 0)
{
Return "An error occurred while generating the code! ";
}
You can write the generated code to the file, as shown in the msdn example:
Codedomprovider provider = codedomprovider. createprovider ("C #");
Codegeneratoroptions Options = new codegeneratoroptions ();
Indentedtextwriter textwriter = new indentedtextwriter (New streamwriter (proxyclass ));
Provider. generatecodefromcompileunit (contractgenerator. targetcompileunit, textwriter, options );
Textwriter. Close ();
If you want to compile, note that the WCF is based on.. NET Framework Versions later than 3.0, but codedom has only 2.0 so far. servicemodel. DLL and system. runtime. serialization. the dll must specify the path:
Compilerparameters compileoptions = new compilerparameters ();
Compileoptions. referencedassemblies. Add ("system. dll ");
Compileoptions. compileroptions = "/lib:/" "+ environment. getfolderpath (environment. specialfolder. ProgramFiles)
+ "// Reference assemblies // Microsoft // framework // V3.0/" "; // This path includes system. servicemodel. dll and system. runtime. serialization. dll.
In addition, in Framework 2.0, icodecompiler is used, and 3.0 prompts that this method is outdated. simply use the provider generated above:
Compileoptions. outputassembly = proxyassembly;
Compileoptions. includedebuginformation = false;
Compileoptions. generateinmemory = false;
Compileoptions. generateexecutable = false;
Codecompileunit [] ccus = new codecompileunit [1];
Ccus [0] = contractgenerator. targetcompileunit;
Compilerresults Results = provider. compileassemblyfromdom (compileoptions, ccus );
Foreach (compilererror err in results. Errors)
{// The following statements help debugging
String T = err. errortext;
Int S = err. line;
}
The client configuration file is not generated here, but the required information has been retrieved using the importallendpoints and mportallcontracts methods. It is not difficult to use this information to generate a configuration file or dynamically call it in a data structure.

5) Use reflection to call the proxy class:
The following is an example:
Public static object invokewcfmethod (string proxyassembly, string methodname, object [] parameters, arraylist endpoints) // The endpoints generated above
Object returnvalue = NULL;
Object serviceobject = NULL;
Assembly = assembly. LoadFile (proxyassembly );

Foreach (type in assembly. gettypes ())
{
If (type. isinterface |! (Type. namespace = NULL ))
{
Continue;
}

If (endpoints! = NULL & endpoints. Count> 0)
{
Foreach (serviceendpoint endpoint in endpoints)
{
Bool isfinished = false;
Type contracttype = assembly. GetType (endpoint. Contract. Name );

If (contracttype! = NULL)
{
Foreach (type interfacetype in type. getinterfaces ())
{
If (interfacetype = contracttype)
{
Serviceobject = convert. changetype (activator. createinstance (type, new object [] {endpoint. Binding, endpoint. Address}), type );
Isfinished = true;
Break;
}

}
}
If (isfinished)
{
Break;
}
}
}

Foreach (methodinfo method in type. getmethods (
Bindingflags. Public | bindingflags. declaredonly
| Bindingflags. instance ))
{

If (methodname = method. Name)
{
Try
{
Returnvalue = method. Invoke (serviceobject, parameters );
}
Catch (exception ex)
{
Throw ex;
}
}
}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.