• 动态创建WebService


          需求背景:公司对外提供了几个WebService服务,部署在多台服务器上,WebMethod参数为XML格式,测试起来很不方便,只能由开发人员进行测试,单靠一两个人测试质量难以保证。本着对用户负责,解放开发人员的初衷,开发一个WebService的测试程序。

          设计思路:首先考虑的是通过添加Web引用的方式,让.NET编译器帮我们生成服务代理,然后调用对应的Web服务,这种方式最简单,但却和Web服务的地址、方法名、参数全都绑定在了一起,如果Web服务的方法或者是参数发生改变,就需要重新添加引用,使用起来不大方便。而且,还要应对若干个站点的Web服务,都要添加Web引用(其实这个理由不成立,添加Web引用方式应对多个站点没问题,只要站点提供的Web服务相同,就可以靠动态设置Url的方式,实现调用不同站点的WebService,根本不用为每个站点都添加Web引用),很不方便,于是想到了动态创建调用WebService。

          动态调用WebService的方法在园子里可以搜索到很多,看看标题看看内容,发现大多源于同一版本,这里作为知识整理记录下来,内容来自园友博客。动态调用,需要准备几个方面的知识:反射、WSDL、CodeDomProvider、编程使用编译器、泛型、WebService,具体关系参见下图,图的上半部分是动态创建调用WebService的过程,下半部分是通过添加Web引用调用WebService的过程。

          

          实现动态创建WebService的完整代码如下,只需要调用函数public static T InvokeMethod<T>(string url, string methodName, params object[] args)传入服务地址、方法名称、参数集合即可实现动态调用Web方法。

    WebServiceHelper实现
    using System;
    using System.Web;
    using System.Net;
    using System.IO;
    using System.CodeDom;
    using Microsoft.CSharp;
    using System.Reflection;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Web.Services.Protocols;
    using System.Web.Services.Description;

    namespace HIIStest.UIBase
    {
    /// <summary>
    /// 动态调用WebService帮助类
    /// </summary>
    public class WebServiceHelper
    {
    /// <summary>
    /// 调用WebService
    /// </summary>
    /// <typeparam name="T">方法返回值类型</typeparam>
    /// <param name="url">服务网址</param>
    /// <param name="methodName">方法名</param>
    /// <param name="args">方法参数</param>
    /// <returns>返回调用结果</returns>
    public static T InvokeMethod<T>(string url, string methodName, params object[] args)
    {
    //设置泛型类型的默认值
    T result = default(T);
    //获得类型
    Type t = GetType(url, GetWsClassName(url));
    try
    {
    //依据类型创建实例
    object obj = CreateInstance(t);
    //调用方法
    result = InvokeMethod<T>(obj, t, methodName, args);
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    return result;
    }

    /// <summary>
    /// 调用WebService
    /// </summary>
    /// <typeparam name="T">方法返回值类型</typeparam>
    /// <param name="InstanceObject">实例</param>
    /// <param name="t">类的类型(Type)</param>
    /// <param name="methodName">方法名</param>
    /// <param name="args">方法参数</param>
    /// <returns>返回调用结果</returns>
    private static T InvokeMethod<T>(object InstanceObject, Type t, string methodName, params object[] args)
    {
    T result
    = default(T);
    //依据方法名获取方法信息
    System.Reflection.MethodInfo mi = t.GetMethod(methodName);
    //调用实例方法
    result = (T)mi.Invoke(InstanceObject, args);
    return result;
    }

    /// <summary>
    /// 获取类型
    /// </summary>
    /// <param name="url">服务网址</param>
    /// <param name="className">服务类型名称</param>
    /// <returns>返回Type</returns>
    private static Type GetType(string url, string className)
    {
    Type result
    = null;
    string @namespace = "HIIStest.UIBase.Temp.WebService";
    if (string.IsNullOrEmpty(className))
    {
    className
    = WebServiceHelper.GetWsClassName(url);
    }
    //获取WSDL
    WebClient wc = new WebClient();
    Stream stream
    = wc.OpenRead(url + "?WSDL");
    ServiceDescription sd
    = ServiceDescription.Read(stream);
    ServiceDescriptionImporter sdi
    = new ServiceDescriptionImporter();
    sdi.AddServiceDescription(sd,
    "", "");
    //生成客户端代理类代码
    CodeNamespace np = new CodeNamespace(@namespace);
    CodeCompileUnit ccu
    = new CodeCompileUnit();
    ccu.Namespaces.Add(np);
    sdi.Import(np, ccu);
    //获取c#编译器的实例
    CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
    //设定编译参数
    CompilerParameters param = new CompilerParameters();
    param.GenerateExecutable
    = false;
    param.GenerateInMemory
    = true;
    param.ReferencedAssemblies.Add(
    "System.dll");
    param.ReferencedAssemblies.Add(
    "System.XML.dll");
    param.ReferencedAssemblies.Add(
    "System.Web.Services.dll");
    param.ReferencedAssemblies.Add(
    "System.Data.dll");
    //编译代理类
    CompilerResults cr = provider.CompileAssemblyFromDom(param, 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;
    //反射获取类型
    result = assembly.GetType(@namespace + "." + className, true, true);
    return result;
    }

    /// <summary>
    /// 依据类型创建实例
    /// </summary>
    /// <param name="t">类型(Type)</param>
    /// <returns>类型实例</returns>
    private static object CreateInstance(Type t)
    {
    //获取类型的默认值
    object result = null;
    try
    {
    //创建实例类型
    result = Activator.CreateInstance(t);
    }
    catch (Exception ex)
    {
    throw new Exception(ex.Message);
    }
    return result;
    }

    /// <summary>
    /// 给实例对象属性赋值
    /// </summary>
    /// <param name="InstanceObject">对象实例</param>
    /// <param name="valueObj"></param>
    /// <param name="t">类型</param>
    /// <param name="propertyName">属性的名字</param>
    private static void SetProperty(object InstanceObject, object valueObj, Type t, string propertyName)
    {
    //依据类型获得类型属性
    System.Reflection.PropertyInfo pi = t.GetProperty(propertyName, BindingFlags.Public);
    //给实例对象属性赋值
    pi.SetValue(InstanceObject, valueObj, null);
    }

    /// <summary>
    /// 获得类的名字
    /// </summary>
    /// <param name="url">网址</param>
    /// <returns>类型名</returns>
    private static string GetWsClassName(string url)
    {
    string result = string.Empty;
    string[] parts = url.Split('/');
    string fileName = parts[parts.Length - 1];
    result
    = fileName.Split('.')[0];
    return result;
    }
    }
    }

           动态调用http://www.webxml.com.cn/WebServices/WeatherWebService.asmx提供Web服务获取天气预报的示例。这个示例稍加改造,利用中移动139邮箱邮件短信提醒功能,就可以做成一个自动发送天气预报短信的小程序,感兴趣的朋友可以试试哦。

    获取天气信息
    private void GetWeather()
    {
    string url = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx";
    string methodName = "getWeatherbyCityName";
    object[] args = new object[1];
    args[
    0] = "北京";

    String[] str
    = WebServiceHelper.InvokeMethod<String[]>(url, methodName, args);
    this.message.InnerHtml = str[6] + " " + str[5] + "<br />" + str[7] + "<br />" + str[10];
    }

           到这,动态创建并调用WebService的问题已经解决了,这种方式不需要添加Web引用,项目中没有额外的代码,看起来很美,可当运行示例程序时,发现原来并没有想象中的美。这种方式存在什么问题呢?效率,每一次调用Web服务,都要经历获取WSDL,生成客户代理类,动态编译,调用获取数据的过程,不是一般的慢,这也是美的代价吧,而且是我们不能承受的代价。如果直接把这种方式用在项目中,肯定是不行的,一种初步的思路是利用缓存,将生成代理类缓存起来,第一次访问添加到缓存后,以后直接从内存中获取,想必可以大幅提高效率,接下来会做这方面的研究,提升效率,使这种方式具有实用性。

  • 相关阅读:
    使用form表单上传文件
    繁星——JQuery选择器之层级
    繁星——jquery的data()方法
    running programmer——spring-01(初谈spring)
    django+javascrpt+python实现私有云盘代码
    prometheus+grafana 监控生产环境机器的系统信息、redis、mongodb以及jmx
    运维管理后台
    django+javascrpt+python实现私有云盘
    python logging模块
    python hashlib模块
  • 原文地址:https://www.cnblogs.com/freshman0216/p/1871383.html
Copyright © 2020-2023  润新知