Web服务客户端的动态方案

在.net下一般的的Web服务开发是这样的:先规划服务端的服务程序,例如.asmx请求处理程序。然后用disco实用程序生成发现文件,再用wsdl实用程序生成代理类的源文件,将这个源文件编译到客户端应用程序中。如果根据业务需求,修改了Web服务程序,例如增加了一个参数,那么这个过程就必须重复一遍,或者至少客户端程序必须改动。
在某些情况下,这样的要求是苛刻的,或者至少是代价高昂的。例如,我的客户端程序是一个7*24运行的一个Windows服务应用程序,不允许任意停机。这样所有静态的客户端方案虽然美好,却代价高昂。
理论上所有静态的方案都可以很轻松地修改为动态方案。我于是开始设计这个动态方案。
这个方案的实质是,动态获取.wsdl文档,获取Web服务的方法原型,用一个通用的方法来调用,而实际转接到相应的服务方法上。

先计划将代理类不直接从SoapHttpClientProtocol继承,而是从SoapHttpClientProtocol的基类继承,然后仿照SoapHttpClientProtocol的实现做一些实现,结果发现是死路一条。其一是SoapHttpClientProtocol中使用的一些重要的中间类型都是私有的;其二是SoapHttpClientProtocol的SOAP封包是反射器完成的,而自己实现的仿真SoapHttpClientProtocol无法满足私有的反射器的需要。
接下来选择一个合适的动态生成代码的方案。CodeDom稍微简单一些,但是对于代码量较小的方案,不是太实用。
最后只好选择Emit方案了。经过几个小时的努力,终于实现了。与直接使用.wsdl生成的代理比较,速度相差无异。

下面是一个示例,解决一个短信的代理服务需求。其背景是:网络设备维护一个数据库,通过增加记录和标记记录这两种方式接收短信或者发送短信。这个SOAP客户端是一个正常的Windows服务应用程序,定期通过ODBC访问这个数据库中的某个表,当发现未标记的记录时,取出来交给对应的Web服务程序来处理。换句话说,根据短信记录的类别不同,会转到不同的服务中,这个服务按设计是可以通过配置文件来添加的。
例如:

None.gif < services >
None.gif    
< service  id ="3"  name ="BartonAgent"  sign ="Barton"  wsdl ="bartonagent.wsdl"   />
None.gif    
< service  id ="4"  name ="JeffAgent"  wsdl ="jeffagent.wsdl"   />
None.gif
</ services >
None.gif

由一个工具维护这个配置文件。当配置变动时,配置工具重启Windows服务程序,动态将指定的Web服务加入到服务列表中。

这是客户端的调用模型:

None.gif public   sealed   class  ServiceEntry
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
public ServiceEntry(int id, string name, string sign, string wsdl)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        _ID 
= id;
InBlock.gif        _Name 
= name;
InBlock.gif        _Sign 
= sign;
InBlock.gif        _Type 
= GetTypeFromWsdl(_Name, wsdl);
ExpandedSubBlockStart.gifContractedSubBlock.gif        _Instance 
= Activator.CreateInstance(_Type, new object[] dot.gif{_Url, _MethodName});
InBlock.gif        _Method 
= _Type.GetMethod(_MethodName, BindingFlags.Instance | BindingFlags.Public);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
private int _ID;
InBlock.gif    
private string _Name, _Sign;
InBlock.gif    
private object _Instance;
InBlock.gif    
private MethodInfo _Method;
InBlock.gif    
InBlock.gif    
public string Execute(params string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
return _Method.Invoke(_Instance, args).ToString();
ExpandedSubBlockEnd.gif    }

InBlock.gif    
InBlock.gif    
private static Type GetTypeFromWsdl(string name, string wsdl)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        dot.gif
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
于是,就有了这个完整的方案:
1.定义一个基类,动态生成的类将基于这个类工作:
None.gif public   class  SmsAgentServiceClient : SoapHttpClientProtocol
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
public SmsAgentServiceClient(string url, string methodName)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        Url 
= url;
InBlock.gif        _MethodName 
= methodName;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
protected string _MethodName;
InBlock.gif
InBlock.gif    
protected string HookInvoke(string[] args) 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
object[] results = Invoke(_MethodName, args);
InBlock.gif        
return results[0].ToString();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
2.这个分析Wsdl的代码太冗长,这里忽略。以下的代码解决从wsdl获取足够信息后生成动态代理代码:
None.gif     dot.gif
None.gif    
//  分析基类
None.gif
    Type super  =   typeof (SmsAgentServiceClient);
None.gif    ConstructorInfo ctorInfo 
=  super.GetConstructor(
ExpandedBlockStart.gifContractedBlock.gif        
new  Type[]  dot.gif {typeof(string), typeof(string)} );
None.gif    MethodInfo invokeInfo 
=  super.GetMethod( " HookInvoke " ,
None.gif        BindingFlags.Instance 
|  BindingFlags.NonPublic);
None.gif
None.gif    
//  建立程序集
None.gif
    AssemblyName aname  =   new  AssemblyName();
