动态调用 webservice 的三种方式 
 
 
多数时候我们通过  "添加  Web  引用..."  创建客户端代理类的方式调用 WebService,但在某
些情况下我们可能需要在程序运行期间动态调用一个未知的服务。在  .NET  Framework  的 
System.Web.Services.Description  命名空间中有我们需要的东西。 
 
具体步骤: 
 
1.  从目标  URL  下载  WSDL  数据。 
2.  使用  ServiceDescription  创建和格式化  WSDL  文档文件。 
3.  使用  ServiceDescriptionImporter  创建客户端代理类。 
4.  使用  CodeDom  动态创建客户端代理类程序集。 
5.  利用反射调用相关  WebService  方法。 
 
上述步骤需要引用如下四个名称空间: 
  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 下: 
CodedomProvider  //用于创建和检索代码生成器和代码编译器的实例,我们主要用到其实现
子类 CShareCodeProvider 
可 以 直 接 用 CShareCodeProvider  provider=new  CShareCodeProvider() 来 生 成 , 或 者 用
URL  是 
CodedomProvider.CreateProvider("CSharp")来生成 
ICodeCompiler //用于编译基于  System.CodeDom  的源代码表示形式。   
它通过 CodedomProvider 的 CreateCompiler()方法来 
CompilerResults //表示从编译器返回的编译结果。  它由 ICodeCompiler 根据指定的编译器设
置 从 指 定 的   CodeCompileUnit  所 包 含 的   System.CodeDom  树 中 编 译 程 序 集 并 返 回 。
CompiledAssembly  属性指示编译的程序集。 
了解如上信息后,就可动态调用 WS 了。 
 
OK,看看具体的例子。 
 
我 们 要 调 用 的 目 标   WebService , 其  
http://localhost:60436/Learn.WEB/WebService.asmx 
 
HelloWorld.asmx 
[WebService(Namespace = "http://www.rainsts.net/", Description="我的 Web 服务")] 
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
public class WebService : System.Web.Services.WebService { 
 
 public WebService () { 
 } 
 
  [WebMethod] 
  public string HelloWorld() 
  { 
    return "Hello Wolrd!"; 
  } 
} 
 
1.  动态调用  WebService 
 
客户端动态调用代码 
using System.IO; 
using System.Net; 
using System.Reflection; 
using System.CodeDom; 
using System.CodeDom.Compiler; 
using System.Web.Services; 
using System.Web.Services.Description; 
using System.Web.Services.Protocols; 
using System.Xml.Serialization; 
 
// 1.  使用  WebClient  下载  WSDL  信息。 
WebClient web = new WebClient(); 
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 
= 
| 
CodeGenerationOptions.GenerateProperties 
 
// 2.  创建和格式化  WSDL  文档。 
ServiceDescription description = ServiceDescription.Read(stream); 
 
// 3.  创建客户端代理代理类。 
ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 
 
importer.ProtocolName = "Soap"; //  指定访问协议。 
importer.Style = ServiceDescriptionImportStyle.Client; //  生成客户端代理。 
importer.CodeGenerationOptions 
CodeGenerationOptions.GenerateNewAsync;   
 
importer.AddServiceDescription(description, null, null); //  添加  WSDL  文档。 
 
// 4.  使用  CodeDom  编译客户端代理类。 
CodeNamespace nmspace = new CodeNamespace(); //  为代理类添加命名空间,缺省为全局空
间。 
CodeCompileUnit unit = new CodeCompileUnit(); 
unit.Namespaces.Add(nmspace); 
 
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);   
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 
 
CompilerParameters parameter = new CompilerParameters(); 
parameter.GenerateExecutable = false; 
parameter.GenerateInMemory = true; 
parameter.ReferencedAssemblies.Add("System.dll"); 
parameter.ReferencedAssemblies.Add("System.XML.dll"); 
parameter.ReferencedAssemblies.Add("System.Web.Services.dll"); 
parameter.ReferencedAssemblies.Add("System.Data.dll"); 
 
CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); 
 
// 5.  使用  Reflection  调用  WebService。 
if (!result.Errors.HasErrors) 
{ 
  Assembly asm = result.CompiledAssembly; 
  Type  t  =  asm.GetType("WebService");  //  如果在前面为代理类添加了命名空间,此处需要将
命名空间添加到类型前面。 
 
  object o = Activator.CreateInstance(t); 
  MethodInfo method = t.GetMethod("HelloWorld"); 
  Console.WriteLine(method.Invoke(o, null)); 
} 
 
2.  生成客户端代理程序集文件 
 
