项目中需要用到WebService的方式来进行两个服务之间的方法调用,之前都是在项目中添加服务引用的方式来实现,但是这种方式有一个弊端,就是如果提供WebService服务的一方的IP、端口一旦变更,
调用的一方代码就需要重新编译部署,实际使用不是很方便,因此就采用了下面的方法,通过在代码中动态加载WebService的方式,把IP地址、端口都做成配置项。这样如果只是url的ip和端口出现变更,
就不需要再去重新编译代码了。
具体代码如下:
public static class WebServiceHelper { static SortedList<string, Type> _typeList = new SortedList<string, Type>(); #region InvokeWebService static string GetCacheKey(string url, string className) { return url.ToLower() + className; } static Type GetTypeFromCache(string url, string className) { string key = GetCacheKey(url, className); foreach (KeyValuePair<string, Type> pair in _typeList) { if (key == pair.Key) { return pair.Value; } } return null; } static Type GetTypeFromWebService(string url, string className) { if ((className == null) || (className == "")) { className = GetWsClassName(url); } //获取WSDL WebClient wc = new WebClient(); Stream stream = wc.OpenRead(url); ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, "", ""); CodeNamespace cn = new CodeNamespace(); //生成客户端代理类代码 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); CSharpCodeProvider csc = new CSharpCodeProvider(); //设定编译参数 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(className, true, true); return t; } /// <summary> /// 动态调用web服务 /// </summary> /// <param name="url">WebService地址</param> /// <param name="methodName">需要调用的方法</param> /// <param name="args">方法用到的参数列表(需要按照顺序)</param> /// <returns></returns> public static object InvokeWebService(string url, string methodName, object[] args) { return InvokeWebService(url, null, methodName, args); } public static object InvokeWebService(string url, string className, string methodName, object[] args) { try { Type t = GetTypeFromCache(url, className); if (t == null) { t = GetTypeFromWebService(url, className); //添加到缓冲中 string key = GetCacheKey(url, className); _typeList.Add(key, t); } object obj = Activator.CreateInstance(t); MethodInfo mi = t.GetMethod(methodName); return mi.Invoke(obj, args); } catch (Exception ex) { throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace)); } } private static string GetWsClassName(string wsUrl) { string[] parts = wsUrl.Split('/'); string[] pps = parts[parts.Length - 1].Split('?'); return pps[0]; } #endregion }
这是最底层的方法类,在实际使用中,我又在上层封装了一个具体接口方法调用的类,示例如下:
public class WebHelper { ServiceReference.findSceneVideoDataServicePortTypeClient ts = new ServiceReference.findSceneVideoDataServicePortTypeClient(); LogPrn logger = new LogPrn("WebHelper"); //现勘webservice地址 private string xkWebserveUrl = ""; public WebHelper(string webserveurl) { xkWebserveUrl = webserveurl; } /// <summary> /// 数量获取服务 /// </summary> /// <param name="unitcode">六位单位代码(选填)</param> /// <param name="kno">关联编号(选填)</param> /// <param name="updatetimefrom">起始时间(yyyy-mm-dd HH:MM:SS)(选填)</param> /// <param name="updatetimeto">结束时间(yyyy-mm-dd HH:MM:SS)(选填)</param> /// <param name="scenedetail">发案地点(选填)</param> /// <param name="allflag">查询范围(必填)</param> /// <param name="caseno">案件编号(A号)(选填)</param> /// <returns>-1:单位代码必须为6位数字; /// -2:时间不符合格式:yyyy-mm-dd HH:MM:SS; /// n(n>=0):根据条件查询结果数量; /// -3:其他异常</returns> /// <summary> public int getOriginalDataCount(string unitcode, string kno, string updatetimefrom, string updatetimeto, string scenedetail, string allflag, string caseno) { //if(string.IsNullOrEmpty(unitcode)) //{ // return -3; //} if (string.IsNullOrEmpty(allflag)) { return -3; } int result = 0; try { //result = ts.getOriginalDataCount(unitcode, kno, updatetimefrom, updatetimeto); string[] args = new string[7]; args[0] = unitcode; args[1] = kno; args[2] = updatetimefrom; args[3] = updatetimeto; args[4] = scenedetail; args[5] = allflag; args[6] = caseno; object obj = WebServiceHelper.InvokeWebService(xkWebserveUrl, "getOriginalDataCount", args); result = (int)obj; } catch (Exception ex) { //TODO 记录异常信息 logger.Error(ex.ToString()); result = -3; } return result; } /// <summary> /// 安装位置信息反馈 /// </summary> /// <param name="macAddress">Mac地址(必填)</param> /// <param name="installPath">视频客户端安装路径(必填),绝对路径,要精确到exe文件</param> /// <returns>1.反馈成功,0.反馈失败,-1.mac地址格式不正确,-2.安装路径格式不正确</returns> public string InstallPositionFeedBack(string macAddress, string installPath) { if (string.IsNullOrEmpty(macAddress) || string.IsNullOrEmpty(installPath)) { return null; } string result = ""; try { string[] args = new string[2]; args[0] = macAddress; args[1] = installPath; object obj = WebServiceHelper.InvokeWebService(xkWebserveUrl, "installPositionFeedBack", args); result = (string)obj; return result; } catch (Exception ex) { //TODO 记录异常信息 logger.Error(ex.ToString()); return null; } } }