在.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服务程序来处理。换句话说,根据短信记录的类别不同,会转到不同的服务中,这个服务按设计是可以通过配置文件来添加的。
例如:
<
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
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public ServiceEntry(int id, string name, string sign, string wsdl)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_ID = id;
_Name = name;
_Sign = sign;
_Type = GetTypeFromWsdl(_Name, wsdl);
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
_Instance = Activator.CreateInstance(_Type, new object[]
{_Url, _MethodName});
_Method = _Type.GetMethod(_MethodName, BindingFlags.Instance | BindingFlags.Public);
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
private int _ID;
private string _Name, _Sign;
private object _Instance;
private MethodInfo _Method;
public string Execute(params string[] args)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return _Method.Invoke(_Instance, args).ToString();
}
private static Type GetTypeFromWsdl(string name, string wsdl)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![dot.gif](https://www.cnblogs.com/Images/dot.gif)
}
}
于是,就有了这个完整的方案:
1.定义一个基类,动态生成的类将基于这个类工作:
public
class
SmsAgentServiceClient : SoapHttpClientProtocol
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public SmsAgentServiceClient(string url, string methodName)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Url = url;
_MethodName = methodName;
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
protected string _MethodName;
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
protected string HookInvoke(string[] args)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
object[] results = Invoke(_MethodName, args);
return results[0].ToString();
}
}
2.这个分析Wsdl的代码太冗长,这里忽略。以下的代码解决从wsdl获取足够信息后生成动态代理代码:
![dot.gif](https://www.cnblogs.com/Images/dot.gif)
//
分析基类
Type super
=
typeof
(SmsAgentServiceClient);
ConstructorInfo ctorInfo
=
super.GetConstructor(
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
new
Type[]
{typeof(string), typeof(string)}
);
MethodInfo invokeInfo
=
super.GetMethod(
"
HookInvoke
"
,
BindingFlags.Instance
|
BindingFlags.NonPublic);
![None.gif](/Images/OutliningIndicators/None.gif)
//
建立程序集
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
"
);
![None.gif](/Images/OutliningIndicators/None.gif)
//
建立类
TypeBuilder type
=
module.DefineType(typeName, TypeAttributes.Class, super);
Type serviceAttributeType
=
typeof
(WebServiceBindingAttribute);
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
ConstructorInfo serviceAttributeCtor
=
serviceAttributeType.GetConstructor(
new
Type[]
{}
);
PropertyInfo serviceAttributeNamespace
=
serviceAttributeType.GetProperty(
"
Namespace
"
,
BindingFlags.Instance
|
BindingFlags.Public);
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
type.SetCustomAttribute(
new
CustomAttributeBuilder(serviceAttributeCtor,
new
object
[]
{}
,
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
new
PropertyInfo[]
{serviceAttributeNamespace}
,
new
object
[]
{namespaceName}
));
![None.gif](/Images/OutliningIndicators/None.gif)
//
建立构造器
ConstructorBuilder ctor
=
type.DefineConstructor(MethodAttributes.Public,
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
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);
![None.gif](/Images/OutliningIndicators/None.gif)
//
建立方法
MethodBuilder method
=
type.DefineMethod(methodName,
MethodAttributes.Public
|
MethodAttributes.Final,
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
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(
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
new
Type[]
{typeof(string)}
);
method.SetCustomAttribute(
new
CustomAttributeBuilder(methodAttributeCtor,
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
new
object
[]
{namespaceName + methodName}
));
![None.gif](/Images/OutliningIndicators/None.gif)
//
建立方法体
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只需要调用基类即可。
二、为简化设计,可以将参数及返回值全部改成字符串型。