上面的代码通过在内存中创建动态程序集的方  式完成了动态调用过程。如果我们希望将客
户端代理类生成程序集文件保存到硬盘,则可以进行如下修改。生成程序集文件后,我们可
以通过  Assembly.LoadFrom()  载入并进行反射调用。对于需要多次调用的系统,要比每次生
成动态程序集效率高出很多。 
using System.IO; 
using System.Net; 
using System.CodeDom; 
using System.CodeDom.Compiler; 
using System.Web.Services; 
using System.Web.Services.Description; 
using System.Web.Services.Protocols; 
using System.Xml.Serialization; 
 
// 1.  使用  WebClient  下载  WSDL  信息。 
WebClient web = new WebClient(); 
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 
 
// 2.  创建和格式化  WSDL  文档。 
ServiceDescription description = ServiceDescription.Read(stream); 
 
// 3.  创建客户端代理代理类。 
ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 
 
importer.ProtocolName = "Soap"; //  指定访问协议。 
importer.Style = ServiceDescriptionImportStyle.Client; //  生成客户端代理。 
importer.CodeGenerationOptions 
CodeGenerationOptions.GenerateNewAsync;   
 
importer.AddServiceDescription(description, null, null); //  添加  WSDL  文档。 
 
// 4.  使用  CodeDom  编译客户端代理类。 
CodeNamespace nmspace = new CodeNamespace(); //  为代理类添加命名空间,缺省为全局空
间。 
CodeCompileUnit unit = new CodeCompileUnit(); 
unit.Namespaces.Add(nmspace); 
 
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);   
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 
 
CompilerParameters parameter = new CompilerParameters(); 
parameter.GenerateExecutable = false; 
CodeGenerationOptions.GenerateProperties 
= 
| 
parameter.OutputAssembly = "test.dll"; //  可以指定你所需的任何文件名。 
parameter.ReferencedAssemblies.Add("System.dll"); 
parameter.ReferencedAssemblies.Add("System.XML.dll"); 
parameter.ReferencedAssemblies.Add("System.Web.Services.dll"); 
parameter.ReferencedAssemblies.Add("System.Data.dll"); 
 
CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); 
if (result.Errors.HasErrors) 
{ 
  //  显示编译错误信息 
} 
 
调用程序集文件演示 
Assembly asm = Assembly.LoadFrom("test.dll"); 
Type t = asm.GetType("WebService");   
 
object o = Activator.CreateInstance(t); 
MethodInfo method = t.GetMethod("HelloWorld"); 
Console.WriteLine(method.Invoke(o, null)); 
 
3.  获取客户端代理类源代码 
 
还有一种情形,就是我们需要获得客户端代理类的  C#  源代码。 
using System.IO; 
using System.Net; 
using System.CodeDom; 
using System.CodeDom.Compiler; 
using System.Web.Services; 
using System.Web.Services.Description; 
using System.Web.Services.Protocols; 
using System.Xml.Serialization; 
 
// 1.  使用  WebClient  下载  WSDL  信息。 
WebClient web = new WebClient(); 
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 
 
// 2.  创建和格式化  WSDL  文档。 
ServiceDescription description = ServiceDescription.Read(stream); 
 
// 3.  创建客户端代理代理类。 
ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 
 
importer.ProtocolName = "Soap"; //  指定访问协议。 
importer.Style = ServiceDescriptionImportStyle.Client; //  生成客户端代理。 
= 
| 
CodeGenerationOptions.GenerateProperties 
importer.CodeGenerationOptions 
CodeGenerationOptions.GenerateNewAsync;   
 
importer.AddServiceDescription(description, null, null); //  添加  WSDL  文档。 
 
// 4.  使用  CodeDom  编译客户端代理类。 
CodeNamespace nmspace = new CodeNamespace(); //  为代理类添加命名空间,缺省为全局空
间。 
CodeCompileUnit unit = new CodeCompileUnit(); 
unit.Namespaces.Add(nmspace); 
 
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);   
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 
 
// 5.  保存源代码到文件。当然,你也可以直接保存到内存字符串中。 
TextWriter writer = File.CreateText("test.cs"); //  指定你所需的源代码文件名。 
provider.GenerateCodeFromCompileUnit(unit, writer, null); 
writer.Flush(); 
writer.Close(); 
 
如果你调用时触发  "WebException:  请求因  HTTP  状态  415  失败: Unsupported Media Type。
"  这样的异常,那么恭喜你和我一样郁闷  ,赶紧把服务器端的  WSE  关掉吧。在必须使
用  WSE  的情况下,需要对客户端进行调整,至于代码需要你自己去写了。呵呵~~~~