??
通常我们在程序中需要调用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??
- ????????
- ??????object[]?args)??
- ???????{??
- ??????????return?WebServiceHelper.InvokeWebService(url?,null?,methodname?,args)?;??
- ???????}??
- ??
- ??????string?classname,153); font-weight:bold; background-color:inherit">object[]?args)??
- ???????{??
- ??????????string?@namespace?=?"EnterpriseServerBase.WebService.DynamicWebCalling"?;??
- if((classname?==?null)?||(classname?==?""))??
- ???????????{??
- ???????????????classname?=?WebServiceHelper.GetWsClassName(url)?;??
- ???????????}??
- try??
- ???????????{??
- ????????????????
- ???????????????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);???
- ???????????????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,153); font-weight:bold; background-color:inherit">true,153); font-weight:bold; background-color:inherit">true);??
- object?obj?=?Activator.CreateInstance(t);??
- ???????????????System.Reflection.MethodInfo?mi?=?t.GetMethod(methodname);??
- ??????????????return?mi.Invoke(obj,args);??
- catch(Exception?ex)??
- new?Exception(ex.InnerException.Message,153); font-weight:bold; background-color:inherit">new?Exception(ex.InnerException.StackTrace));??
- ???????}??
- private?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"?,153); font-weight:bold; background-color:inherit">this.label_Result.Text?=?result.ToString()?;??
??? 上述的例子中,调用web服务使用了两个参数,第一个是城市的名字,第二个是国家的名字,Web服务返回的是XML文档,可以从其中解析出温度、风力等天气情况。 ???? ???? 最后说一下,C#虽然仍属于静态语言之列,但是其动态能力也是很强大的,不信,你可以看看Spring.net的AOP实现,这种“无侵入”的AOP实现比通常的.NET声明式AOP实现(一般是通过AOP Attribute)要漂亮的多。
c#动态调用WebService
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<??
- ????class?WebServiceAgent??
- ????{??
- ????????object?agent;??
- ????????private?Type?agentType;??
- const?string?CODE_NAMESPACE?=?"Beyondbit.WebServiceAgent.Dynamic";??
- ????????
- ///?<param?name="url"<</param<??
- public?WebServiceAgent(string?url)??
- ????????{??
- ????????????XmlTextReader?reader?=?new?XmlTextReader(url?+?"?wsdl");??
- ??????????????
- ????????????ServiceDescription?sd?=?ServiceDescription.Read(reader);??
- ??????????????
- ????????????ServiceDescriptionImporter?sdi?=? ????????????sdi.AddServiceDescription(sd,?null,153); font-weight:bold; background-color:inherit">null);??
- //使用?CodeDom?编译客户端代理类??
- ????????????CodeNamespace?cn?=?new?CodeNamespace(CODE_NAMESPACE);??
- ????????????CodeCompileUnit?ccu?=? ????????????ccu.Namespaces.Add(cn);??
- ????????????sdi.Import(cn,108); list-style:decimal-leading-zero outside; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ????????????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<??
- object?Invoke(string?methodName,153); font-weight:bold; background-color:inherit">params? ????????{??
- ????????????MethodInfo?mi?=?agentType.GetMethod(methodName);??
- ????????????return?this.Invoke(mi,?args);??
- ///<summary<??
- ///调用指定方法??
- ///</summary<??
- ///<param?name="method"<方法信息</param<??
- ///<param?name="args"<参数,按照参数顺序赋值</param<??
- ///<returns<Web服务的返回值</returns<??
- object?Invoke(MethodInfo?method,153); font-weight:bold; background-color:inherit">return?method.Invoke(agent,153); font-weight:bold; background-color:inherit">public?MethodInfo[]?Methods??
- get??
- ????????????{??
- ????????????????return?agentType.GetMethods();??
- ????????????}??
- ????????}??
- ????}??
- }??
?
using?System.ComponentModel;??
- using?System.Data;??
- using?System.Drawing;??
- using?System.Windows.Forms;??
- namespace?WindowsApplication1??
- ????public?partial?class?Form1?:?Form??
- ????{??
- string?_url?=?"http://www.baidu.com";??
- public?Form1()??
- ????????????InitializeComponent();??
- ????????????init_Data();??
- void?init_Data()??
- ????????????WindowsServiceWebDefaultHotCity.WebServiceAgent?agent?=?new?WindowsServiceWebDefaultHotCity.WebServiceAgent(_url);??
- object[]?args?=?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;???
- ??
- using?System.CodeDom;???
- using?Microsoft.CSharp;???
- using?System.CodeDom.Compiler;???
- 上述几个名称空间中包括如下几个重要的类:???
- using?System.Web.Services.Description下:???
- ServiceDescription???
- ServiceDescriptionImporter???
- 以下是MSDN对其的描述:???
- XML?Web?services?的接口通常由?Web?服务描述语言?(WSDL)?文件来说明。例如,若要获取有关使用?http:??
- using?System.CodeDom下:???
- CodedomUnit???
using System.CodeDom.Compiler下:? CodedomProvider //用于创建和检索代码生成器和代码编译器的实例,我们主要用到其实现子类CShareCodeProvider? 可以直接用CShareCodeProvider provider=new CShareCodeProvider()来生成,或者用CodedomProvider.CreateProvider("CSharp")来生成? ICodeCompiler //用于编译基于 System.CodeDom 的源代码表示形式。? 它通过CodedomProvider的CreateCompiler()方法来? CompilerResults //表示从编译器返回的编译结果。 它由ICodeCompiler根据指定的编译器设置从指定的 CodeCompileUnit 所包含的 System.CodeDom 树中编译程序集并返回。CompiledAssembly 属性指示编译的程序集。
了解如上信息后,就可动态调用WS了。? 如下是摘自http://www.cnblogs.com/ruochen/archive/2007/12/11/990427.html的代码演示: Code?
?? 该方法可以使程序不通过web引用的方式去调用webservices方法,直接在代码里调用该方法就能达到动态调用webservices的目的。使用前先引用System.Web.Services动态链接库,是.net自带的dll。
方法如下:
using?System.Net;??
- using?System.IO;??
- 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<??
- //这里的namespace是需引用的webservices的命名空间,在这里是写死的,大家可以加一个参数从外面传进来。??
- namespace?=?"client";??
- ????????????????
- ????????????????ServiceDescription?sd?=?ServiceDescription.Read(stream);??
- string?classname?=?sd.Services[0].Name;??
- ????????????????ServiceDescriptionImporter?sdi?=?new?ServiceDescriptionImporter();??
- ????????????????sdi.AddServiceDescription(sd,?"",?"");??
- ????????????????CodeNamespace?cn?=?namespace);??
- ??????????????????
- ????????????????CodeCompileUnit?ccu?=?new?CodeCompileUnit();??
- ????????????????ccu.Namespaces.Add(cn);??
- ????????????????sdi.Import(cn,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px"> ????????????????CSharpCodeProvider?csc?=?new?CSharpCodeProvider();??
- ????????????????ICodeCompiler?icc?=?csc.CreateCompiler();??
- //设定编译参数??
- ????????????????CompilerParameters?cplist?=? ????????????????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,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px"> ????????????????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);??
- ????????????????????}??
- ????????????????????new?Exception(sb.ToString());??
- ????????????????}??
- //生成代理实例,并调用方法??
- ????????????????System.Reflection.Assembly?assembly?=?cr.CompiledAssembly;??
- ????????????????Type?t?=?assembly.GetType(@namespace?+?"."?+?classname,153); font-weight:bold; background-color:inherit">true);??
- object?obj?=?Activator.CreateInstance(t);??
- ????????????????System.Reflection.MethodInfo?mi?=?t.GetMethod(methodname);??
- catch??
- null;??
- }??
?
===了解上述类和方法后,基本就可以动态调用WS了。? 特别注意的是:动态编译后需要用到反射来读取并执行。因此需要您了解什么是反射及如何反射。??
web service的动态调用,主要有三种方法。
1、修改config文件。只要你引用了web service,就会在config文件中出现asmx文件的地址。只需要修改该地址即可。
2、程序修改url。web service是集成了System.Web.Service.WebService类的,而该类有一个Url属性。通过修改该属性可以达到与方法一一样的效果,并且比它还要灵活。有的时候,我们需要提供一个列表,有很多的服务器让用户选择。程序根据用户的选择连接到不同的服务器上调用web service。这时,就可以用这个方法了。
3、接口引用。有的时候,我们需要调用不同服务器上的web service,但他们彼此又不一样,只是都实现了同一个接口。这时候,就可以考虑下面的这个方法。只要先引用所有的web service,然后用接口实例来保存创建出来的web service对象即可。
4、动态编译。这个应该算得上是真正意义上的动态了。有的时候,各个服务器上的web service更新比较快,我们不可能天天去更新代理类的,这个时候就可以用这个方法了。
在该方法中,有一点限制。就是各个服务器的web service,要么是都继承了同一个接口,要么是都有一些同样签名的方法,而且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;????
- ????
- ??
- 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,153); font-weight:bold; background-color:inherit">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,0); background-color:inherit">//检查是否编译成功????
- if?(!cr.Errors.HasErrors)????
- {????
- ????
- ????
- ??Assembly?assembly?=?cr.CompiledAssembly;????
- //获取程序集类型????
- //前面的Test就是命名空间,必须要与前面指定的一致????
- //后面的TestService就是service的类名????
- //如果所有的服务器都是一致的类名,这里就可以写死,否则要动态提供类名????
- ??Type?type?=?assembly.GetType("Test.TestService",153); font-weight:bold; background-color:inherit">true);????
- ??object?service?=?Activator.CreateInstance(type);????
- //获取方法????
- //如果所有的服务器都是一致的方法名,这里可以写死,否则就要动态提供方法名????
- ??MethodInfo?mi?=?type.GetMethod("HelloWorld");????
- //调用方法????
- //如果方法没有参数,第二个参数可以传递null,否则就要传递object数组,数组元素的顺序要与参数的顺序一致????
- //如果所有服务器的方法签名都是一致的,object数组的顺序就可以写死了,否则还要动态调整元素的数量及顺序????
- ??mi.Invoke(service,0); background-color:inherit">//最后,返回的是object类型,根据方法的签名,把返回值转换成不同的对象即可。????
- }????
- else????
- //这里自己处理编译错误????
- }????
转载:http://blog.csdn.net/ysq5202121/article/details/6942813
方法二:利用 wsdl.exe生成webservice代理类:
根据提供的wsdl生成webservice代理类,然后在代码里引用这个类文件。
步骤:1、在开始菜单找到? Microsoft Visual Studio 2010 下面的Visual Studio Tools , 点击Visual Studio 命令提示(2010),打开命令行。
????????? 2、?在命令行中输入:? wsdl /language:c# /n:TestDemo /out:d:/Temp/TestService.cs?http://jm1.services.gmcc.net/ad/Services/AD.asmx?wsdl
??????????????? 这句命令行的意思是:对最后面的服务地址进行编译,在D盘temp 目录下生成testservice文件。
????????? 3、 把上面命令编译后的cs文件,复制到我们项目中,在项目代码中可以直接new 一个出来后,可以进行调用。
更为详细的可以参见:
http://www.voidcn.com/article/p-drlofvnv-dn.html
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|