在.net下一般的的Web服务开发是这样的:先规划服务端的服务程序,例如.asmx请求处理程序。然后用disco实用程序生成发现文件,再用wsdl实用程序生成代理类的源文件,将这个源文件编译到客户端应用程序中。如果根据业务需求,修改了Web服务程序,例如增加了一个参数,那么这个过程就必须重复一遍,或者至少客户端程序必须改动。 先计划将代理类不直接从SoapHttpClientProtocol继承,而是从SoapHttpClientProtocol的基类继承,然后仿照SoapHttpClientProtocol的实现做一些实现,结果发现是死路一条。其一是SoapHttpClientProtocol中使用的一些重要的中间类型都是私有的;其二是SoapHttpClientProtocol的SOAP封包是反射器完成的,而自己实现的仿真SoapHttpClientProtocol无法满足私有的反射器的需要。 下面是一个示例,解决一个短信的代理服务需求。其背景是:网络设备维护一个数据库,通过增加记录和标记记录这两种方式接收短信或者发送短信。这个SOAP客户端是一个正常的Windows服务应用程序,定期通过ODBC访问这个数据库中的某个表,当发现未标记的记录时,取出来交给对应的Web服务程序来处理。换句话说,根据短信记录的类别不同,会转到不同的服务中,这个服务按设计是可以通过配置文件来添加的。 <services> <service id="3" name="BartonAgent" sign="Barton" wsdl="bartonagent.wsdl" /> <service id="4" name="JeffAgent" wsdl="jeffagent.wsdl" /> </services> 由一个工具维护这个配置文件。当配置变动时,配置工具重启Windows服务程序,动态将指定的Web服务加入到服务列表中。 这是客户端的调用模型: public sealed class ServiceEntry { public ServiceEntry(int id, string name, string sign, string wsdl) { _ID = id; _Name = name; _Sign = sign; _Type = GetTypeFromWsdl(_Name, wsdl); _Instance = Activator.CreateInstance(_Type, new object[] {_Url, _MethodName}); _Method = _Type.GetMethod(_MethodName, BindingFlags.Instance | BindingFlags.Public); } private int _ID; private string _Name, _Sign; private object _Instance; private MethodInfo _Method; public string Execute(params string[] args) { return _Method.Invoke(_Instance, args).ToString(); } private static Type GetTypeFromWsdl(string name, string wsdl) { } } 1.定义一个基类,动态生成的类将基于这个类工作: public class SmsAgentServiceClient : SoapHttpClientProtocol { public SmsAgentServiceClient(string url, string methodName) { Url = url; _MethodName = methodName; } protected string _MethodName; protected string HookInvoke(string[] args) { object[] results = Invoke(_MethodName, args); return results[0].ToString(); } } // 分析基类 Type super = typeof(SmsAgentServiceClient); ConstructorInfo ctorInfo = super.GetConstructor( new Type[] {typeof(string), typeof(string)}); MethodInfo invokeInfo = super.GetMethod("HookInvoke", BindingFlags.Instance | BindingFlags.NonPublic); // 建立程序集 AssemblyName aname = new AssemblyName(); aname.Name = "SmsAgentServiceClient.Attachments"; aname.Version = new Version("1.0.0.0"); AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run); ModuleBuilder module = assembly.DefineDynamicModule("MyModule"); // 建立类 TypeBuilder type = module.DefineType(typeName, TypeAttributes.Class, super); Type serviceAttributeType = typeof(WebServiceBindingAttribute); ConstructorInfo serviceAttributeCtor = serviceAttributeType.GetConstructor(new Type[] {}); PropertyInfo serviceAttributeNamespace = serviceAttributeType.GetProperty("Namespace", BindingFlags.Instance | BindingFlags.Public); type.SetCustomAttribute(new CustomAttributeBuilder(serviceAttributeCtor, new object[] {}, new PropertyInfo[] {serviceAttributeNamespace}, new object[] {namespaceName})); // 建立构造器 ConstructorBuilder ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] {typeof(string), typeof(string)}); // 建立构造器体 ILGenerator ctorGenerator = ctor.GetILGenerator(); ctorGenerator.Emit(OpCodes.Ldarg_0); ctorGenerator.Emit(OpCodes.Ldarg_1); ctorGenerator.Emit(OpCodes.Ldarg_2); ctorGenerator.Emit(OpCodes.Call, ctorInfo); ctorGenerator.Emit(OpCodes.Ret); // 建立方法 MethodBuilder method = type.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Final, typeof(string), new Type[] {typeof(string[])}); method.DefineParameter(1, ParameterAttributes.None, mobile); method.DefineParameter(2, ParameterAttributes.None, content); Type methodAttributeType = typeof(SoapDocumentMethodAttribute); ConstructorInfo methodAttributeCtor = methodAttributeType.GetConstructor( new Type[] {typeof(string)}); method.SetCustomAttribute(new CustomAttributeBuilder(methodAttributeCtor, new object[] {namespaceName + methodName})); // 建立方法体 ILGenerator methodGenerator = method.GetILGenerator(); LocalBuilder p1 = methodGenerator.DeclareLocal(typeof(string)); Label l1 = methodGenerator.DefineLabel(); methodGenerator.Emit(OpCodes.Ldarg_0); methodGenerator.Emit(OpCodes.Ldarg_1); methodGenerator.Emit(OpCodes.Call, invokeInfo); methodGenerator.Emit(OpCodes.Stloc, p1); methodGenerator.Emit(OpCodes.Br_S, l1); methodGenerator.MarkLabel(l1); methodGenerator.Emit(OpCodes.Ldloc, p1); methodGenerator.Emit(OpCodes.Ret); return type.CreateType(); 一、在使用Emit生成代码时尽可能采用继承自自有基类,将不变的部分写到基类中,Emit只需要调用基类即可。 二、为简化设计,可以将参数及返回值全部改成字符串型。 |
|
来自: ThinkTank_引擎 > 《WebService》