在 WCF 中使用高效的 BinaryFormatter 序列化
本文将定义一个 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 来实现对象到二进制流的序列化及反序列化。
1: /// <summary>
2: /// 序列化门户 API
3: /// </summary>
4: public static class Serializer
5: {
6: /// <summary>
7: /// 使用二进制序列化对象。
8: /// </summary>
9: /// <param name="value"></param>
10: /// <returns></returns>
11: public static byte[] SerializeBytes(object value)
12: {
13: if (value == null) return null;
14:
15: var stream = new MemoryStream();
16: new BinaryFormatter().Serialize(stream, value);
17:
18: //var dto = Encoding.UTF8.GetString(stream.GetBuffer());
19: var bytes = stream.ToArray();
20: return bytes;
21: }
22:
23: /// <summary>
24: /// 使用二进制反序列化对象。
25: /// </summary>
26: /// <param name="bytes"></param>
27: /// <returns></returns>
28: public static object DeserializeBytes(byte[] bytes)
29: {
30: if (bytes == null) return null;
31:
32: //var bytes = Encoding.UTF8.GetBytes(dto as string);
33: var stream = new MemoryStream(bytes);
34:
35: var result = new BinaryFormatter().Deserialize(stream);
36:
37: return result;
38: }
39: }
- 添加 BinaryFormatterAdapter
添加一个 BinaryFormatterAdapter 类型,该类实现了从 WCF 序列化器到 BinaryFormatter 的甜适配。它实现 IClientMessageFormatter 及 IDispatchMessageFormatter 两个接口,并调用 Serializer 来进行二进制序列化。
1: namespace OEA.WCF
2: {
3: /// <summary>
4: /// 在内部序列化器的基础上添加 Remoting 二进制序列化的功能。
5: /// </summary>
6: internal class BinaryFormatterAdapter : IClientMessageFormatter, IDispatchMessageFormatter
7: {
8: private IClientMessageFormatter _innerClientFormatter;
9: private IDispatchMessageFormatter _innerDispatchFormatter;
10: private ParameterInfo[] _parameterInfos;
11: private string _operationName;
12: private string _action;
13:
14: /// <summary>
15: /// for client
16: /// </summary>
17: /// <param name="operationName"></param>
18: /// <param name="parameterInfos"></param>
19: /// <param name="innerClientFormatter"></param>
20: /// <param name="action"></param>
21: public BinaryFormatterAdapter(
22: string operationName,
23: ParameterInfo[] parameterInfos,
24: IClientMessageFormatter innerClientFormatter,
25: string action
26: )
27: {
28: if (operationName == null) throw new ArgumentNullException("methodName");
29: if (parameterInfos == null) throw new ArgumentNullException("parameterInfos");
30: if (innerClientFormatter == null) throw new ArgumentNullException("innerClientFormatter");
31: if (action == null) throw new ArgumentNullException("action");
32:
33: this._innerClientFormatter = innerClientFormatter;
34: this._parameterInfos = parameterInfos;
35: this._operationName = operationName;
36: this._action = action;
37: }
38:
39: /// <summary>
40: /// for server
41: /// </summary>
42: /// <param name="operationName"></param>
43: /// <param name="parameterInfos"></param>
44: /// <param name="innerDispatchFormatter"></param>
45: public BinaryFormatterAdapter(
46: string operationName,
47: ParameterInfo[] parameterInfos,
48: IDispatchMessageFormatter innerDispatchFormatter
49: )
50: {
51: if (operationName == null) throw new ArgumentNullException("operationName");
52: if (parameterInfos == null) throw new ArgumentNullException("parameterInfos");
53: if (innerDispatchFormatter == null) throw new ArgumentNullException("innerDispatchFormatter");
54:
55: this._innerDispatchFormatter = innerDispatchFormatter;
56: this._operationName = operationName;
57: this._parameterInfos = parameterInfos;
58: }
59:
60: Message IClientMessageFormatter.SerializeRequest(MessageVersion messageVersion, object[] parameters)
61: {
62: var result = new object[parameters.Length];
63: for (int i = 0; i < parameters.Length; i++) { result[i] = Serializer.SerializeBytes(parameters[i]); }
64:
65: return _innerClientFormatter.SerializeRequest(messageVersion, result);
66: }
67:
68: object IClientMessageFormatter.DeserializeReply(Message message, object[] parameters)
69: {
70: var result = _innerClientFormatter.DeserializeReply(message, parameters);
71:
72: for (int i = 0; i < parameters.Length; i++) { parameters[i] = Serializer.DeserializeBytes(parameters[i] as byte[]); }
73: result = Serializer.DeserializeBytes(result as byte[]);
74:
75: return result;
76: }
77:
78: void IDispatchMessageFormatter.DeserializeRequest(Message message, object[] parameters)
79: {
80: _innerDispatchFormatter.DeserializeRequest(message, parameters);
81:
82: for (int i = 0; i < parameters.Length; i++) { parameters[i] = Serializer.DeserializeBytes(parameters[i] as byte[]); }
83: }
84:
85: Message IDispatchMessageFormatter.SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
86: {
87: var seralizedParameters = new object[parameters.Length];
88: for (int i = 0; i < parameters.Length; i++) { seralizedParameters[i] = Serializer.SerializeBytes(parameters[i]); }
89: var serialzedResult = Serializer.SerializeBytes(result);
90:
91: return _innerDispatchFormatter.SerializeReply(messageVersion, seralizedParameters, serialzedResult);
92: }
93: }
94: }
- 添加 BinaryFormatterOperationBehavior
添加 BinaryFormatterOperationBehavior 操作行为类。这个类会设置客户端、服务端的操作的序列化器。
1: namespace OEA.WCF
2: {
3: /// <summary>
4: /// 在原始 Formatter 的基础上装饰 BinaryFormatterAdapter
5: /// <remarks>
6: /// BinaryFormatterOperationBehavior 为什么要实现为操作的行为:
7: /// 因为只有当操作的 DataContractSerializerBehavior 行为应用功能后,才能拿到 DataContractSerializerFormatter 并包装到 BinaryFormatterAdapter 中。
8: ///
9: /// 由于一个操作的操作契约在系统中只有一份。而我们期望序列化的行为只影响指定的终结点,所以这个行为在应用时,会检查是否传入的运行时,即是添加时的运行时。
10: /// </remarks>
11: /// </summary>
12: internal class BinaryFormatterOperationBehavior : IOperationBehavior
13: {
14: private object _runtime;
15:
16: internal BinaryFormatterOperationBehavior(object runtime)
17: {
18: _runtime = runtime;
19: }
20:
21: /// <summary>
22: /// 本行为只为这个运行时起作用。
23: /// </summary>
24: public object ParentRuntime
25: {
26: get { return _runtime; }
27: }
28:
29: public void ApplyClientBehavior(OperationDescription description, ClientOperation runtime)
30: {
31: if (_runtime == runtime.Parent)
32: {
33: //在之前的创建的 Formatter 的基础上,装饰新的 Formatter
34: runtime.Formatter = new BinaryFormatterAdapter(description.Name, runtime.SyncMethod.GetParameters(), runtime.Formatter, runtime.Action);
35: }
36: }
37:
38: public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation runtime)
39: {
40: if (_runtime == runtime.Parent)
41: {
42: runtime.Formatter = new BinaryFormatterAdapter(description.Name, description.SyncMethod.GetParameters(), runtime.Formatter);
43: }
44: }
45:
46: public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { }
47:
48: public void Validate(OperationDescription description) { }
49: }
50: }
- 添加终结点行为 EnableBinaryFormatterBehavior
添加终结点行为 EnableBinaryFormatterBehavior,实现为该终结点下的所有操作添加 BinaryFormatterOperationBehavior 的逻辑。
1: namespace OEA.WCF
2: {
3: class EnableBinaryFormatterBehavior : IEndpointBehavior
4: {
5: public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
6: {
7: foreach (var operation in endpoint.Contract.Operations)
8: {
9: DecorateFormatterBehavior(operation, clientRuntime);
10: }
11: }
12:
13: public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
14: {
15: foreach (var operation in endpoint.Contract.Operations)
16: {
17: DecorateFormatterBehavior(operation, endpointDispatcher.DispatchRuntime);
18: }
19: }
20:
21: public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
22:
23: public void Validate(ServiceEndpoint endpoint) { }
24:
25: private static void DecorateFormatterBehavior(OperationDescription operation, object runtime)
26: {
27: //这个行为附加一次。
28: var dfBehavior = operation.Behaviors.Find<BinaryFormatterOperationBehavior>();
29: if (dfBehavior == null)
30: {
31: //装饰新的操作行为
32: //这个行为是操作的行为,但是我们期望只为当前终结点做操作的序列化,所以传入 runtime 进行区分。
33: dfBehavior = new BinaryFormatterOperationBehavior(runtime);
34: operation.Behaviors.Add(dfBehavior);
35: }
36: }
37: }
38: }
- 添加行为扩展元素 EnableBinaryFormatterBehaviorElement
添加终结点行为扩展类,使得该类在配置文件可以使用。它指定了对应的运行时行为类型是 EnableBinaryFormatterBehavior
1: namespace OEA.WCF
2: {
3: /// <summary>
4: /// 启用旧的 BinaryFormatter 来对数据进行序列化。
5: /// </summary>
6: public class EnableBinaryFormatterBehaviorElement : BehaviorExtensionElement
7: {
8: public override Type BehaviorType
9: {
10: get { return typeof(EnableBinaryFormatterBehavior); }
11: }
12:
13: protected override object CreateBehavior()
14: {
15: return new EnableBinaryFormatterBehavior();
16: }
17: }
18: }
使用方法
要使用这个扩展,只需要在客户端、服务端做相应的配置即可:
服务端配置
在 system.serviceModel 中添加扩展及行为配置:
1: <system.serviceModel>
2: <behaviors>
3: <endpointBehaviors>
4: <behavior name="enableRemotingBinarySerialization">
5: <remotingBinarySerialization/>
6: </behavior>
7: </endpointBehaviors>
8: </behaviors>
9: <extensions>
10: <behaviorExtensions>
11: <add name="remotingBinarySerialization" type="OEA.WCF.EnableBinaryFormatterBehaviorElement, OEA"/>
12: </behaviorExtensions>
13: </extensions>
14: </system.serviceModel>
为服务终结点添加行为配置 behaviorConfiguration="enableRemotingBinarySerialization"。
1: <system.serviceModel>
2: <services>
3: <service name="OEA.Server.Hosts.WcfPortal" behaviorConfiguration="includesException">
4: <endpoint address="/Binary" binding="customBinding" bindingConfiguration="compactBindingConfig"
5: behaviorConfiguration="enableRemotingBinarySerialization"
6: contract="OEA.Server.Hosts.IWcfPortal"/>
7: </service>
8: </services>
9: </system.serviceModel>
客户端
客户端同样添加相应的扩展及行为配置,并添加到服务终结点上即可。
效果
效果图:

