通常我们在程序中需要调用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