轉載自 百度文庫 http://wenku.baidu.com/link?url=Q2q50wohf5W6UX44zqotXFEe_XOMaib4UtI3BigaNwipOHKNETloMF4ax4W4iPZcjAW6q8vu45QOjK6DSUon1R10ePVlqG6nwussbbXE6jmweb
多數時候咱們經過 "添加 Web 引用..." 建立客戶端代理類的方式調用WebService,但在某些狀況下咱們可能須要在程序運行期間動態調用一個未知的服務。在 .NET Framework 的 System.Web.Services.Description 命名空間中有咱們須要的東西。
具體步驟:
1. 從目標 URL 下載 WSDL 數據。
2. 使用 ServiceDescription 建立和格式化 WSDL 文檔文件。
3. 使用 ServiceDescriptionImporter 建立客戶端代理類。
4. 使用 CodeDom 動態建立客戶端代理類程序集。
5. 利用反射調用相關 WebService 方法。
服務器
上述步驟須要引用以下四個名稱空間:dom
using System.Web.Services.Description; //WS的描述ide
//如下幾個用於根據描述動態生成代碼並動態編譯獲取程序集url
using System.CodeDom; spa
using Microsoft.CSharp;.net
using System.CodeDom.Compiler;代理
上述幾個名稱空間中包括以下幾個重要的類:code
using System.Web.Services.Description下:orm
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()來生成,或者用CodedomProvider.CreateProvider("CSharp")來生成
ICodeCompiler //用於編譯基於 System.CodeDom 的源代碼表示形式。
它經過CodedomProvider的CreateCompiler()方法來
CompilerResults //表示從編譯器返回的編譯結果。 它由ICodeCompiler根據指定的編譯器設置從指定的 CodeCompileUnit 所包含的 System.CodeDom 樹中編譯程序集並返回。CompiledAssembly 屬性指示編譯的程序集。
瞭解如上信息後,就可動態調用WS了。
OK,看看具體的例子。
咱們要調用的目標 WebService,其 URL 是 http://localhost:60436/Learn.WEB/WebService.asmx
HelloWorld.asmx
1 [WebService(Namespace = "http://www.rainsts.net/", Description="個人Web服務")] 2 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 3 public class WebService : System.Web.Services.WebService { 4 5 public WebService () { 6 } 7 8 [WebMethod] 9 public string HelloWorld() 10 { 11 return "Hello Wolrd!"; 12 } 13 }
1. 動態調用 WebService
客戶端動態調用代碼
1 using System.IO; 2 using System.Net; 3 using System.Reflection; 4 using System.CodeDom; 5 using System.CodeDom.Compiler; 6 using System.Web.Services; 7 using System.Web.Services.Description; 8 using System.Web.Services.Protocols; 9 using System.Xml.Serialization; 10 11 // 1. 使用 WebClient 下載 WSDL 信息。 12 WebClient web = new WebClient(); 13 Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 14 15 // 2. 建立和格式化 WSDL 文檔。 16 ServiceDescription description = ServiceDescription.Read(stream); 17 18 // 3. 建立客戶端代理代理類。 19 ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 20 21 importer.ProtocolName = "Soap"; // 指定訪問協議。 22 importer.Style = ServiceDescriptionImportStyle.Client; // 生成客戶端代理。 23 importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 24 25 importer.AddServiceDescription(description, null, null); // 添加 WSDL 文檔。 26 27 // 4. 使用 CodeDom 編譯客戶端代理類。 28 CodeNamespace nmspace = new CodeNamespace(); // 爲代理類添加命名空間,缺省爲全局空間。 29 CodeCompileUnit unit = new CodeCompileUnit(); 30 unit.Namespaces.Add(nmspace); 31 32 ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit); 33 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 34 35 CompilerParameters parameter = new CompilerParameters(); 36 parameter.GenerateExecutable = false; 37 parameter.GenerateInMemory = true; 38 parameter.ReferencedAssemblies.Add("System.dll"); 39 parameter.ReferencedAssemblies.Add("System.XML.dll"); 40 parameter.ReferencedAssemblies.Add("System.Web.Services.dll"); 41 parameter.ReferencedAssemblies.Add("System.Data.dll"); 42 43 CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); 44 45 // 5. 使用 Reflection 調用 WebService。 46 if (!result.Errors.HasErrors) 47 { 48 Assembly asm = result.CompiledAssembly; 49 Type t = asm.GetType("WebService"); // 若是在前面爲代理類添加了命名空間,此處須要將命名空間添加到類型前面。 50 51 object o = Activator.CreateInstance(t); 52 MethodInfo method = t.GetMethod("HelloWorld"); 53 Console.WriteLine(method.Invoke(o, null)); 54 }
2. 生成客戶端代理程序集文件
上面的代碼經過在內存中建立動態程序集的方 式完成了動態調用過程。若是咱們但願將客戶端代理類生成程序集文件保存到硬盤,則能夠進行以下修改。生成程序集文件後,咱們能夠經過 Assembly.LoadFrom() 載入並進行反射調用。對於須要屢次調用的系統,要比每次生成動態程序集效率高出不少。
1 using System.IO; 2 using System.Net; 3 using System.CodeDom; 4 using System.CodeDom.Compiler; 5 using System.Web.Services; 6 using System.Web.Services.Description; 7 using System.Web.Services.Protocols; 8 using System.Xml.Serialization; 9 10 // 1. 使用 WebClient 下載 WSDL 信息。 11 WebClient web = new WebClient(); 12 Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 13 14 // 2. 建立和格式化 WSDL 文檔。 15 ServiceDescription description = ServiceDescription.Read(stream); 16 17 // 3. 建立客戶端代理代理類。 18 ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 19 20 importer.ProtocolName = "Soap"; // 指定訪問協議。 21 importer.Style = ServiceDescriptionImportStyle.Client; // 生成客戶端代理。 22 importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 23 24 importer.AddServiceDescription(description, null, null); // 添加 WSDL 文檔。 25 26 // 4. 使用 CodeDom 編譯客戶端代理類。 27 CodeNamespace nmspace = new CodeNamespace(); // 爲代理類添加命名空間,缺省爲全局空間。 28 CodeCompileUnit unit = new CodeCompileUnit(); 29 unit.Namespaces.Add(nmspace); 30 31 ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit); 32 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 33 34 CompilerParameters parameter = new CompilerParameters(); 35 parameter.GenerateExecutable = false; 36 parameter.OutputAssembly = "test.dll"; // 能夠指定你所需的任何文件名。 37 parameter.ReferencedAssemblies.Add("System.dll"); 38 parameter.ReferencedAssemblies.Add("System.XML.dll"); 39 parameter.ReferencedAssemblies.Add("System.Web.Services.dll"); 40 parameter.ReferencedAssemblies.Add("System.Data.dll"); 41 42 CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); 43 if (result.Errors.HasErrors) 44 { 45 // 顯示編譯錯誤信息 46 } 47 48 調用程序集文件演示 49 Assembly asm = Assembly.LoadFrom("test.dll"); 50 Type t = asm.GetType("WebService"); 51 52 object o = Activator.CreateInstance(t); 53 MethodInfo method = t.GetMethod("HelloWorld"); 54 Console.WriteLine(method.Invoke(o, null));
3. 獲取客戶端代理類源代碼
還有一種情形,就是咱們須要得到客戶端代理類的 C# 源代碼。
1 using System.IO; 2 using System.Net; 3 using System.CodeDom; 4 using System.CodeDom.Compiler; 5 using System.Web.Services; 6 using System.Web.Services.Description; 7 using System.Web.Services.Protocols; 8 using System.Xml.Serialization; 9 10 // 1. 使用 WebClient 下載 WSDL 信息。 11 WebClient web = new WebClient(); 12 Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL"); 13 14 // 2. 建立和格式化 WSDL 文檔。 15 ServiceDescription description = ServiceDescription.Read(stream); 16 17 // 3. 建立客戶端代理代理類。 18 ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 19 20 importer.ProtocolName = "Soap"; // 指定訪問協議。 21 importer.Style = ServiceDescriptionImportStyle.Client; // 生成客戶端代理。 22 importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 23 24 importer.AddServiceDescription(description, null, null); // 添加 WSDL 文檔。 25 26 // 4. 使用 CodeDom 編譯客戶端代理類。 27 CodeNamespace nmspace = new CodeNamespace(); // 爲代理類添加命名空間,缺省爲全局空間。 28 CodeCompileUnit unit = new CodeCompileUnit(); 29 unit.Namespaces.Add(nmspace); 30 31 ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit); 32 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 33 34 // 5. 保存源代碼到文件。固然,你也能夠直接保存到內存字符串中。 35 TextWriter writer = File.CreateText("test.cs"); // 指定你所需的源代碼文件名。 36 provider.GenerateCodeFromCompileUnit(unit, writer, null); 37 writer.Flush(); 38 writer.Close();
若是你調用時觸發 "WebException: 請求因 HTTP 狀態 415 失敗: Unsupported Media Type。" 這樣的異常,那麼恭喜你和我同樣鬱悶 ,趕忙把服務器端的 WSE 關掉吧。在必須使用 WSE 的狀況下,須要對客戶端進行調整,至於代碼須要你本身去寫了。呵呵~~~~