動態調用WebService接口的幾種方式


一、什么是WebService?

 這里就不再贅述了,想要了解的====》傳送門

二、為什么要動態調用WebService接口?

一般在C#開發中調用webService服務中的接口都是通過引用過來就行調用的,步驟如下:

1.找到引用,右擊添加服務引用,找到高級,添加web引用,添加之后就可以直接調用里面的方法。

 

 

 

 

以上這種方法是最簡單粗暴的一種方式。當然在開發中總是不那么如意,以上方式是在本機直接可以訪問服務的地址,假如在本機不能直接訪問WebService,那么就會有些蛋疼。

這種方式就不可取了,那么有什么方式可以不直接訪問就可以開發呢?這就要使用動態調用的方式進行調用了。接下來就是如何動態調用WebService了。

三、動態調用WebService的幾種方式

 方式一:

隨便百度一下就可以找到的一種,接下了的代碼來源於百度找到的並性能進行優化升級過。

 public static class CommonServiceHelper
    {
        /// <summary>
        /// 靜態緩存字典,速度提升至8倍
        /// </summary>
        private static Dictionary<string, object> _webServiceConfig = new Dictionary<string, object>();

        //獲取WSDL
        private static readonly WebClient wc = new WebClient();

        /// < summary>
        /// 動態調用web服務
        /// </summary>
        /// < param name="url">WSDL服務地址</param>
        /// < param name="classname">類名</param>
        /// < param name="methodname">方法名</param>
        /// < param name="args">參數</param>
        /// < returns></returns>
        public static object InvokeWebService(this string url, string methodname, object[] args, string classname = "")
        {
            string key = $"{url}_{methodname}";//緩存Key唯一標識
            Type webService;
            if (!_webServiceConfig.ContainsKey(key))
            {
                string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
                //classname 一般自己指定 如果有些地址是http://www.webxml.com.cn/WebServices/WeatherWS?wsdl 就會調用失敗
                if ((classname == null) || (classname == ""))
                {
                    classname = GetWsClassName(url);
                }
                try
                {
                    Stream stream = wc.OpenRead(url);
                    ServiceDescription sd = ServiceDescription.Read(stream);
                    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 icc = new CSharpCodeProvider();
                    CompilerParameters cplist = new CompilerParameters
                    {
                        GenerateExecutable = false,
                        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 compiler = icc.CompileAssemblyFromDom(cplist, ccu);
                    icc.Dispose();
                    if (compiler.Errors.HasErrors)
                    {
                        System.Text.StringBuilder sb = new System.Text.StringBuilder();
                        foreach (CompilerError ce in compiler.Errors)
                        {
                            sb.Append(ce.ToString());
                            sb.Append(Environment.NewLine);
                        }
                        throw new Exception(sb.ToString());
                    }
                    System.Reflection.Assembly assembly = compiler.CompiledAssembly;
                    Type t = assembly.GetType(@namespace + "." + classname, true, true);
                    webService = t;
                    _webServiceConfig.Add(key, webService);//加入緩存
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            else
            {
                webService = _webServiceConfig[key] as Type;//獲取緩存的數據
            }
            if (webService != null)
            {
                object obj = Activator.CreateInstance(webService);
                System.Reflection.MethodInfo mi = webService.GetMethod(methodname);
                if (mi != null)
                {
                    return mi.Invoke(obj, args);
                }
                throw new Exception($"找不到{methodname}這個方法,調用失敗");
            }
            else
            {
                throw new Exception($"WebService 對象為空,調用失敗");
            }
        }

        /// <summary>
        /// 獲取wsdl類名稱
        /// </summary>
        /// <param name="wsUrl">wsdl地址</param>
        /// <returns></returns>
        private static string GetWsClassName(string wsUrl)
        {
            string[] parts = wsUrl.Split('/');
            string[] pps = parts[parts.Length - 1].Split('.');//classname 一般自己指定 原因是因為在這里
            return pps[0];
        }
        /// <summary>
        /// 清空緩存
        /// </summary>
        public static void ClearCache()
        {
            _webServiceConfig.Clear();
        }
    }

 以上方法是第一種,這種方法不是萬能的,因為方法是通過反射調用的,多個參數的時候順序不能亂,方法名稱不能寫錯,對於有些WebService是無法調用的。

方式二:

這種方式針對本機不能訪問的WebService非常有效,參數啥的不容易搞混,跟C#直接添加引用之后調用方式一樣。

C#添加引用的方式是通過對於WebService的xml進行解析並生成相關代碼,根據原理可知,只要有WebService的xml就好辦了。

對於本機不能訪問的WebService,找個可以訪問的環境,用瀏覽器進行訪問,后面加上wsdl,例如:http://www.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

然后進行訪問,把頁面另存為你可以找得到的地方。我這里直接存儲到D盤

把這個xml文件丟到你本地隨便一個可以訪問的的站點下,這個xml文件要可以正常訪問。(這個很重要)

 接下來就要使用visual studio 開發人員命令工具,我這里是使用vs2019 ,前面幾個版本的vs都可以

在開始菜單里面找到並打開

 

 接下來使用命令生成代碼。

命令如下:wsdl  你本地訪問的那個xml地址全路徑                                     

我這里是放在端口81的站點下

 

 

生成的cs類文件跟通過web引用生成的cs文件是一樣,這樣就可以解決本機不能直接訪問WebService不能調用方法的問題。

當然,想要可以執行其中的方法還需要把代碼放到對應環境上才能執行。

四、總結

以上是使用兩種方式動態調用WebService接口的方式,在實際開發中,遇到不能訪問的,一般用第二種比較保險。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM