第一种方式通过在内存中创建动态程序集的方式完成了动态调用过程;
第二种方式将客户端代理类生成程序集文件保存到硬盘,然后可以通过 Assembly.LoadFrom() 载入并进行反射调用。对于需要多次调用的系统,要比每次生成动态程序集效率高出很多;
第三种方式是保存源码文件到硬盘中,然后再进行反射调用。
这里将只讨论第二种方式,这种方式也是我们在实际应用中最常用的。这种方式只下载 一次 WSDL 信息并创建代理类的程序集。往后程序每次启动都会反射之前创建好的程序集。
如果是 Web服务 URL 变更,只需要修改 App.config 中的 WebServiceUrl 和 ProxyClassName 配置项,并将程序根目录下生成的程序集删除即可。下次程序启动又会重新下载WSDL信息并创建代理类的程序集。
public class WebServiceHelper
{
/// <summary>
/// 输出的dll文件名称
/// </summary>
private string m_OutputDllFilename = "WS.dll";
/// <summary>
/// WebService代理网址
/// </summary>
public string m_WebServiceUrl = "http://127.0.0.1:8080/uapws/service/classname";
/// <summary>
/// WebService代理类名称
/// </summary>
public string m_ProxyClassName = "ClassName";
public Type m_asmType;
/// <summary>
/// WebService代理类实例
/// </summary>
public object m_ObjInvoke;
/// <summary>
/// 调用方法名列表
/// </summary>
public List<string> m_MethodName;
/// <summary>
/// WebService接口方法字典
/// </summary>
public Dictionary<string, MethodInfo> m_MethodDic = new Dictionary<string, MethodInfo>();
/// <summary>
/// WebService 代理是否运行
/// </summary>
public bool bProxyRun = false;
public WebServiceHelper(string dllname, string WSURL, string classname, List<string> methodname)
{
m_OutputDllFilename = dllname;
m_WebServiceUrl = WSURL + "?WSDL";
m_ProxyClassName = classname;
m_MethodName = methodname;
bProxyRun = false;
if (CreateWebService())
{
bProxyRun = true;
}
}
/// <summary>
/// 创建webservice代理
/// </summary>
/// <returns></returns>
public bool CreateWebService()
{
try
{
// 如果程序集已存在,直接使用
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(m_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);
m_asmType = asm.GetType(m_ProxyClassName);
m_ObjInvoke = Activator.CreateInstance(m_asmType);
//清空调用方法字典 m_MethodDic
m_MethodDic.Clear();
//重建调用方法字典 m_MethodDic
foreach (string name in m_MethodName)
{
System.Reflection.MethodInfo item = m_asmType.GetMethod(name);
m_MethodDic.Add(name, item);
}
return true;
}
catch (Exception ex)
{
//DelegateState.DelegateTipsText(ex.Message);
return false;
}
}
/// <summary>
/// 获取请求响应
/// </summary>
/// <param name="method"></param>
/// <param name="para"></param>
/// <returns></returns>
public object InvokeMethod(string method, params object[] para)
{
object obj = null;
//System.Reflection.MethodInfo mi = m_asmType.GetMethod(method);
//object obj = mi.Invoke(m_ObjInvoke, para);
//Type t = obj.GetType();
//PropertyInfo[] pi = t.GetProperties();
if (m_MethodDic.ContainsKey(method))
{
obj = m_MethodDic[method].Invoke(m_ObjInvoke, para);
}
return obj;
}
// <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;
}
}
配置文件
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="wsGetUrl" value="http://172.30.33.198:8063/uapws/service/nc.pub.levm.levmpub.itf.IBditfws"/>
<add key="wsGetDll" value="WSGet.dll"/>
<add key="wsGetClass" value="IBditfws"/>
<add key="wsSetUrl" value="http://172.30.33.198:8063/uapws/service/nc.itf.levmunattendedprocess.poundprocess.ICardListenerService"/>
<add key="wsSetDll" value="WSSet.dll"/>
<add key="wsSetClass" value="ICardListenerService"/>
</appSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration>