动态调用Webservice

 通常我们在程序中需要调用WebService时,都是通过“添加Web引用”,让VS.NET环境来为我们生成服务代理,然后调用对应的Web服务。这样是使工作简单了,但是却和提供Web服务的URL、方法名、参数绑定在一起了,这是VS.NET自动为我们生成Web服务代理的限制。如果哪一天发布Web服务的URL改变了,则我们需要重新让VS.NET生成代理,并重新编译。在某些情况下,这可能是不能忍受的,我们需要C#中动态调用WebService的能力。比如我们可以把Web服务的URL保存在配置文件中,这样,当服务URL改变时,只需要修改配置文件就可以了。

    说了这么多,实际上我们要实现这样的功能:

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

    其中,url是Web服务的地址,methodname是要调用服务方法名,args是要调用Web服务所需的参数,返回值就是web服务返回的结果了。

    要实现这样的功能,你需要这几个方面的技能:反射、CodeDom、编程使用C#编译器、WebService。在了解这些知识后,就可以容易的实现web服务的动态调用了:  

  1. using System.CodeDom.Compiler;  
  2. using System;  
  3. using System.Net;  
  4. using System.CodeDom;  
  5. using Microsoft.CSharp;  
  6. using System.IO;  
  7. using System.Web.Services.Description;  
  8. using System.Collections.Generic;  
  9. using System.Reflection;  
  10. namespace cjl.WebServices  
  11. {  
  12. public class DynamicWebServices  
  13. {  
  14. static SortedList〈string,Type〉_typeList=  
  15. new SortedList〈string,Type〉();  
  16.  
  17. #regionInvokeWebService  
  18.  
  19. static string GetCacheKey(string url,  
  20. string className)  
  21. {  
  22. return url.ToLower()+className;  
  23. }  
  24. static Type GetTypeFromCache(string url,  
  25. string className)  
  26. {  
  27. string key=GetCacheKey(url,className);  
  28. foreach(KeyValuePair〈string,Type〉  
  29. pair in _typeList)  
  30. {  
  31. if(key==pair.Key)  
  32. {  
  33. return pair.Value;  
  34. }  
  35. }  
  36.  
  37. return null;  
  38. }  
  39. static Type GetTypeFromWebService  
  40. (string url,string className)  
  41. {  
  42. string @namespace="EnterpriseServerBase.  
  43. WebService.DynamicWebCalling";  
  44. if((className==null)||(className==""))  
  45. {  
  46. className=GetWsClassName(url);  
  47. }  
  48.  
  49. //获取WSDL  
  50. WebClient wc=new WebClient();  
  51. Stream stream=wc.OpenRead(url+"?WSDL");  
  52. ServiceDescription sd=ServiceDescription.  
  53. Read(stream);  
  54. ServiceDescriptionImporter sdi=  
  55. new ServiceDescriptionImporter();  
  56. sdi.AddServiceDescription(sd,"","");  
  57. CodeNamespace cn=new CodeNamespace  
  58. (@namespace);  
  59.  
  60. //生成客户端代理类代码  
  61. CodeCompileUnit ccu=new CodeCompileUnit();  
  62. ccu.Namespaces.Add(cn);  
  63. sdi.Import(cn,ccu);  
  64. CSharpCodeProvider csc=new CSharpCodeProvider();  
  65. ICodeCompiler icc=csc.CreateCompiler();  
  66.  
  67. //设定编译参数  
  68. CompilerParameters cplist=new CompilerParameters();  
  69. cplist.GenerateExecutable=false;  
  70. cplist.GenerateInMemory=true;  
  71. cplist.ReferencedAssemblies.Add 
  72. ("System.dll");  
  73. cplist.ReferencedAssemblies.Add 
  74. ("System.XML.dll");  
  75. cplist.ReferencedAssemblies.Add 
  76. ("System.Web.Services.dll");  
  77. cplist.ReferencedAssemblies.Add 
  78. ("System.Data.dll");  
  79.  
  80. //编译代理类  
  81. CompilerResults cr=  
  82. icc.CompileAssemblyFromDom(cplist,ccu);  
  83. if(true==cr.Errors.HasErrors)  
  84. {  
  85. System.Text.StringBuildersb=  
  86. new System.Text.StringBuilder();  
  87. foreach(System.CodeDom.Compiler.  
  88. CompilerError ce in cr.Errors)  
  89. {  
  90. sb.Append(ce.ToString());  
  91. sb.Append(System.Environment.NewLine);  
  92. }  
  93. throw new Exception(sb.ToString());  
  94. }  
  95.  
  96. //生成代理实例,并调用方法  
  97. System.Reflection.Assemblyassembly=  
  98. cr.CompiledAssembly;  
  99. Type t=assembly.GetType(@namespace+".  
  100. "+className,true,true);  
  101. return t;  
  102. }  
  103.  
  104. //动态调用web服务  
  105. public static object InvokeWebService  
  106. (string url,string methodName,object[]args)  
  107. {  
  108. return InvokeWebService(url,null,  
  109. methodName,args);  
  110. }  
  111.  
  112. public static object InvokeWebService(string url,  
  113. string className,string methodName,object[]args)  
  114. {  
  115. try  
  116. {  
  117. Type t=GetTypeFromCache(url,className);  
  118. if(t==null)  
  119. {  
  120. t=GetTypeFromWebService(url,className);  
  121.  
  122. //添加到缓冲中  
  123. stringkey=GetCacheKey(url,className);  
  124. _typeList.Add(key,t);  
  125. }  
  126.  
  127. object obj=Activator.CreateInstance(t);  
  128. MethodInfo mi=t.GetMethod(methodName);  
  129. return mi.Invoke(obj,args);  
  130. }  
  131. catch(Exception ex)  
  132. {  
  133. throw new Exception(ex.InnerException.Message,  
  134. new Exception(ex.InnerException.StackTrace));  
  135. }  
  136. }  
  137. private static string GetWsClassName(string wsUrl)  
  138. {  
  139. string []parts=wsUrl.Split('/');  
  140. string []pps=parts[parts.Length-1].Split('.');  
  141. return pps[0];  
  142. }  
  143. #endregion  
  144. }  
  145. }  

    上面的注释已经很好的说明了各代码段的功能,下面给个例子看看,这个例子是通过访问http://www.webservicex.net/globalweather.asmx服务来获取各大城市的天气状况。              

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

    上述的例子中,调用web服务使用了两个参数,第一个是城市的名字,第二个是国家的名字,Web服务返回的是XML文档,可以从其中解析出温度、风力等天气情况。

    最后说一下,C#虽然仍属于静态语言之列,但是其动态能力也是很强大的,不信,你可以看看Spring.net的AOP实现,这种“无侵入”的AOP实现比通常的.NET声明式AOP实现(一般是通过AOP Attribute)要漂亮的多。

posted @ 2012-03-22 20:38  欢喜王子  阅读(293)  评论(0编辑  收藏  举报