在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
通常我们在程序中需要调用WebService时,都是通过“添加Web引用”,让VS.NET环境来为我们生成服务代理,然后调用对应的Web服务。这样是使工作简单了,但是却和提供Web服务的URL、方法名、参数绑定在一起了,这是VS.NET自动为我们生成Web服务代理的限制。如果哪一天发布Web服务的URL改变了,则我们需要重新让VS.NET生成代理,并重新编译。在某些情况下,这可能是不能忍受的,我们需要动态调用WebService的能力。比如我们可以把Web服务的URL保存在配置文件中,这样,当服务URL改变时,只需要修改配置文件就可以了。 public static object InvokeWebService(string url, string methodname, object[] args) 其中,url是Web服务的地址,methodname是要调用服务方法名,args是要调用Web服务所需的参数,返回值就是web服务返回的结果了。 要实现这样的功能,你需要这几个方面的技能:反射、CodeDom、编程使用C#编译器、WebService。在了解这些知识后,就可以容易的实现web服务的动态调用了: #region InvokeWebService //动态调用web服务 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 { //获取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(@namespace); //生成客户端代理类代码 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn ,ccu); CodeDomProvider provider = new CSharpCodeProvider();//设定编译参数 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()); } //生成代理实例,并调用方法 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 上面的注释已经很好的说明了各代码段的功能,下面给个例子看看,这个例子是通过访问http://www.webservicex.net/globalweather.asmx 服务来获取各大城市的天气状况。 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() ; 上述的例子中,调用web服务使用了两个参数,第一个是城市的名字,第二个是国家的名字,Web服务返回的是XML文档,可以从其中解析出温度、风力等天气情况。 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 { /// <summary< /// WebService代理类 /// </summary< public class WebServiceAgent { private object agent; private Type agentType; private const string CODE_NAMESPACE = "Beyondbit.WebServiceAgent.Dynamic"; /// <summary< /// 构造函数 /// </summary< /// <param name="url"<</param< public WebServiceAgent(string url) { XmlTextReader reader = new XmlTextReader(url + "?wsdl"); //创建和格式化 WSDL 文档 ServiceDescription sd = ServiceDescription.Read(reader); //创建客户端代理代理类 ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, null, null); //使用 CodeDom 编译客户端代理类 CodeNamespace cn = new CodeNamespace(CODE_NAMESPACE); CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); Microsoft.CSharp.CSharpCodeProvider icc = new Microsoft.CSharp.CSharpCodeProvider(); CompilerParameters cp = new CompilerParameters(); CompilerResults cr = icc.CompileAssemblyFromDom(cp, ccu); agentType = cr.CompiledAssembly.GetTypes()[0]; agent = Activator.CreateInstance(agentType); } ///<summary< ///调用指定的方法 ///</summary< ///<param name="methodName"<方法名,大小写敏感</param< ///<param name="args"<参数,按照参数顺序赋值</param< ///<returns<Web服务的返回值</returns< public object Invoke(string methodName, params object[] args) { MethodInfo mi = agentType.GetMethod(methodName); return this.Invoke(mi, args); } ///<summary< ///调用指定方法 ///</summary< ///<param name="method"<方法信息</param< ///<param name="args"<参数,按照参数顺序赋值</param< ///<returns<Web服务的返回值</returns< public object Invoke(MethodInfo method, params object[] args) { return method.Invoke(agent, args); } public MethodInfo[] Methods { get { return agentType.GetMethods(); } } } } 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.WebServiceAgent(_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; } } } 我们都知道,调用WS可以在工程中添加对WS的WEB引用。 但是,如果我们不想通过添加引用的方式,而是在代码中动态引用该怎么办呢? 首先,我们该想到WS的实现也是一个类的形式。 其次,WS在传输过程中是通过WSDL来进行描述的(使用SOAP协议)。 因此,我们需要获取WS的WSDL描述,并通过该描述来动态生成程序集。 最后:通过反射来获取新生成的程序集,并调用其方法! 上述步骤需要引用如下四个名称空间: using System.Web.Services.Description; //WS的描述 //以下几个用于根据描述动态生成代码并动态编译获取程序集 using System.CodeDom; using Microsoft.CSharp; using System.CodeDom.Compiler; 上述几个名称空间中包括如下几个重要的类: using System.Web.Services.Description下: ServiceDescription //WS描述 ServiceDescriptionImporter //通过描述生成客户端代理类,特别注意其中的Style 以下是MSDN对其的描述: XML Web services 的接口通常由 Web 服务描述语言 (WSDL) 文件来说明。例如,若要获取有关使用 http://localhost/service.asmx 处公开的 ASP.NET 的 Web 服务的 WSDL 说明,只需导航到 http://localhost/service.asmx?WSDL。使用 ServiceDescriptionImporter 类可以方便地将 WSDL 说明中包含的信息导入到 System.CodeDom.CodeCompileUnit 对象。通过调整 Style 参数的值,可以指示 ServiceDescriptionImporter 实例生成客户端代理类(通过透明调用该类可提供 Web 服务的功能)或生成抽象类(该类封装 Web 服务的功能而不实现该功能)。如果将 Style 属性设置为 Client,则 ServiceDescriptionImporter 生成客户端代理类,通过调用这些类来提供说明的 Web 服务的功能。如果将 Style 属性设置为 Server,则 ServiceDescriptionImporter 实例生成抽象类,这些类表示所说明的 XML Web services 的功能而不进行实现。然后,可以通过编写从这些抽象类继承的类来对其进行实现,并实现相关的方法。 using System.CodeDom下: CodedomUnit //它用于设定动态代码的名称空间,类名等,可以通过ServiceDescriptionImporter.Import()方法将WS的描述代码写入该类,以作动态编译用 using System.CodeDom.Compiler下: 了解如上信息后,就可动态调用WS了。 该方法可以使程序不通过web引用的方式去调用webservices方法,直接在代码里调用该方法就能达到动态调用webservices的目的。使用前先引用System.Web.Services动态链接库,是.net自带的dll。 方法如下: 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 { /// <summary< /// 实例化WebServices /// </summary< /// <param name="url"<WebServices地址</param< /// <param name="methodname"<调用的方法</param< /// <param name="args"<把webservices里需要的参数按顺序放到这个object[]里</param< public static object InvokeWebService(string url, string methodname, object[] args) { //这里的namespace是需引用的webservices的命名空间,在这里是写死的,大家可以加一个参数从外面传进来。 string @namespace = "client"; try { //获取WSDL WebClient wc = new WebClient(); Stream stream = wc.OpenRead(url + "?WSDL"); ServiceDescription sd = ServiceDescription.Read(stream); string classname = sd.Services[0].Name; ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, "", ""); CodeNamespace cn = new CodeNamespace(@namespace); //生成客户端代理类代码 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); //设定编译参数 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 = icc.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()); } //生成代理实例,并调用方法 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 { return null; } } } } ===了解上述类和方法后,基本就可以动态调用WS了。 web service的动态调用,主要有三种方法。 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; //获取Web Service描述 WebClient wc= new WebClient(); Stream stream = wc.OpenRead("http://localhost/TestService.asmx?WSDL"); //这里指定你自己的web service url,一定要以?WSDL结尾 ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.ProtocolName = "soap"; sdi.Style = ServiceDescriptionImportStyle.Client; sdi.AddServiceDescription(sd, null, null); //指定命名空间 CodeNamespace cn = new CodeNamespace("Test"); //这里随便指定一个命名空间,但要与后面的一致 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); 建立C#编译器 CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); CompilerParameters cp = new CompilerParameters(); cp.GenerateExecutable = false; cp.GenerateInMemory = true; //添加编译条件 cp.ReferencedAssemblies.Add("System.dll"); cp.ReferencedAssemblies.Add("System.XML.dll"); cp.ReferencedAssemblies.Add("System.Web.Services.dll"); //编译程序集 CompilerResults cr = icc.CompileAssemblyFromDom(cp, ccu); //检查是否编译成功 if (!cr.Errors.HasErrors) { //编译成功 //获取程序集 Assembly assembly = cr.CompiledAssembly; //获取程序集类型 //前面的Test就是命名空间,必须要与前面指定的一致 //后面的TestService就是service的类名 //如果所有的服务器都是一致的类名,这里就可以写死,否则要动态提供类名 Type type = assembly.GetType("Test.TestService", true); object service = Activator.CreateInstance(type); //获取方法 //如果所有的服务器都是一致的方法名,这里可以写死,否则就要动态提供方法名 MethodInfo mi = type.GetMethod("HelloWorld"); //调用方法 //如果方法没有参数,第二个参数可以传递null,否则就要传递object数组,数组元素的顺序要与参数的顺序一致 //如果所有服务器的方法签名都是一致的,object数组的顺序就可以写死了,否则还要动态调整元素的数量及顺序 mi.Invoke(service, null); //最后,返回的是object类型,根据方法的签名,把返回值转换成不同的对象即可。 } else { //这里自己处理编译错误 } |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论