C#動態呼叫WebService方法之WS代理
阿新 • • 發佈:2019-02-18
通常我們在程式中需要呼叫WebService時,都是通過“新增Web引用”,讓VS.NET環境來為我們生成服務代理,然後呼叫對應的Web服務。這樣是使工作簡單了,但是卻和提供Web服務的URL、方法名、引數繫結在一起了,這是VS.NET自動為我們生成Web服務代理的限制。
本文主要記錄自己應用過程中使用的動態生成WS的一個示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Net; using System.IO; using System.Web.Services.Description; using System.Xml.Serialization; using System.CodeDom; using System.CodeDom.Compiler; using System.ComponentModel; namespace VXXS { public class WsProxy { public enum EMethod { queryObjectOut, writeObjectOut }; /// <summary> /// 輸出的dll檔名稱 /// </summary> private string m_OutputDllFilename = "WSDLL.dll"; /// <summary> /// WebService代理類名稱 /// </summary> public string m_ProxyClassName = ""; /// <summary> /// WebService代理類例項 /// </summary> public object m_ObjInvoke; /// <summary> /// WebService代理網址 /// </summary> public string webServiceUrl = ""; /// <summary> /// WebService 代理是否執行 /// </summary> public bool bProxyRun = false; public WsProxy(string WSDefaltURL) { m_ProxyClassName = "Service"; webServiceUrl = WSDefaltURL; bProxyRun = false; if (CreateWebService()) { bProxyRun = true; } } /// <summary> /// WebService介面方法字典 /// </summary> public Dictionary<EMethod, MethodInfo> m_MethodDic = new Dictionary<EMethod, MethodInfo>(); /// <summary> /// 建立webservice代理 /// </summary> /// <returns></returns> public bool CreateWebService() { try { webServiceUrl += "?WSDL"; // 如果程式集已存在,直接使用 if (File.Exists(Path.Combine(Environment.CurrentDirectory, m_OutputDllFilename))) { if (BuildMethods()) { return true; } else { return false; } } else { UpdateWebService(); } } catch (Exception ex) { DelegateState.DelegateTipsText(ex.Message); } return false; } /// <summary> /// 更新WebService /// </summary> /// <returns></returns> public bool UpdateWebService() { //使用 WebClient 下載 WSDL 資訊。 WebClient web = new WebClient(); Stream stream = web.OpenRead(webServiceUrl); //建立和格式化 WSDL 文件 if (stream != null) { // 格式化WSDL ServiceDescription description = ServiceDescription.Read(stream); // 建立客戶端代理類 ServiceDescriptionImporter importer = new ServiceDescriptionImporter { ProtocolName = "Soap", Style = ServiceDescriptionImportStyle.Client, CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync }; // 新增 WSDL 文件 importer.AddServiceDescription(description, null, null); //使用 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 { GenerateExecutable = false, // 指定輸出dll檔名。 OutputAssembly = m_OutputDllFilename }; 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); // 使用 Reflection 呼叫 WebService。 if (!result.Errors.HasErrors) { if (BuildMethods()) { return true; } else { return false; } } else { DelegateState.DelegateTipsText("反射生成dll檔案時異常"); } stream.Close(); stream.Dispose(); } else { DelegateState.DelegateTipsText("開啟WebServiceUrl失敗"); } return false; } /// <summary> /// 反射構建Methods /// </summary> private bool BuildMethods() { try { //LoadFrom 載入完後會一直佔用 如果更新會導致失敗 //Assembly asm = Assembly.LoadFrom(m_OutputDllFilename); //var types = asm.GetTypes(); byte[] fileData = File.ReadAllBytes(m_OutputDllFilename); Assembly asm = Assembly.Load(fileData); Type asmType = asm.GetType(m_ProxyClassName); m_ObjInvoke = Activator.CreateInstance(asmType); //var methods = asmType.GetMethods(); var methods = Enum.GetNames(typeof(EMethod)).ToList(); //清空呼叫方法字典 m_MethodDic m_MethodDic.Clear(); //重建呼叫方法字典 m_MethodDic foreach (var item in methods) { var methodInfo = asmType.GetMethod(item); if (methodInfo != null) { var method = (EMethod)Enum.Parse(typeof(EMethod), item); m_MethodDic.Add(method, methodInfo); } } return true; } catch (Exception ex) { DelegateState.DelegateTipsText(ex.Message); return false; } } /// <summary> /// 獲取請求響應 /// </summary> /// <param name="method"></param> /// <param name="para"></param> /// <returns></returns> public ReturnInfo InvokeMethod(EMethod method, params object[] para) { ReturnInfo ri = new ReturnInfo(); if (m_MethodDic.ContainsKey(method)) { object obj = m_MethodDic[method].Invoke(m_ObjInvoke, para); Type t = obj.GetType(); PropertyInfo[] pi = t.GetProperties(); foreach (PropertyInfo item in pi) { var otherProp = t.GetProperty(item.Name); if (otherProp.GetValue(obj, null) != null) { switch (item.Name) { case "code": ri.code = Convert.ToInt32(otherProp.GetValue(obj, null)); break; case "message": ri.message = otherProp.GetValue(obj, null).ToString(); break; case "rownum": ri.rownum = Convert.ToInt32(otherProp.GetValue(obj, null)); break; case "infoXML": ri.infoXML = otherProp.GetValue(obj, null).ToString(); break; default: break; } } } } return ri; } /// <summary> /// webservice 代理 查詢類介面 /// </summary> /// <param name="jkxlh"></param> /// <param name="jkid"></param> /// <param name="QueryXmlDoc"></param> /// <returns></returns> public ReturnInfo queryObjectOut(string jkxlh, string jkid, string QueryXmlDoc) { ReturnInfo ri = new ReturnInfo(); string[] args = new string[3]; try { args[0] = jkxlh; args[1] = jkid; args[2] = QueryXmlDoc; ri = InvokeMethod(EMethod.queryObjectOut, args); } catch (Exception ex) { DelegateState.DelegateTipsText(ex.Message); } return ri; } public ReturnInfo writeObjectOut(string jkxlh, string jkid, string WriteXmlDoc) { ReturnInfo ri = new ReturnInfo(); Dictionary<string, string> sResult = new Dictionary<string, string>(); string[] args = new string[3]; try { args[0] = jkxlh; args[1] = jkid; args[2] = WriteXmlDoc; ri = InvokeMethod(EMethod.queryObjectOut, args); } catch (Exception ex) { DelegateState.DelegateTipsText(ex.Message); } return ri; } // <summary> /// 將一個物件轉換為指定型別 /// </summary> /// <param name="obj">待轉換的物件</param> /// <param name="type">目標型別</param> /// <returns>轉換後的物件</returns> private object ConvertObject(object obj, Type type) { if (type == null) return obj; if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null; Type underlyingType = Nullable.GetUnderlyingType(type); if (type.IsAssignableFrom(obj.GetType())) // 如果待轉換物件的型別與目標型別相容,則無需轉換 { return obj; } else if ((underlyingType ?? type).IsEnum) // 如果待轉換的物件的基型別為列舉 { if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) // 如果目標型別為可空列舉,並且待轉換物件為null 則直接返回null值 { return null; } else { return Enum.Parse(underlyingType ?? type, obj.ToString()); } } else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) // 如果目標型別的基型別實現了IConvertible,則直接轉換 { try { return Convert.ChangeType(obj, underlyingType ?? type, null); } catch { return underlyingType == null ? Activator.CreateInstance(type) : null; } } else { TypeConverter converter = TypeDescriptor.GetConverter(type); if (converter.CanConvertFrom(obj.GetType())) { return converter.ConvertFrom(obj); } ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); if (constructor != null) { object o = constructor.Invoke(null); PropertyInfo[] propertys = type.GetProperties(); Type oldType = obj.GetType(); foreach (PropertyInfo property in propertys) { PropertyInfo p = oldType.GetProperty(property.Name); if (property.CanWrite && p != null && p.CanRead) { property.SetValue(o, ConvertObject(p.GetValue(obj, null), property.PropertyType), null); } } return o; } } return obj; } } }
綜合分析,需要以下步驟using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace VXXS { public class ReturnInfo { private int ncode; public int code { get { return ncode; } set { ncode = value; } } private string smessage; public string message { get { return smessage; } set { smessage = value; } } public ReturnInfo() { code = 0; smessage = "122"; } } }
1. 從目標 URL 下載 WSDL 資料。
2. 使用 ServiceDescription 建立和格式化 WSDL 文件檔案。
3. 使用 ServiceDescriptionImporter 建立客戶端代理類。
4. 使用 CodeDom 動態建立客戶端代理類程式集。
5. 利用反射呼叫相關 WebService 方法。
同時,如果已經下載生成過WS代理,可以不必再重複步驟1-5,直接通過載入dll,建立WS的呼叫介面。
相關參考博文如下:
http://blog.csdn.net/hrbeuwhw/article/details/7571939