关于.NET中动态调用Web Service服务的方法心得
介绍.NET中动态调用Web Service的相关技术文章。
在.NET中调用Web Service服务(WSDL)有两种可行的方法:
1、通过Web 服务引用,在本地生成所要调用服务的类;(静态方法)
2、通过给定的WSDL服务地址,动态生成Web Service服务类进行服务调用;(动态方法)
由于1中的方法大部分人都会经常用到,因此暂不讨论。
对于2,实现上较为复杂,主要的过程为:
(1)读取WSDL内容到内存中;
(2)根据WSDL内容,动态生成Web Service服务代码;
(3)使用动态编译技术将生成的Web Service服务代码编译为DLL;
(4)通过反射机制实现动态调用。
_________________________________________________________
有关这类技术请参考如下网站:
1、《动态调用 WebService》
地址:http://www.rainsts.net/article.asp?id=304
2、《DynWsLib》
地址:http://www.thinktecture.com/Resources/Software/DynWsLib/default.html
说明:
欧洲一家名位Thinktecture公司发布的开源项目,实现了比较完整的动态调用Web Service的方法,最新版本为1.6,支持.NET 2.0。最可贵的是这是一个完全开源的项目,我们可以下载下来根据实际情况进行一些修改,以适应不同的需求。
3、WSE
地址:http://msdn2.microsoft.com/en-us/webservices/aa740663.aspx
说明:
微软发布的支持Web Service的工具包。目前最新版本为3.0。
_________________________________________________________
Java 与 .NET的互调用
这是本文重点要讨论的话题。
由于项目需要,我们必须为客户提供一个.NET的动态调用Web Service包,以实现对Oracle的BPEL服务器上发布的Web Service进行动态调用。
在使用动态DynWSLib生成的对象进行调用的时候,发现只能够发送一次SOAP,当第二次发送之后,.NET程序会抛出链接已被断开的异常。
这样的错误十分诡异,在网上搜索了之后,发现问题在于BPEL服务器与Microsoft的IIS之间是有区别的,看来微软还是很喜欢搞垄断。
下面说说个人分析的结论,共大家参考:
问题的出现可能和微软的地层支持有关,本人猜测,微软生成的Web Service对象在发送SOAP请求时,建立的HTTP链接在请求发送完成之后,会长时间保持链接状态(即链接没有立即断开),而对于Oracle的Web服务器,当相应SOAP请求之后,会主动断开HTTP链接,这也许就是为什么用微软的东西发送SOAP消息给Oracle服务器,第一次能够成功,第二次就会报链接已断开,发送失败的错误。后面的解决方案中也部分支持了我的这个观点。
________________________________________________________
解决方案:
WSE + HTTP
1、使用WSE构造SOAP请求消息;
2、使用HTTP发送SOAP消息。
下面给出部分参考代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace My.Web.WebService
{
internal class SoapHttpClient
{
private string url = null;
public SoapHttpClient(string url)
{
this.url = url;
}
public string RequestResponse(string methodName, string envelope)
{
// 用于支持SSL
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(OnCheckRemoteCallback);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
// 必须设置该值为flase,否则出错
request.KeepAlive = false;
request.Method = "POST";
request.ContentType = "text/xml";
request.Headers.Add("SOAPAction", methodName);
UTF8Encoding encoding = new UTF8Encoding();
byte[] bodyBytes = encoding.GetBytes(envelope);
request.ContentLength = bodyBytes.Length;
using (Stream serviceRequestBodyStream = request.GetRequestStream())
{
serviceRequestBodyStream.Write(bodyBytes, 0, bodyBytes.Length);
serviceRequestBodyStream.Close();
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
string result = reader.ReadToEnd();
return result;
}
}
}
}
/// <summary>
/// Using untrusted SSL certificates
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
private static bool OnCheckRemoteCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
}
_________________________________________________________
[b]最后讨论一下有关DynWSLib动态调用的问题。[/b]
使用DynWSLib在调用Web Service服务的时候,需要对传入的参数进行适当的处理。
private void bnCalc_Click(object sender, System.EventArgs e)
{
DynamicWebServiceProxy ws = new DynamicWebServiceProxy();
ws.EnableMessageAccess = true;
ws.Wsdl = "http://localhost:3092/CatalogueDataPublishTest.asmx?WSDL";
//ws.Url = new Uri("http://localhost:3092/CatalogueDataPublishTest.asmx");
ws.TypeName = "CatalogueDataPublishTest";
ws.MethodName = "HelloWorld";
//ws.AddParameter(XmlHelper.Object2Xml(new DCMCatalogueWork()));
//ws.AddParameter(new DCMCatalogueWork());
AddParameter(ws, "dcw", new Sobey.MAM.Common.InterfaceDataType.DCM2.Catalogue.SystemDefineType());
object result = ws.InvokeCall();
MessageBox.Show(result.ToString());
}
/// <summary>
/// 添加WebService类型体系中的参数值
/// </summary>
/// <param name="ws">代理</param>
/// <param name="name">参数名字(WebService中的参数名字,这里也可以是参数位置索引)</param>
/// <param name="value">参数取值</param>
public void AddParameter(DynamicWebServiceProxy ws,string name, object value)
{
//找到该参数的类型
Type objType = GetParameterType(ws,name);
//由于2个对象只是命名空间不同,不能强制转换,就采用序列化与反序列化的方式进行深度拷贝。
ws.AddParameter(XmlHelper.Xml2Object(XmlHelper.Object2Xml(value), objType));
}
/// <summary>
/// 获取WebService方法指定参数的类型
/// </summary>
/// <param name="ws">服务代理</param>
/// <param name="parameterName">参数名称</param>
/// <returns></returns>
private Type GetParameterType(DynamicWebServiceProxy ws,string parameterName)
{
Type t = ws.Instance.GetType();
MethodInfo methodInfo = t.GetMethod(ws.MethodName);
// 获得方法的参数类型
ParameterInfo[] param = methodInfo.GetParameters();
if (param.Length < 1)
{
return null;
}
foreach (ParameterInfo pi in param)
{
if (pi.Name == parameterName)
{
return pi.ParameterType;
}
}
return null;
}