您的位置:首页 > 其它

在 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来实现对象到二进制流的序列化及反序列化。

///<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

同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: