用C#經過反射實現動態調用WebService 告別Web引用(轉載)

咱們都知道,調用WebService能夠在工程中對WebService地址進行WEB引用,可是這確實很不方便。我想可以利用配置文件靈活調用WebService。如何實現呢?html

用C#經過反射實現動態調用WebServiceweb

 

下面是WebService代碼:數組

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace TestWebService
{
    /// <summary>
    /// Service1 的摘要說明
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/",Description="個人Web服務")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // 若要容許使用 ASP.NET AJAX 從腳本中調用此 Web 服務,請取消對下行的註釋。
    // [System.Web.Script.Services.ScriptService]
    public class TestWebService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "測試Hello World";
        }

        [WebMethod]
        public string Test()
        {
            return "測試Test";
        }
      
        [WebMethod(CacheDuration = 60,Description = "測試")]
        public List<String> GetPersons()
        {
            List<String> list = new List<string>();
            list.Add("測試一");
            list.Add("測試二");
            list.Add("測試三");
            return list;
        }  

    }

}
複製代碼

 

下面是客戶端的代碼:安全

複製代碼
using System.Text;
using System.Net;
using System.IO;
using System.Web.Services.Description;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System;

namespace TestCommon
{
    public class Webservice
    {
        /// <summary>
        /// 實例化WebServices
        /// </summary>
        /// <param name="url">WebServices地址</param>
        /// <param name="methodname">調用的方法</param>
        /// <param name="args">把webservices裏須要的參數按順序放到這個object[]裏</param>
        public static object InvokeWebService(string url, string methodname, object[] args)
        {
            //這裏的namespace是需引用的webservices的命名空間,我沒有改過,也可使用。也能夠加一個參數從外面傳進來。
            string @namespace = "client";

            try
            {
                //獲取WSDL
                WebClient wc = new WebClient();
                Stream stream = wc.OpenRead(url + "?WSDL");
                ServiceDescription sd = ServiceDescription.Read(stream);
                string classname = sd.Services[0].Name;
                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 = csc.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, true, true);
                object obj = Activator.CreateInstance(t);
                System.Reflection.MethodInfo mi = t.GetMethod(methodname);

                //注:method.Invoke(o, null)返回的是一個Object,若是你服務端返回的是DataSet,這裏也是用(DataSet)method.Invoke(o, null)轉一下就好了,method.Invoke(0,null)這裏的null能夠傳調用方法須要的參數,string[]形式的
                return mi.Invoke(obj, args);
            }
            catch
            {
                return null;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string url = "http://localhost:3182/Service1.asmx?WSDL";//這個地址能夠寫在Config文件裏面,這裏取出來就好了.在原地址後面加上: ?WSDL
            string method = "GetPersons";

            String[] item = (String[])Webservice.InvokeWebService(url, method, null);
            
            foreach (string str in item)
                Console.WriteLine(str);
        }
    }
}
複製代碼

 

注意:上述代碼須要引用以下四個名稱空間:
using System.Web.Services.Description;  //WS的描述
//如下幾個用於根據描述動態生成代碼並動態編譯獲取程序集
using System.CodeDom; 
using Microsoft.CSharp;
using System.CodeDom.Compiler;ide

 

代碼相對簡單,爲何能夠如此調用呢?動態編譯後用反射來讀取並執行。也許瞭解反射及如何反射對你會有幫助。post

反射提供了封裝程序集、模塊和類型的對象(Type 類型)。可使用反射動態建立類型的實例,將類型綁定到現有對象,或從現有對象獲取類型並調用其方法或訪問其字段和屬性。詳細請查看:https://msdn.microsoft.com/zh-cn/library/ms173183(VS.80).aspx

爲何WebServices能夠經過反射實現?測試

WebService在傳輸過程當中是經過WSDL來進行描述的(使用SOAP協議)。所以,咱們須要獲取WebService的WSDL描述,並經過該描述來動態生成程序集。而後經過反射來獲取新生成的程序集,並調用其方法!ui

