动态调用WebService的两种方法(多线程)

【转】动态调用WebService的两种方法(多线程)

       在.net中,可以添加Web 引用来添加WebService,但是这种方法的缺陷是当WebService内的方法一变动,引用的系统这边就必须更新引用,重新编译,再发布,是不是很麻烦?也未可预知?

               那么就使用动态调用WebService吧!

第1种,具体步骤:

1. 从目标 URL 下载 WSDL 数据。
2. 使用 ServiceDescription 创建和格式化 WSDL 文档文件。
3. 使用 ServiceDescriptionImporter 创建客户端代理类。
4. 使用 CodeDom 动态创建客户端代理类程序集。
5. 利用反射调用相关 WebService 方法。

代码如下:

[csharp] view plaincopy

  1. /// <summary>  

  2.     /// 在.Net环境 下,最常用的方法就是采用代理类来调用WebService,可以通过改变代理类的Url属性来实现动态调用,  

  3.     /// 但当xmlns改变时就会出错,似乎要重新 绑定Webservice并重新编译后才能再次运行。  

  4.     /// 此法子是一种动态编译并动态调用WebService的方式,这种方法效率低,而且需要有较高 的权限,否则编译失败。  

  5.     /// 此法子的缺陷。。。。。都是泪啊。。。多线程下运行第二次就报错:无法从命名空间“****”导入绑定“SyncToContractDataCenterSoap”  

  6.     /// </summary>  

  7.     public class WebServiceHelper2  

  8.     {  

  9.         #region InvokeWebService  

  10.         //动态调用web服务  

  11.         public  object InvokeWebService(string url, string methodname, object[] args)  

  12.         {  

  13.             return InvokeWebService(url, null, methodname, args);  

  14.         }  

  15.   

  16.         public  object InvokeWebService(string url, string classname, string methodname, object[] args)  

  17.         {  

  18.             string @namespace = "TransactionDataSync.Common";  

  19.             if ((classname == null) || (classname == ""))  

  20.             {  

  21.                 classname = GetWsClassName(url);  

  22.             }  

  23.   

  24.             try  

  25.             {  

  26.                 //获取WSDL  

  27.                 WebClient wc = new WebClient();  

  28.                 Stream stream = wc.OpenRead(url + "?wsdl");  

  29.                 ServiceDescription sd = ServiceDescription.Read(stream);  

  30.                 ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();  

  31.                 sdi.AddServiceDescription(sd, """");  

  32.                 CodeNamespace cn = new CodeNamespace(@namespace);  

  33.   

  34.                 //生成客户端代理类代码  

  35.                 CodeCompileUnit ccu = new CodeCompileUnit();  

  36.                 ccu.Namespaces.Add(cn);  

  37.                 sdi.Import(cn, ccu);  

  38.               //  CSharpCodeProvider csc = new CSharpCodeProvider();  

  39.              //   ICodeCompiler icc = csc.CreateCompiler();  

  40.                 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");  

  41.   

  42.                 //设定编译参数  

  43.                 CompilerParameters cplist = new CompilerParameters();  

  44.                 cplist.GenerateExecutable = false;  

  45.                 cplist.GenerateInMemory = true;  

  46.                 cplist.ReferencedAssemblies.Add("System.dll");  

  47.                 cplist.ReferencedAssemblies.Add("System.XML.dll");  

  48.                 cplist.ReferencedAssemblies.Add("System.Web.Services.dll");  

  49.                 cplist.ReferencedAssemblies.Add("System.Data.dll");  

  50.   

  51.                 //编译代理类   

  52.                 CompilerResults cr = provider.CompileAssemblyFromDom(cplist, ccu);  

  53.             //    CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu);  

  54.                 if (true == cr.Errors.HasErrors)  

  55.                 {  

  56.                     System.Text.StringBuilder sb = new System.Text.StringBuilder();  

  57.                     foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)  

  58.                     {  

  59.                         sb.Append(ce.ToString());  

  60.                         sb.Append(System.Environment.NewLine);  

  61.                     }  

  62.                     throw new Exception(sb.ToString());  

  63.                 }  

  64.   

  65.                 //生成代理实例,并调用方法  

  66.                 System.Reflection.Assembly assembly = cr.CompiledAssembly;  

  67.                 Type t = assembly.GetType(@namespace + "." + classname, truetrue);  

  68.                 object obj = Activator.CreateInstance(t);  

  69.                 System.Reflection.MethodInfo mi = t.GetMethod(methodname);  

  70.   

  71.                 return mi.Invoke(obj, args);  

  72.             }  

  73.             catch (Exception ex)  

  74.             {  

  75.                 Logger.Error(string.Format("{0} {1} {2} {3}", url, methodname, ex.Message, ex.StackTrace));  

  76.                 throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));  

  77.             }  

  78.         }  

  79.   

  80.         public  object CreateWebServiceInstance(string url, string packageName, out Type t)  

  81.         {  

  82.             return CreateWebServiceInstance(url, null,packageName, out t);  

  83.         }  

  84.   

  85.         public  object CreateWebServiceInstance(string url, string classname,string packageName,out Type t)  

  86.         {  

  87.             string @namespace = "TransactionDataSync.Common."+packageName;  

  88.             if ((classname == null) || (classname == ""))  

  89.             {  

  90.                 classname = GetWsClassName(url);  

  91.             }  

  92.             Stream stream = null;  

  93.             try  

  94.             {  

  95.                 //获取WSDL  

  96.                 WebClient wc = new WebClient();  

  97.                 stream = wc.OpenRead(url + "?WSDL");  

  98.    

  99.                 ServiceDescription sd = ServiceDescription.Read(stream);  

  100.                 ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();  

  101.                 sdi.AddServiceDescription(sd, """");  

  102.                 CodeNamespace cn = new CodeNamespace(@namespace);  

  103.   

  104.                   

  105.   

  106.                 //生成客户端代理类代码  

  107.                 CodeCompileUnit ccu = new CodeCompileUnit();  

  108.                 ccu.Namespaces.Add(cn);  

  109.                 sdi.Import(cn, ccu);  

  110.                 //  CSharpCodeProvider csc = new CSharpCodeProvider();  

  111.                 //   ICodeCompiler icc = csc.CreateCompiler();  

  112.                 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");  

  113.   

  114.                 //设定编译参数  

  115.                 CompilerParameters cplist = new CompilerParameters();  

  116.                 cplist.GenerateExecutable = false;  

  117.                 cplist.GenerateInMemory = true;  

  118.                 cplist.ReferencedAssemblies.Add("System.dll");  

  119.                 cplist.ReferencedAssemblies.Add("System.XML.dll");  

  120.                 cplist.ReferencedAssemblies.Add("System.Web.Services.dll");  

  121.                 cplist.ReferencedAssemblies.Add("System.Data.dll");  

  122.   

  123.                 //编译代理类   

  124.                 CompilerResults cr = provider.CompileAssemblyFromDom(cplist, ccu);  

  125.                 //    CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu);  

  126.                 if (true == cr.Errors.HasErrors)  

  127.                 {  

  128.                     System.Text.StringBuilder sb = new System.Text.StringBuilder();  

  129.                     foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)  

  130.                     {  

  131.                         sb.Append(ce.ToString());  

  132.                         sb.Append(System.Environment.NewLine);  

  133.                     }  

  134.                     throw new Exception(sb.ToString());  

  135.                 }  

  136.   

  137.                   

  138.   

  139.                 //生成代理实例  

  140.                 System.Reflection.Assembly assembly = cr.CompiledAssembly;  

  141.                 t = assembly.GetType(@namespace + "." + classname, truetrue);  

  142.                 

  143.                 object obj = Activator.CreateInstance(t);  

  144.                 return obj;  

  145.             }  

  146.             catch (Exception ex)  

  147.             {  

  148.                 Logger.Error(string.Format("{0} {1} {2}", url, ex.Message, ex.StackTrace));  

  149.   

  150.                 if(stream!=null)  

  151.                 {  

  152.                     stream.Close();  

  153.                     stream.Dispose();  

  154.                 }  

  155.                 throw ex;  

  156.             }  

  157.         }  

  158.   

  159.         public  object InvokeMethod(object obj, Type t, string methodname, object[] args)  

  160.         {  

  161.             try  

  162.             {  

  163.                 System.Reflection.MethodInfo mi = t.GetMethod(methodname);  

  164.   

  165.                 return mi.Invoke(obj, args);  

  166.             }  

  167.             catch(Exception ex)  

  168.             {  

  169.                 Logger.Error(string.Format("{0} {1} {2}", methodname, ex.Message, ex.StackTrace));  

  170.                 throw ex;  

  171.             }  

  172.         }  

  173.   

  174.         private  string GetWsClassName(string wsUrl)  

  175.         {  

  176.             string[] parts = wsUrl.Split('/');  

  177.             string[] pps = parts[parts.Length – 1].Split('.');  

  178.   

  179.             return pps[0];  

  180.         }  

  181.         #endregion  

调用时的代码:

[csharp] view plaincopy

  1. object instance = WebServiceHelper2.CreateWebServiceInstance(addr.Url,name, out t);  

  2.   

  3. ataSet dsContract = WebServiceHelper2.InvokeMethod(instance, t, "GetContractData"new object[]{authorizationCode}) as DataSet;  

  4.                 

缺陷:

  1. 每次调用WebService,都需要动态创建客户端代理类程序集,然后利用反射去调用方法,每次都这样啊,是不是很耗性能?特别是后台服务中调WebService,1小时运行一次,是不是很无语。。。。

  2. 正常情况下,调一个WebService是不成问题的,可是在多线程下,每个线程调一个WebService(都是不同URL的WebService),只有在编译后调第一遍是正常的,再调就报错啊,无法从命名空间“****”导入绑定“SyncToContractDataCenterSoap”,或者是调用目标异常,坑爹啊  

               第2种动态调用的法子:利用WebRequest/WebResponse进行WebService调用的类

                动态调用的类如下:

       

[csharp] view plaincopy

  1. using System;  

  2. using System.Collections;  

  3. using System.Collections.Generic;  

  4. using System.Data;  

  5. using System.IO;  

  6. using System.Linq;  

  7. using System.Net;  

  8. using System.Text;  

  9. using System.Xml;  

  10. using System.Xml.Serialization;  

  11.   

  12. namespace TransactionDataSync.Common  

  13. {  

  14.     /// <summary>  

  15.     /// 利用WebRequest/WebResponse进行WebService调用的类  

  16.     /// </summary>  

  17.     /// <remarks>  

  18.     /// 作成者:cyf  

  19.     /// </remarks>  

  20.     public class WebServiceCaller  

  21.     {  

  22.          //缓存xmlNamespace,避免重复调用GetNamespace  

  23.         private static Hashtable XML_NAMESPACE = new Hashtable();  

  24.   

  25.         /// <summary>  

  26.         /// 通过SOAP协议动态调用webservice   

  27.         /// </summary>  

  28.         /// <param name="url"> webservice地址</param>  

  29.         /// <param name="methodName"> 调用方法名</param>  

  30.         /// <param name="pars"> 参数表</param>  

  31.         /// <returns> 结果集xml</returns>  

  32.         public static XmlDocument QuerySoapWebService(String url, String methodName, Hashtable pars)  

  33.         {  

  34.             if (XML_NAMESPACE.ContainsKey(url))  

  35.             {   // 名字空间在缓存中存在时,读取缓存,然后执行调用  

  36.                 return QuerySoapWebService(url, methodName, pars, XML_NAMESPACE[url].ToString());  

  37.             }  

  38.             else  

  39.             {  

  40.                 // 名字空间不存在时直接从wsdl的请求中读取名字空间,然后执行调用  

  41.                 return QuerySoapWebService(url, methodName, pars, GetNamespace(url));  

  42.             }  

  43.         }  

  44.   

  45.         /// <summary>  

  46.         /// 通过SOAP协议动态调用webservice    

  47.         /// </summary>  

  48.         /// <param name="url"> webservice地址</param>  

  49.         /// <param name="methodName"> 调用方法名</param>  

  50.         /// <param name="pars"> 参数表</param>  

  51.         /// <param name="xmlNs"> 名字空间</param>  

  52.         /// <returns> 结果集</returns>  

  53.         private static XmlDocument QuerySoapWebService(String url, String methodName, Hashtable pars, string xmlNs)  

  54.         {     

  55.             XML_NAMESPACE[url] = xmlNs;//加入缓存,提高效率  

  56.             // 获取请求对象  

  57.             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);  

  58.             // 设置请求head  

  59.             request.Method = "POST";  

  60.             request.ContentType = "text/xml; charset=utf-8";  

  61.             request.Headers.Add("SOAPAction"""" + xmlNs + (xmlNs.EndsWith("/") ? "" : "/") + methodName + """);  

  62.             // 设置请求身份  

  63.             SetWebRequest(request);  

  64.             // 获取soap协议  

  65.             byte[] data = EncodeParsToSoap(pars, xmlNs, methodName);  

  66.             // 将soap协议写入请求  

  67.             WriteRequestData(request, data);  

  68.             XmlDocument returnDoc = new XmlDocument();  

  69.             XmlDocument returnValueDoc = new XmlDocument();  

  70.             // 读取服务端响应  

  71.             returnDoc = ReadXmlResponse(request.GetResponse());  

  72.   

  73.             XmlNamespaceManager mgr = new XmlNamespaceManager(returnDoc.NameTable);  

  74.             mgr.AddNamespace("soap""http://schemas.xmlsoap.org/soap/envelope/");  

  75.             // 返回结果  

  76.             string RetXml= returnDoc.SelectSingleNode("//soap:Body/*/*", mgr).InnerXml;  

  77.   

  78.             returnValueDoc.LoadXml("<root>" + RetXml + "</root>");  

  79.             AddDelaration(returnValueDoc);  

  80.   

  81.           /*  System.Data.DataSet ds = new System.Data.DataSet();  

  82.             XmlNodeReader reader = new XmlNodeReader(returnValueDoc);  

  83.             ds.ReadXml(reader);*/  

  84.   

  85.            // return returnValueDoc.OuterXml;  

  86.   

  87.             return returnValueDoc;  

  88.         }  

  89.   

  90.         /// <summary>  

  91.         /// 获取wsdl中的名字空间  

  92.         /// </summary>  

  93.         /// <param name="url"> wsdl地址</param>  

  94.         /// <returns> 名字空间</returns>  

  95.         private static string GetNamespace(String url)  

  96.         {  

  97.             // 创建wsdl请求对象,并从中读取名字空间  

  98.             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + "?WSDL");  

  99.             SetWebRequest(request);  

  100.             WebResponse response = request.GetResponse();  

  101.             StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);  

  102.             XmlDocument doc = new XmlDocument();  

  103.             doc.LoadXml(sr.ReadToEnd());  

  104.             sr.Close();  

  105.             return doc.SelectSingleNode("//@targetNamespace").Value;  

  106.         }  

  107.   

  108.         /// <summary>  

  109.         /// 加入soapheader节点  

  110.         /// </summary>  

  111.         /// <param name="doc"> soap文档</param>  

  112.         private static void InitSoapHeader(XmlDocument doc)  

  113.         {  

  114.             // 添加soapheader节点  

  115.             XmlElement soapHeader = doc.CreateElement("soap""Header""http://schemas.xmlsoap.org/soap/envelope/");  

  116.             //XmlElement soapId = doc.CreateElement("userid");  

  117.             //soapId.InnerText = ID;  

  118.             //XmlElement soapPwd = doc.CreateElement("userpwd");  

  119.             //soapPwd.InnerText = PWD;  

  120.             //soapHeader.AppendChild(soapId);  

  121.             //soapHeader.AppendChild(soapPwd);  

  122.             doc.ChildNodes[0].AppendChild(soapHeader);  

  123.         }  

  124.   

  125.         /// <summary>  

  126.         /// 将以字节数组的形式返回soap协议  

  127.         /// </summary>  

  128.         /// <param name="pars"> 参数表</param>  

  129.         /// <param name="xmlNs"> 名字空间</param>  

  130.         /// <param name="methodName"> 方法名</param>  

  131.         /// <returns> 字节数组</returns>  

  132.         private static byte[] EncodeParsToSoap(Hashtable pars, String xmlNs, String methodName)  

  133.         {  

  134.             XmlDocument doc = new XmlDocument();  

  135.             // 构建soap文档  

  136.             doc.LoadXml("<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>");  

  137.   

  138.             // 加入soapbody节点  

  139.             InitSoapHeader(doc);  

  140.   

  141.             // 创建soapbody节点  

  142.             XmlElement soapBody = doc.CreateElement("soap""Body""http://schemas.xmlsoap.org/soap/envelope/");  

  143.             // 根据要调用的方法创建一个方法节点  

  144.             XmlElement soapMethod = doc.CreateElement(methodName);  

  145.             soapMethod.SetAttribute("xmlns", xmlNs);  

  146.             // 遍历参数表中的参数键  

  147.             foreach (string key in pars.Keys)  

  148.             {  

  149.                 // 根据参数表中的键值对,生成一个参数节点,并加入方法节点内  

  150.                 XmlElement soapPar = doc.CreateElement(key);  

  151.                 soapPar.InnerXml = ObjectToSoapXml(pars[key]);  

  152.                 soapMethod.AppendChild(soapPar);  

  153.             }  

  154.   

  155.             // soapbody节点中加入方法节点  

  156.             soapBody.AppendChild(soapMethod);  

  157.   

  158.             // soap文档中加入soapbody节点  

  159.             doc.DocumentElement.AppendChild(soapBody);  

  160.   

  161.             // 添加声明  

  162.             AddDelaration(doc);  

  163.   

  164.             // 传入的参数有DataSet类型,必须在序列化后的XML中的diffgr:diffgram/NewDataSet节点加xmlns='' 否则无法取到每行的记录。  

  165.             XmlNode node = doc.DocumentElement.SelectSingleNode("//NewDataSet");     

  166.             if (node != null)  

  167.             {  

  168.                 XmlAttribute attr = doc.CreateAttribute("xmlns");  

  169.                 attr.InnerText = "";  

  170.                 node.Attributes.Append(attr);  

  171.             }  

  172.             // 以字节数组的形式返回soap文档  

  173.             return Encoding.UTF8.GetBytes(doc.OuterXml);  

  174.         }  

  175.   

  176.         /// <summary>  

  177.         /// 将参数对象中的内容取出  

  178.         /// </summary>  

  179.         /// <param name="o">参数值对象</param>  

  180.         /// <returns>字符型值对象</returns>  

  181.         private static string ObjectToSoapXml(object o)  

  182.         {  

  183.             XmlSerializer mySerializer = new XmlSerializer(o.GetType());  

  184.             MemoryStream ms = new MemoryStream();  

  185.             mySerializer.Serialize(ms, o);  

  186.             XmlDocument doc = new XmlDocument();  

  187.             doc.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));  

  188.             if (doc.DocumentElement != null)  

  189.             {  

  190.                 return doc.DocumentElement.InnerXml;  

  191.             }  

  192.             else  

  193.             {  

  194.                 return o.ToString();  

  195.             }  

  196.         }  

  197.   

  198.         /// <summary>  

  199.         /// 设置请求身份  

  200.         /// </summary>  

  201.         /// <param name="request"> 请求</param>  

  202.         private static void SetWebRequest(HttpWebRequest request)  

  203.         {  

  204.             request.Credentials = CredentialCache.DefaultCredentials;  

  205.             //request.Timeout = 10000;  

  206.         }  

  207.   

  208.         /// <summary>  

  209.         /// 将soap协议写入请求  

  210.         /// </summary>  

  211.         /// <param name="request"> 请求</param>  

  212.         /// <param name="data"> soap协议</param>  

  213.         private static void WriteRequestData(HttpWebRequest request, byte[] data)  

  214.         {  

  215.             request.ContentLength = data.Length;  

  216.             Stream writer = request.GetRequestStream();  

  217.             writer.Write(data, 0, data.Length);  

  218.             writer.Close();  

  219.         }  

  220.   

  221.         /// <summary>  

  222.         /// 将响应对象读取为xml对象  

  223.         /// </summary>  

  224.         /// <param name="response"> 响应对象</param>  

  225.         /// <returns> xml对象</returns>  

  226.         private static XmlDocument ReadXmlResponse(WebResponse response)  

  227.         {  

  228.             StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);  

  229.             String retXml = sr.ReadToEnd();  

  230.             sr.Close();  

  231.             XmlDocument doc = new XmlDocument();  

  232.             doc.LoadXml(retXml);  

  233.             return doc;  

  234.         }  

  235.   

  236.         /// <summary>  

  237.         /// 给xml文档添加声明  

  238.         /// </summary>  

  239.         /// <param name="doc"> xml文档</param>  

  240.         private static void AddDelaration(XmlDocument doc)  

  241.         {  

  242.             XmlDeclaration decl = doc.CreateXmlDeclaration("1.0""utf-8"null);  

  243.             doc.InsertBefore(decl, doc.DocumentElement);  

  244.         }  

  245.   

  246.   

  247.         public static String QuerySoapWebServiceString(String url, String methodName, Hashtable pars)  

  248.         {  

  249.             XmlDocument doc = QuerySoapWebService(url, methodName, pars);  

  250.             return doc.InnerText;  

  251.         }  

  252.   

  253.         public static DataSet QuerySoapWebServiceDataSet(String url, String methodName, Hashtable pars)  

  254.         {  

  255.             XmlDocument doc = QuerySoapWebService(url, methodName, pars);  

  256.             System.Data.DataSet ds = new System.Data.DataSet();  

  257.             using (XmlNodeReader reader = new XmlNodeReader(doc))  

  258.             {  

  259.                 ds.ReadXml(reader);  

  260.             }  

  261.             return ds;  

  262.         }  

  263.     }  

  264. }  

使用时的调用如下:

[csharp] view plaincopy

  1. Hashtable htParms = new Hashtable();  

  2.                htParms.Add("authorizationCode", authCode);  

  3.                DataSet dsComm = WebServiceCaller.QuerySoapWebServiceDataSet(url, "GetReceivedCommissionData", htParms);  

或者

[csharp] view plaincopy

  1. DataSet dsSelected = new DataSet();  

  2.            dsSelected.Tables.Add(dtSelected);  //需要返回给业务系统的数据集,表示哪些数据已收集  

  3.   

  4.            Hashtable htParms = new Hashtable();  

  5.            htParms.Add("authorizationCode", addr.AuthorizationCode);  

  6.            htParms.Add("dsData", dtSelected);  

  7. string result = WebServiceCaller.QuerySoapWebServiceString(url, methodName, htParms);//传入的参数是一个String类型和一个DataSet类型  

动态调用时用的是Soap协议的方式来请求的,而不是Http Post,因为当WebService中的方法传入参数中有DataSet类型,将无法用Http Post方式请求,只能用Soap。

在多线程中,同时调多个WebService,此种方法不会报错。因此我选择了它。。。。

Leave a Reply