在 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 ...
随机推荐
- IOS网络第七天WebView-01WebView和网页的交互1
******** #import "HMViewController.h" @interface HMViewController () <UIWebViewDelegate ...
- 学习笔记:delphi多线程知识
最近一直在温习旧的知识,刚好学习了一下Java的线程安全方面的知识,今天想起之前一直做的Delphi开发,所以还是有必要温习一下,看看这些不同的编程语言有什么不同之处. Delphi的线程同步方法: ...
- Linux 部署ASP.NET SQLite 应用 的坎坷之旅 附demo及源码
Linux 部署ASP.NET SQLite 应用 的坎坷之旅.文章底部 附示例代码. 有一台闲置的Linux VPS,尝试着部署一下.NET 程序,结果就踏上了坑之路,不过最后算是完美解决问题,遂记 ...
- iOS开发系列--Objective-C之KVC、KVO
概述 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听( ...
- Step by Step 创建一个 Web Service
原创地址:http://www.cnblogs.com/jfzhu/p/4022139.html 转载请注明出处 (一)创建Web Service 创建第一个项目,类型选择ASP.NET Empty ...
- CoreProfiler/NanoProfiler性能调试监控系列总目录
NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇 NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇 NanoProfiler - 适合生产环境的性能监控类 ...
- Module Zero之Nuget包
返回<Module Zero学习目录> ABP module-zero已经发布在了nuget上了.这里是所有的包列表. Abp.Zero module zero的核心包. Abp.Zero ...
- python 栈和队列(使用list实现)
5.1.1. Using Lists as Stacks The list methods make it very easy to use a list as a stack, where the ...
- Sublime Text(2/3)编译lua
想在subLime text 3中集成编译Lua(或其他语言)功能,只需要在Tool->Build System -> New Build System中将原来的 { "shel ...
- 谈谈php里的IOC控制反转,DI依赖注入
理论 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和"DI依赖注入"是什么,能够解决什么问题,这些在维基百科中有非常清晰的说明. 控制反转(In ...