如下是MSDN對其的描述:this

複製代碼
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 的功能而不進行實現。而後,能夠經過編寫從這些抽象類繼承的類來對其進行實現,並實現相關的方法。
複製代碼

 

關於上面代碼中CompilerParameters的配置參數,有以下說明:url

CodeDom能夠動態編譯Code代碼成爲程序集,有時咱們只想動態編譯的程序集,在內存中或者是硬盤上調用,這就是CodeDom的動態編譯。微軟在CodeDom中提供了動態編譯程序,這是ICodeCompiler的用武之地了,它定義用於調用源代碼編譯的接口或使用指定編譯器的 CodeDOM 樹。能夠從CodeDomProvider生成引用對象:CodeDomProvider.CreateProvider("").CreateCompiler();

 

在ICodeCompiler中爲咱們提供了程序集編譯的方法有:

CompileAssemblyFromDom :使用指定的編譯器設置從指定的 CodeCompileUnit 所包含的 System.CodeDom 樹中編譯程序集。            

CompileAssemblyFromDomBatch:基於包含在 CodeCompileUnit 對象的指定數組中的 System.CodeDom 樹,使用指定的編譯器設置編譯程序集。              

CompileAssemblyFromFile:從包含在指定文件中的源代碼,使用指定的編譯器設置編譯程序集。              

CompileAssemblyFromFileBatch:從包含在指定文件中的源代碼,使用指定的編譯器設置編譯程序集。              

CompileAssemblyFromSource: 從包含源代碼的指定字符串,使用指定的編譯器設置編譯程序集。            

CompileAssemblyFromSourceBatch:從包含源代碼的字符串的指定數組,使用指定的編譯器設置編譯程序集。

 

在咱們的CodeDomProvider也提供了CompileAssemblyFromDom、CompileAssemblyFromFile、CompileAssemblyFromSource。

在他們的編譯時候都有一個變異參數CompilerParameters,提供了編譯時參數選項:

CompilerOptions:獲取或設置調用編譯器時使用的可選附加命令行參數字符串。

EmbeddedResources:獲取要在編譯程序集輸出時包含的 .NET Framework 資源文件。

Evidence:指定一個證據對象,該對象表示要授予已編譯的程序集的安全策略權限。

GenerateExecutable:獲取或設置一個值,該值指示是否生成可執行文件。

GenerateInMemory:獲取或設置一個值,該值指示是否在內存中生成輸出。

IncludeDebugInformation:獲取或設置一個值,該值指示是否在已編譯的可執行文件中包含調試信息。

LinkedResources:獲取當前源中引用的 .NET Framework 資源文件。

MainClass:獲取或設置主類的名稱。

OutputAssembly:獲取或設置輸出程序集的名稱。

ReferencedAssemblies:獲取當前項目所引用的程序集。

TempFiles:獲取或設置包含臨時文件的集合.

TreatWarningsAsErrors:獲取或設置一個值,該值指示是否將警告視爲錯誤。

UserToken:獲取或設置在建立編譯器進程時使用的用戶標記。

WarningLevel:獲取或設置使編譯器停止編譯的警告級別。

Win32Resource:獲取或設置要連接到已編譯程序集中的 Win32 資源文件的文件名。

 

他們的結果返回編譯結果CompilerResults,提供了編譯結果信息:

CompiledAssembly:獲取或設置已編譯的程序集。

Errors:獲取編譯器錯誤和警告的集合。

Evidence:指示證據對象,該對象表示編譯的程序集的安全策略權限。

NativeCompilerReturnValue:獲取或設置編譯器的返回值。

Output:獲取編譯器輸出消息。

PathToAssembly:獲取或設置已編譯程序集的路徑。

TempFiles:獲取或設置要使用的臨時文件集合。

 

原文連接1

原文連接2

 

 

出處:https://www.cnblogs.com/OpenCoder/p/7677758.html

相關文章
相關標籤/搜索