None.gif    aname.Name 
=   " SmsAgentServiceClient.Attachments " ;
None.gif    aname.Version 
=   new  Version( " 1.0.0.0 " );
None.gif    AssemblyBuilder assembly 
=  AppDomain.CurrentDomain.DefineDynamicAssembly(aname,
None.gif        AssemblyBuilderAccess.Run);
None.gif    ModuleBuilder module 
=  assembly.DefineDynamicModule( " MyModule " );
None.gif
None.gif    
//  建立类
None.gif
    TypeBuilder type  =  module.DefineType(typeName, TypeAttributes.Class, super);
None.gif    Type serviceAttributeType 
=   typeof (WebServiceBindingAttribute);
ExpandedBlockStart.gifContractedBlock.gif    ConstructorInfo serviceAttributeCtor 
=  serviceAttributeType.GetConstructor( new  Type[]  dot.gif {} );
None.gif    PropertyInfo serviceAttributeNamespace 
=  serviceAttributeType.GetProperty( " Namespace " ,
None.gif        BindingFlags.Instance 
|  BindingFlags.Public);
ExpandedBlockStart.gifContractedBlock.gif    type.SetCustomAttribute(
new  CustomAttributeBuilder(serviceAttributeCtor,  new   object []  dot.gif {} ,
ExpandedBlockStart.gifContractedBlock.gif        
new  PropertyInfo[]  dot.gif {serviceAttributeNamespace} new   object []  dot.gif {namespaceName} ));
None.gif
None.gif    
//  建立构造器
None.gif
    ConstructorBuilder ctor  =  type.DefineConstructor(MethodAttributes.Public,
ExpandedBlockStart.gifContractedBlock.gif        CallingConventions.Standard, 
new  Type[]  dot.gif {typeof(string), typeof(string)} );
None.gif    
//  建立构造器体
None.gif
    ILGenerator ctorGenerator  =  ctor.GetILGenerator();
None.gif    ctorGenerator.Emit(OpCodes.Ldarg_0);
None.gif    ctorGenerator.Emit(OpCodes.Ldarg_1);
None.gif    ctorGenerator.Emit(OpCodes.Ldarg_2);
None.gif    ctorGenerator.Emit(OpCodes.Call, ctorInfo);
None.gif    ctorGenerator.Emit(OpCodes.Ret);
None.gif
None.gif    
//  建立方法
None.gif
    MethodBuilder method  =  type.DefineMethod(methodName,
None.gif        MethodAttributes.Public 
|  MethodAttributes.Final, 
ExpandedBlockStart.gifContractedBlock.gif        
typeof ( string ),  new  Type[]  dot.gif {typeof(string[])} );
None.gif    method.DefineParameter(
1 , ParameterAttributes.None, mobile);
None.gif    method.DefineParameter(
2 , ParameterAttributes.None, content);
None.gif    Type methodAttributeType 
=   typeof (SoapDocumentMethodAttribute);
None.gif    ConstructorInfo methodAttributeCtor 
=  methodAttributeType.GetConstructor(
ExpandedBlockStart.gifContractedBlock.gif        
new  Type[]  dot.gif {typeof(string)} );
None.gif    method.SetCustomAttribute(
new  CustomAttributeBuilder(methodAttributeCtor,
ExpandedBlockStart.gifContractedBlock.gif        
new   object []  dot.gif {namespaceName + methodName} ));
None.gif
None.gif    
//  建立方法体
None.gif
    ILGenerator methodGenerator  =  method.GetILGenerator();
None.gif    LocalBuilder p1 
=  methodGenerator.DeclareLocal( typeof ( string ));
None.gif    Label l1 
=  methodGenerator.DefineLabel();
None.gif    methodGenerator.Emit(OpCodes.Ldarg_0);
None.gif    methodGenerator.Emit(OpCodes.Ldarg_1);
None.gif    methodGenerator.Emit(OpCodes.Call, invokeInfo);
None.gif    methodGenerator.Emit(OpCodes.Stloc, p1);
None.gif    methodGenerator.Emit(OpCodes.Br_S, l1);
None.gif    methodGenerator.MarkLabel(l1);
None.gif    methodGenerator.Emit(OpCodes.Ldloc, p1);
None.gif    methodGenerator.Emit(OpCodes.Ret);
None.gif    
None.gif    
return  type.CreateType();
None.gif
结论:
一、在使用Emit生成代码时尽可能采用继承自自有基类,将不变的部分写到基类中,Emit只需要调用基类即可。
二、为简化设计,可以将参数及返回值全部改成字符串型。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值