以上是使用公司目前正在开发的系统的数据量进行测试的结果。可以看到,使用 WCF 直接二进制序列化时,32000 行数据序列化后大小是 28.34M(黄底),而启用这个扩展进行序列化后大小是 13.89M(浅绿底)。当同时使用 WCF 二进制序列化及 BinaryFormatter 序列化后,数据大小是10.42 M(绿底)。
Note同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。
在 WCF 中使用高效的 BinaryFormatter 序列化的更多相关文章
- 在Dubbo中使用高效的Java序列化(Kryo和FST)
在Dubbo中使用高效的Java序列化(Kryo和FST) 作者:沈理 文档版权:Creative Commons 3.0许可证 署名-禁止演绎 完善中…… TODO 生成可点击的目录 目录 序列化漫 ...
- 十五天精通WCF——第十二天 说说wcf中的那几种序列化
我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf ...
- [转]十五天精通WCF——第十二天 说说wcf中的那几种序列化
我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf ...
- 在Wcf中应用ProtoBuf替代默认的序列化器
Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...
- Entity Framework在WCF中序列化的问题
问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...
- WCF技术剖析之十三:序列化过程中的已知类型(Known Type)
原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...
- .net的XML对象序列化VS WCF中xml序列化问题
整理一下 .net 对象序列化注意事项: 1. 字段:必须是 public类型 2.属性:只读或者只写的属性不被序列化,只有 可读可写并且赋值的才可以 序列化: Someclass obj = new ...
- Entity Framework在WCF中序列化的问题(转)
问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...
- WCF中常用的binding方式
WCF中常用的binding方式: BasicHttpBinding: 用于把 WCF 服务当作 ASMX Web 服务.用于兼容旧的Web ASMX 服务.WSHttpBinding: 比 Basi ...
随机推荐
- SCRIPT65535: 意外地调用了方法或属性访问 ie下不兼容 解决
一般有一下几种 $("#id").text("xxx") 改成 $("#id").attr("text"," ...
- Excel导入导出的业务进化场景及组件化的设计方案(上)
1:前言 看过我文章的网友们都知道,通常前言都是我用来打酱油扯点闲情的. 自从写了上面一篇文章之后,领导就找我谈话了,怕我有什么想不开. 所以上一篇的(下)篇,目前先不出来了,哪天我异地二次回忆的时候 ...
- EasyPR--开发详解(3)高斯模糊、灰度化和Sobel算子
在上篇文章中我们了解了PlateLocate的过程中的所有步骤.在本篇文章中我们对前3个步骤,分别是高斯模糊.灰度化和Sobel算子进行分析. 一.高斯模糊 1.目标 对图像去噪,为边缘检测算法做准备 ...
- 分享一个css3写的气泡对话框,适合于即时通讯和留言本的动态内容
效果预览: css code .message_content{width:100%;margin-top:10px;clear:both;float:left;} .face{float:left; ...
- 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递
通过第一天的学习之后,我们相信您已经对MVC有一些基本了解. 本节所讲的内容是在上节的基础之上,因此需要确保您是否掌握了上一节的内容.本章的目标是在今天学习结束时利用最佳实践解决方案创建一个小型的MV ...
- 【原】Telerik radwindow 在IE10下偶尔报 unable to get value of the property 'toLowerCase' 的解决办法
笔者正在开发的新项目在升级了最新版本的Telerik Control和决定支持IE10后,遇到了一个很棘手的问题,偶尔会在打开Radwindow的时候报JS error :unable to get ...
- Android开发学习之路-带文字的图片分享
有用过微信分享SDK的都应该知道,微信分享到朋友圈的时候是不能同时分享图片和文字的,只要有缩略图,那么文字就不会生效.那么问题就来了,如果我们想把APP内的某些内容连带图片一起分享到微信,是不是没办法 ...
- java IO流 之 字节流
一.file类的常用操作 File file=new File("E:\\test\\javaIo"); System.out.println(file.isDirectory() ...
- CRM/ERP 企业管理软件中常见的七种程序设计模式
管理软件中的常见代码设计模式,来自于业务上的需要,有不恰当的地方欢迎批评指正. 1 RE-TRY 重试模式 场景:在连接数据库服务器时,如果SQL Server数据库没有启动或正在启动,我们需要有一 ...
- 【解决】Word 在试图打开文件时遇到错误 请尝试下列方法:* xxx * xxx * xxx
有好几种情况,我先说我的这个情况 1.word设置不当导致 看图: 然后就能打开了~ 2.word格式问题,比如原来是doc,被人手动改成docx~~~ 解决方法:改回来 3.word版本不兼容,比如 ...