轉載自 百度文庫 http://wenku.baidu.com/link?url=Q2q50wohf5W6UX44zqotXFEe_XOMaib4UtI3BigaNwipOHKNETloMF4ax4W4iPZcjAW6q8vu45QOjK6DSUon1R10ePVlqG6nwussbbXE6jm
多數時候我們通過 "添加 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()來生成,或者用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 的情況下,需要對客戶端進行調整,至於代碼需要你自己去寫了。呵呵~~~~