在 WCF 中使用高效的 BinaryFormatter 序列化
2013-06-24 18:19
323 查看
本文将定义一个WCF终结点行为扩展,以在WCF中使用更高效的BinaryFormatter进行二进制序列化,并实现对是否使用传统二进制序列化功能的可配置。
介绍
实现步骤
使用方法
效果
介绍
在
OEA框架中,是使用WCF作为数据传输框架。但是使用WCF内部的二进制序列化,序列化后的数据大小,要比使用传统的System.Runtime.Serialization.Formatters.Binary.BinaryFormatter类进行序列化后的数据大小要大得多。作为使用.NET框架的系统内部互联,往往期望在使用WCF获取统一传输方案的同时,还能得到BinaryFormatter类的序列化性能。所以本篇文章将设计一个WCF终结点行为扩展,来配置是否使用BinaryFormatter进行数据的序列化。
Tip
只能在操作上添加二进制序列化的行为。这是因为WCF的扩展点中,只有操作才支持设置IClientMessageFormatter及IDispatchMessageFormatter。
WCF中,要实现替换操作的序列化器,最直接的方式应该是使用一个实现IOperationBehavior的特性(Attribute),并将该特性直接标记到操作方法上。但是,这样会导致该方法在所有的终结点都使用BinaryFormatter来进行序列化。这并不是我们所想要的,所以只能使用配置的方法来对WCF进行扩展。
实现步骤
封装BinaryFormatter
首先,需要对BinaryFormatter进行一个简单的封装。该类使用BinaryFormatter来实现对象到二进制流的序列化及反序列化。
添加BinaryFormatterAdapter
添加一个BinaryFormatterAdapter类型,该类实现了从WCF序列化器到BinaryFormatter的甜适配。它实现IClientMessageFormatter及IDispatchMessageFormatter两个接口,并调用Serializer来进行二进制序列化。
添加BinaryFormatterOperationBehavior
添加BinaryFormatterOperationBehavior操作行为类。这个类会设置客户端、服务端的操作的序列化器。
[code]{
[/code]
添加终结点行为EnableBinaryFormatterBehavior
添加终结点行为EnableBinaryFormatterBehavior,实现为该终结点下的所有操作添加BinaryFormatterOperationBehavior的逻辑。
[code]{
[/code]
添加行为扩展元素EnableBinaryFormatterBehaviorElement
添加终结点行为扩展类,使得该类在配置文件可以使用。它指定了对应的运行时行为类型是EnableBinaryFormatterBehavior
[code]{
[/code]
使用方法
要使用这个扩展,只需要在客户端、服务端做相应的配置即可:
服务端配置
在system.serviceModel中添加扩展及行为配置:
[code]<behaviors>
[/code]
为服务终结点添加行为配置behaviorConfiguration="enableRemotingBinarySerialization"。
[code]<services>
[/code]
客户端
客户端同样添加相应的扩展及行为配置,并添加到服务终结点上即可。
效果
效果图:
以上是使用公司目前正在开发的系统的数据量进行测试的结果。可以看到,使用WCF直接二进制序列化时,32000行数据序列化后大小是28.34M(黄底),而启用这个扩展进行序列化后大小是13.89M(浅绿底)。当同时使用WCF二进制序列化及BinaryFormatter序列化后,数据大小是10.42M(绿底)。
Note
同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。
介绍
实现步骤
使用方法
效果
介绍
在
Tip
只能在操作上添加二进制序列化的行为。这是因为WCF的扩展点中,只有操作才支持设置IClientMessageFormatter及IDispatchMessageFormatter。
WCF中,要实现替换操作的序列化器,最直接的方式应该是使用一个实现IOperationBehavior的特性(Attribute),并将该特性直接标记到操作方法上。但是,这样会导致该方法在所有的终结点都使用BinaryFormatter来进行序列化。这并不是我们所想要的,所以只能使用配置的方法来对WCF进行扩展。
实现步骤
封装BinaryFormatter
首先,需要对BinaryFormatter进行一个简单的封装。该类使用BinaryFormatter来实现对象到二进制流的序列化及反序列化。
///<summary>
///序列化门户API
///</summary>
publicstaticclassSerializer
{
///<summary>
///使用二进制序列化对象。
///</summary>
///<paramname="value"></param>
///<returns></returns>
publicstaticbyte[]SerializeBytes(objectvalue)
{
if(value==null)returnnull;
varstream=newMemoryStream();
newBinaryFormatter().Serialize(stream,value);
//vardto=Encoding.UTF8.GetString(stream.GetBuffer());
varbytes=stream.ToArray();
returnbytes;
}
///<summary>
///使用二进制反序列化对象。
///</summary>
///<paramname="bytes"></param>
///<returns></returns>
publicstaticobjectDeserializeBytes(byte[]bytes)
{
if(bytes==null)returnnull;
//varbytes=Encoding.UTF8.GetBytes(dtoasstring);
varstream=newMemoryStream(bytes);
varresult=newBinaryFormatter().Deserialize(stream);
returnresult;
}
}
添加BinaryFormatterAdapter
添加一个BinaryFormatterAdapter类型,该类实现了从WCF序列化器到BinaryFormatter的甜适配。它实现IClientMessageFormatter及IDispatchMessageFormatter两个接口,并调用Serializer来进行二进制序列化。
namespaceOEA.WCF
{
///<summary>
///在内部序列化器的基础上添加Remoting二进制序列化的功能。
///</summary>
internalclassBinaryFormatterAdapter:IClientMessageFormatter,IDispatchMessageFormatter
{
privateIClientMessageFormatter_innerClientFormatter;
privateIDispatchMessageFormatter_innerDispatchFormatter;
privateParameterInfo[]_parameterInfos;
privatestring_operationName;
privatestring_action;
///<summary>
///forclient
///</summary>
///<paramname="operationName"></param>
///<paramname="parameterInfos"></param>
///<paramname="innerClientFormatter"></param>
///<paramname="action"></param>
publicBinaryFormatterAdapter(
stringoperationName,
ParameterInfo[]parameterInfos,
IClientMessageFormatterinnerClientFormatter,
stringaction
)
{
if(operationName==null)thrownewArgumentNullException("methodName");
if(parameterInfos==null)thrownewArgumentNullException("parameterInfos");
if(innerClientFormatter==null)thrownewArgumentNullException("innerClientFormatter");
if(action==null)thrownewArgumentNullException("action");
this._innerClientFormatter=innerClientFormatter;
this._parameterInfos=parameterInfos;
this._operationName=operationName;
this._action=action;
}
///<summary>
///forserver
///</summary>
///<paramname="operationName"></param>
///<paramname="parameterInfos"></param>
///<paramname="innerDispatchFormatter"></param>
publicBinaryFormatterAdapter(
stringoperationName,
ParameterInfo[]parameterInfos,
IDispatchMessageFormatterinnerDispatchFormatter
)
{
if(operationName==null)thrownewArgumentNullException("operationName");
if(parameterInfos==null)thrownewArgumentNullException("parameterInfos");
if(innerDispatchFormatter==null)thrownewArgumentNullException("innerDispatchFormatter");
this._innerDispatchFormatter=innerDispatchFormatter;
this._operationName=operationName;
this._parameterInfos=parameterInfos;
}
MessageIClientMessageFormatter.SerializeRequest(MessageVersionmessageVersion,object[]parameters)
{
varresult=newobject[parameters.Length];
for(inti=0;i<parameters.Length;i++){result[i]=Serializer.SerializeBytes(parameters[i]);}
return_innerClientFormatter.SerializeRequest(messageVersion,result);
}
objectIClientMessageFormatter.DeserializeReply(Messagemessage,object[]parameters)
{
varresult=_innerClientFormatter.DeserializeReply(message,parameters);
for(inti=0;i<parameters.Length;i++){parameters[i]=Serializer.DeserializeBytes(parameters[i]asbyte[]);}
result=Serializer.DeserializeBytes(resultasbyte[]);
returnresult;
}
voidIDispatchMessageFormatter.DeserializeRequest(Messagemessage,object[]parameters)
{
_innerDispatchFormatter.DeserializeRequest(message,parameters);
for(inti=0;i<parameters.Length;i++){parameters[i]=Serializer.DeserializeBytes(parameters[i]asbyte[]);}
}
MessageIDispatchMessageFormatter.SerializeReply(MessageVersionmessageVersion,object[]parameters,objectresult)
{
varseralizedParameters=newobject[parameters.Length];
for(inti=0;i<parameters.Length;i++){seralizedParameters[i]=Serializer.SerializeBytes(parameters[i]);}
varserialzedResult=Serializer.SerializeBytes(result);
return_innerDispatchFormatter.SerializeReply(messageVersion,seralizedParameters,serialzedResult);
}
}
}
添加BinaryFormatterOperationBehavior
添加BinaryFormatterOperationBehavior操作行为类。这个类会设置客户端、服务端的操作的序列化器。
namespaceOEA.WCF
[code]{
///<summary>
///在原始Formatter的基础上装饰BinaryFormatterAdapter
///<remarks>
///BinaryFormatterOperationBehavior为什么要实现为操作的行为:
///因为只有当操作的DataContractSerializerBehavior行为应用功能后,才能拿到DataContractSerializerFormatter并包装到BinaryFormatterAdapter中。
///
///由于一个操作的操作契约在系统中只有一份。而我们期望序列化的行为只影响指定的终结点,所以这个行为在应用时,会检查是否传入的运行时,即是添加时的运行时。
///</remarks>
///</summary>
internalclassBinaryFormatterOperationBehavior:IOperationBehavior
{
privateobject_runtime;
internalBinaryFormatterOperationBehavior(objectruntime)
{
_runtime=runtime;
}
///<summary>
///本行为只为这个运行时起作用。
///</summary>
publicobjectParentRuntime
{
get{return_runtime;}
}
publicvoidApplyClientBehavior(OperationDescriptiondescription,ClientOperationruntime)
{
if(_runtime==runtime.Parent)
{
//在之前的创建的Formatter的基础上,装饰新的Formatter
runtime.Formatter=newBinaryFormatterAdapter(description.Name,runtime.SyncMethod.GetParameters(),runtime.Formatter,runtime.Action);
}
}
publicvoidApplyDispatchBehavior(OperationDescriptiondescription,DispatchOperationruntime)
{
if(_runtime==runtime.Parent)
{
runtime.Formatter=newBinaryFormatterAdapter(description.Name,description.SyncMethod.GetParameters(),runtime.Formatter);
}
}
publicvoidAddBindingParameters(OperationDescriptiondescription,BindingParameterCollectionparameters){}
publicvoidValidate(OperationDescriptiondescription){}
}
}
[/code]
添加终结点行为EnableBinaryFormatterBehavior
添加终结点行为EnableBinaryFormatterBehavior,实现为该终结点下的所有操作添加BinaryFormatterOperationBehavior的逻辑。
namespaceOEA.WCF
[code]{
classEnableBinaryFormatterBehavior:IEndpointBehavior
{
publicvoidApplyClientBehavior(ServiceEndpointendpoint,ClientRuntimeclientRuntime)
{
foreach(varoperationinendpoint.Contract.Operations)
{
DecorateFormatterBehavior(operation,clientRuntime);
}
}
publicvoidApplyDispatchBehavior(ServiceEndpointendpoint,EndpointDispatcherendpointDispatcher)
{
foreach(varoperationinendpoint.Contract.Operations)
{
DecorateFormatterBehavior(operation,endpointDispatcher.DispatchRuntime);
}
}
publicvoidAddBindingParameters(ServiceEndpointendpoint,BindingParameterCollectionbindingParameters){}
publicvoidValidate(ServiceEndpointendpoint){}
privatestaticvoidDecorateFormatterBehavior(OperationDescriptionoperation,objectruntime)
{
//这个行为附加一次。
vardfBehavior=operation.Behaviors.Find<BinaryFormatterOperationBehavior>();
if(dfBehavior==null)
{
//装饰新的操作行为
//这个行为是操作的行为,但是我们期望只为当前终结点做操作的序列化,所以传入runtime进行区分。
dfBehavior=newBinaryFormatterOperationBehavior(runtime);
operation.Behaviors.Add(dfBehavior);
}
}
}
}
[/code]
添加行为扩展元素EnableBinaryFormatterBehaviorElement
添加终结点行为扩展类,使得该类在配置文件可以使用。它指定了对应的运行时行为类型是EnableBinaryFormatterBehavior
namespaceOEA.WCF
[code]{
///<summary>
///启用旧的BinaryFormatter来对数据进行序列化。
///</summary>
publicclassEnableBinaryFormatterBehaviorElement:BehaviorExtensionElement
{
publicoverrideTypeBehaviorType
{
get{returntypeof(EnableBinaryFormatterBehavior);}
}
protectedoverrideobjectCreateBehavior()
{
returnnewEnableBinaryFormatterBehavior();
}
}
}
[/code]
使用方法
要使用这个扩展,只需要在客户端、服务端做相应的配置即可:
服务端配置
在system.serviceModel中添加扩展及行为配置:
<system.serviceModel>
[code]<behaviors>
<endpointBehaviors>
<behaviorname="enableRemotingBinarySerialization">
<remotingBinarySerialization/>
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<addname="remotingBinarySerialization"type="OEA.WCF.EnableBinaryFormatterBehaviorElement,OEA"/>
</behaviorExtensions>
</extensions>
</system.serviceModel>
[/code]
为服务终结点添加行为配置behaviorConfiguration="enableRemotingBinarySerialization"。
<system.serviceModel>
[code]<services>
<servicename="OEA.Server.Hosts.WcfPortal"behaviorConfiguration="includesException">
<endpointaddress="/Binary"binding="customBinding"bindingConfiguration="compactBindingConfig"
behaviorConfiguration="enableRemotingBinarySerialization"
contract="OEA.Server.Hosts.IWcfPortal"/>
</service>
</services>
</system.serviceModel>
[/code]
客户端
客户端同样添加相应的扩展及行为配置,并添加到服务终结点上即可。
效果
效果图:
以上是使用公司目前正在开发的系统的数据量进行测试的结果。可以看到,使用WCF直接二进制序列化时,32000行数据序列化后大小是28.34M(黄底),而启用这个扩展进行序列化后大小是13.89M(浅绿底)。当同时使用WCF二进制序列化及BinaryFormatter序列化后,数据大小是10.42M(绿底)。
Note
同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。
相关文章推荐
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 化零为整WCF(9) - 序列化(DataContractSerializer, XmlSerializer, DataContractJsonSerializer, SoapFormatter, BinaryFormatter)
- 不同Assembly中使用 BinaryFormatter 序列化的问题
- 在DubboX中使用高效的Java序列化(Kryo和FST)
- WCF 第六章 序列化和编码 使用代理序列化类型
- WCF 小程序案例以及序列化的使用
- asp.net使用 csla 序列化错误 有关于wcf的错误.
- Dubbo中使用高效的Java序列化(Kryo和FST)
- 序列化(串行化)- 使用BinaryFormatter进行序列化
- wcf中序列化BinaryFormatter,DataContractJsonSerializer,DataContractSerializer,SoapFormatter,XmlSerializer
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(一)
- WCF 第六章 编码与序列化 使用NetDataContractSerializer共享类型
- WCF 第六章 序列化和编码 为自定义序列化使用XmlSerializer
- Dubbo中使用高效的Java序列化(Kryo和FST)
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(二)
- 在java中使用kryo框架来实现高效序列化与反序列化
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(三)