Dynamic calling WebService (C #)

Usually when we need to call WebService in the program, we use “Add Web Reference” to let the VS.NET environment generate a service proxy for us, and then call the corresponding Web service. This makes the work easier, but it is tied to the URL, method name, and parameters that provide the Web service. This is the limitation of VS.NET automatically generating a Web service proxy for us. If the URL for publishing the Web service changes one day, we need to let VS.NET generate the proxy again and recompile it. In some cases, this may be intolerable, and we need the ability to dynamically call WebService. For example, we can save the URL of the Web service in the configuration file, so that when the service URL changes, only the configuration file needs to be modified.
Having said so much, in fact we want to implement such a function:

public static object InvokeWebService(string url, string methodname, object[] args)

Among them, url is the address of the web service, methodname is the name of the service method to be called, and args is to be called The parameters required by the web service, and the return value is the result returned by the web service. To achieve such a function, you need these skills: reflection, CodeDom, programming using C# compiler, WebService. After understanding this knowledge, you can easily implement dynamic invocation of web services:

#region InvokeWebService //Dynamic invocation of web services public static object InvokeWebService(string url , string methodname, object[] args) {return WebServiceHelper.InvokeWebService(url ,null ,methodname ,args);} public static object InvokeWebService(string url, string classname, string methodname, object[] args) {string @namespace = " EnterpriseServerBase.WebService.DynamicWebCalling"; if((classname == null) ||(classname == "")) {classname = WebServiceHelper.GetWsClassName(url);} try {//Get WSDL WebClient wc = new WebClient(); Stream stream = wc.OpenRead(url+"?WSDL"); ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd,"",""); CodeNamespace cn = new CodeNamespace (@name space); //Generate client proxy class code CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn ,ccu); CodeDomProvider provider = new CSharpCodeProvider();//Set compilation Parameter CompilerParameters cplist = new CompilerParameters(); cplist.GenerateExecutable = false; cplist.GenerateInMemory = true; cplist.ReferencedAssemblies.Add("System.dll"); cplist.ReferencedAssemblies.Add("System.XML.dll"); cplist .ReferencedAssemblies.Add("System.Web.Services.dll"); cplist.ReferencedAssemblies.Add("System.Data.dll"); //CompilerResults cr = provider.CompileAssemblyFromDom(cplist, ccu); if( true == cr.Errors.HasErrors) {System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach(System.CodeDom.Compiler.CompilerError ce in cr.Errors) { sb.Append(ce.ToString()); sb.Append(System.Environment.NewLine);} throw new Exception(sb.ToString());} //Generate proxy instance and call method System.Reflection.Assembly assembly = cr.CompiledAssembly; Type t = assembly.GetType(@namespace+"."+classname,true,true); object obj = Activator.CreateInstance(t); System.Reflection.MethodInfo mi = t.GetMethod(methodname); return mi.Invoke(obj,args);} catch(Exception ex) {throw new Exception(ex.InnerException.Message,new Exception(ex.InnerException.StackTrace));}} private static string GetWsClassName(string wsUrl) {string[ ] parts = wsUrl.Split('/'); string[] pps = parts[parts.Length-1].Split('.'); return pps[0];} #endregion

A good description of the function of each code segment, let’s take a look at an example, this example is to access the http://www.webservicex.net/globalweather.asmx service to get the weather conditions of major cities.

string url = "http://www.webservicex.net/globalweather. asmx"; string[] args = new string[2]; args[0] = this.textBox_CityName.Text; args[1] = "China"; object result = WebServiceHelper.InvokeWebService(url ,"GetWeather" ,args); this.label_Result.Text = result.ToString() ;

In the above example, two parameters are used to call the web service, the first is The name of the city, and the second is the name of the country. The Web service returns an XML document from which weather conditions such as temperature and wind can be parsed.

Finally, although C# is still a static language, its dynamic capabilities are also very powerful. If you don’t believe me, you can take a look at Spring. Net’s AOP implementation, this “non-intrusive” AOP implementation is much more beautiful than the usual .NET declarative AOP implementation (usually through AOP Attribute).

using System;using System.Collections.Generic;using System.Text;using System.Xml;using System.Net;using System.Web .Services.Description;using System.CodeDom;using System.CodeDom.Compiler;using System.Reflection;namespace WindowsServiceWebDefaultHotCity{ /// 
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text ;using System.Windows.Forms;namespace WindowsApplication1{ public partial class Form1: Form {private string _url = "http://www.baidu.com"; public Form1() {InitializeComponent(); init_Data();} public void init_Data() {WindowsServiceWebDefaultHotCity.WebServiceAgent agent = new WindowsServiceWebDefaultHotCity.WebServiceAge nt(_url); object[] args = new object[6]; args[0] = "PEK"; args[1] = "CAN"; args[2] = ""; args[3] = "2008- 08-02"; args[4] = "00:00"; args[5] = "own_9588"; string text=agent.Invoke("GetAllFlight", args).ToString(); textBox1.Text = text;} }}
We all know that calling WS can add a WEB reference to WS in the project. However, what if we don't want to add references, but dynamically reference them in the code? First of all, we should think that the implementation of WS is also in the form of a class. Secondly, WS is described through WSDL (using SOAP protocol) during transmission. Therefore, we need to obtain the WSDL description of WS, and dynamically generate the assembly through the description. Finally: get the newly generated assembly through reflection and call its method! The above steps need to reference the following four namespaces: using System.Web.Services.Description; //Description of WS//The following are used to dynamically generate code based on the description and dynamically compile to obtain the assembly using System.CodeDom; using Microsoft. CSharp; using System.CodeDom.Compiler; The above-mentioned namespaces include the following important classes: under using System.Web.Services.Description: ServiceDescription //WS description ServiceDescriptionImporter //Generate client proxy classes by description, especially Note that the Style is described by MSDN as follows: The interface of XML Web services is usually described by a Web Service Description Language (WSDL) file. For example, to obtain the WSDL instructions for a Web service using ASP.NET exposed at http://localhost/service.asmx, simply navigate to http://localhost/service.asmx?WSDL. Use the ServiceDescriptionImporter class to easily import the information contained in the WSDL description into the System.CodeDom.CodeCompileUnit object. By adjusting the value of the Style parameter, you can instruct the ServiceDescriptionImporter instance to generate a client proxy class (which can provide the function of a Web service by transparently invoking this class) or an abstract class (the class encapsulates the function of the Web service but does not implement the function). If the Style property is set to Client, the ServiceDescriptionImporter generates client-side proxy classes, and calls these classes to provide the functions of the Web service described. If the Style property is set to Server, the ServiceDescriptionImporter instance generates abstract classes that represent the functions of the XML Web services described without being implemented. Then, you can implement them by writing classes that inherit from these abstract classes and implement related methods. Under using System.CodeDom: CodedomUnit //It is used to set the name space, class name, etc. of the dynamic code. The description code of WS can be written into this class through the ServiceDescriptionImporter.Import() method for dynamic compilation.

Under using System.CodeDom.Compiler:
CodedomProvider //Used to create and retrieve instances of code generators and code compilers, we mainly use its implementation subclass CShareCodeProvider
You can directly Use CShareCodeProvider provider=new CShareCodeProvider() to generate, or use CodedomProvider.CreateProvider("CSharp") to generate
ICodeCompiler //Used to compile the source code representation based on System.CodeDom.
It is done through the CreateCompiler() method of CodedomProvider
CompilerResults //Indicates the compilation result returned from the compiler. It is compiled and returned by ICodeCompiler from the System.CodeDom tree contained in the specified CodeCompileUnit according to the specified compiler settings. The CompiledAssembly attribute indicates the compiled assembly.

After understanding the above information, you can call WS dynamically.
The following is a code demonstration taken from http://www.cnblogs.com/ruochen/archive/2007/12/11/990427.html:
Code

This method can make the program not Call the webservices method by web reference, and call the method directly in the code to achieve the purpose of dynamically calling webservices. Before using, reference the System.Web.Services dynamic link library, which is a dll that comes with .net.

The method is as follows:

using System;using System.Collections.Generic;using System.Text;using System.Net;using System. IO;using System.Web.Services.Description;using System.CodeDom;using Microsoft.CSharp;using System.CodeDom.Compiler;namespace TestSkin{ class Webservices {///  

===After understanding the above classes and methods, you can basically call WS dynamically.
Special attention is that: after dynamic compilation, reflection is required to read and execute. So you need to understand what reflection is and how to reflect.

There are three main methods for dynamic invocation of web service.

  1、修改config文件。 As long as you reference the web service, the address of the asmx file will appear in the config file. Only need to modify the address.

  2、程序修改url。 The web service is integrated with the System.Web.Service.WebService class, and this class has a Url attribute. By modifying this attribute, the same effect as method one can be achieved, and it is more flexible than it. Sometimes, we need to provide a list, there are many servers for users to choose. The program connects to different servers to call the web service according to the user's choice. At this time, you can use this method.

  3、接口引用。 Sometimes, we need to call web services on different servers, but they are different from each other, but they all implement the same interface. At this time, you can consider the following method. Just reference all web services first, and then use the interface instance to save the created web service object.

  4、动态编译。 This should be regarded as a real dynamic. Sometimes, the web service update on each server is relatively fast, and we can't update the proxy class every day. This method can be used at this time.

  在该方法中,有一点限制。 That is, the web service of each server either inherits the same interface, or has some methods with the same signature, and the service class name should preferably be the same. But it doesn't matter if you don't meet this condition, I will indicate it in the comments later. Look at the code:

using System; using System.CodeDom; using System.CodeDom.Compiler; using System.IO; using System.Net; using System.Reflection; using System.Web.Services.Description; using Microsoft.CSharp; //Get Web Service description WebClient wc = new WebClient(); Stream stream = wc.OpenRead("http://localhost/TestService.asmx? WSDL"); //Specify your own web service url here, must end with ?WSDL ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.ProtocolName = "soap"; sdi.Style = ServiceDescriptionImportStyle.Client; sdi.AddServiceDescription(sd, null, null); //Specify the namespace CodeNamespace cn = new CodeNamespace("Test"); //Specify a namespace randomly here, but it must be consistent with the following CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); Create a C# compiler CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); CompilerParameters cp = new CompilerParameters( ); cp.GenerateEx ecutable = false; cp.GenerateInMemory = true; //Add compilation conditions cp.ReferencedAssemblies.Add("System.dll"); cp.ReferencedAssemblies.Add("System.XML.dll"); cp.ReferencedAssemblies.Add(" System.Web.Services.dll"); //Compile assembly CompilerResults cr = icc.CompileAssemblyFromDom(cp, ccu); //Check if the compilation is successful if (!cr.Errors.HasErrors) {//Compile successful//Get Assembly assembly = cr.CompiledAssembly; //Get the assembly type//The previous Test is the namespace, and it must be consistent with the one specified in the front.//The following TestService is the class name of the service//If all servers are consistent The name of the class can be written dead here, otherwise the class name must be dynamically provided Type type = assembly.GetType("Test.TestService", true); object service = Activator.CreateInstance(type); //Get method//if all The server is the same method name, you can write dead here, otherwise you must dynamically provide the method name MethodInfo mi = type.GetMethod("HelloWorld"); //Call the method//If the method has no parameters, the second parameter can be passed null, otherwise the object array must be passed. The order of the array elements must be consistent with the order of the parameters.//If the method signatures of all servers are the same, the order of the object array can be written dead, otherwise the number of elements must be dynamically adjusted And the sequence mi.Invoke(service, null); //Finally, the object type is returned. According to the signature of the method, the return value can be converted into a different object. } else {//Handling compilation errors here }

Returned from http://www.cnblogs.com/xuwb/archive/2012/09/25/2701629.html

Leave a Comment

Your email address will not be published.