在 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 ...
随机推荐
- 剑指offer编程题java实现(正在更新)
面试题三:查找二维数组中元素问题 public static void main(String[] args){ int[][] num = {{1,2,8,9},{2,4,9,12},{4,7,10 ...
- iOS开发点滴:iPhone屏幕适配
最近开始做iOS开发,遇到一些小问题和解决方法,记录下. 今天是iPhone屏幕适配 iPhone5出来之后屏幕就有iPhone就有了2种尺寸:3.5寸和4寸,xcode 5 的IB设计器里面界面 ...
- [Xamarin] 使用Webview 來做APP (转帖)
有時候,企業要求的沒有這麼多,他原本可能官方網站就已經有支援Mobile Web Design 他只需要原封不動的開發一個APP 也或是,他只是要型錄型,或是問卷調查的型的APP,這時候透過類似像if ...
- 避免Castle Windsor引起的内存泄露
原文地址: http://nexussharp.wordpress.com/2012/04/21/castle-windsor-avoid-memory-leaks-by-learning-the-u ...
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject
本人博客已转移至:http://www.exblr.com/liam 为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...
- innerHTML,outerHTML,innerText,outerText区别以及insertAdjacentHTML()方法
在需要给文档插入大量的新的HTML标记的情况下,通过多次DOM操作先创建节点再指定它们之间的关系会非常麻烦而且效率不高,相对而言插入标记的方法会更加简单,速度也更快. 插入标记中有这四个属性inner ...
- JDK下载、安装、配置环境变量笔记
自己总是在下载.安装.配置JDK的环境变量,但是这些需要的专业知识并不难,但有很多细节很重要,总是记不住,而且这些细节一旦出错影响还是很严重的,在网上查到的信息很多都比较零散,而且讲解得也不是很详细, ...
- 【Win 10应用开发】如何知道UAP在哪个平台上运行
面向22世纪的现代化应用程序可以同时在多种设备上运行,于是有朋友会有一个疑问:有时候,我们还真的需要判断一下,UAP应用程序在哪个平台上运行.尽管大多情况下我们不必要这样做,但某些特殊情况还得考虑.比 ...
- Windows phone 全景视图
Windows phone 全景视图下为了实现可以上下滑动需要使用listbox. 需要的布局什么的,在listbox中填写 <ListBox Name="ListBox_new&qu ...