动态创建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,生成客户代理类,动态编译,调用获取数据的过程,不是一般的慢,这也是美的代价吧,而且是我们不能承受的代价。如果直接把这种方式用在项目中,肯定是不行的,一种初步的思路是利用缓存,将生成代理类缓存起来,第一次访问添加到缓存后,以后直接从内存中获取,想必可以大幅提高效率,接下来会做这方面的研究,提升效率,使这种方式具有实用性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值