本文将定义一个 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 序列化的更多相关文章

  1. 在Dubbo中使用高效的Java序列化(Kryo和FST)

    在Dubbo中使用高效的Java序列化(Kryo和FST) 作者:沈理 文档版权:Creative Commons 3.0许可证 署名-禁止演绎 完善中…… TODO 生成可点击的目录 目录 序列化漫 ...

  2. 十五天精通WCF——第十二天 说说wcf中的那几种序列化

    我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf ...

  3. [转]十五天精通WCF——第十二天 说说wcf中的那几种序列化

    我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf ...

  4. 在Wcf中应用ProtoBuf替代默认的序列化器

    Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...

  5. Entity Framework在WCF中序列化的问题

    问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...

  6. WCF技术剖析之十三:序列化过程中的已知类型(Known Type)

    原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...

  7. .net的XML对象序列化VS WCF中xml序列化问题

    整理一下 .net 对象序列化注意事项: 1. 字段:必须是 public类型 2.属性:只读或者只写的属性不被序列化,只有 可读可写并且赋值的才可以 序列化: Someclass obj = new ...

  8. Entity Framework在WCF中序列化的问题(转)

    问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...

  9. WCF中常用的binding方式

    WCF中常用的binding方式: BasicHttpBinding: 用于把 WCF 服务当作 ASMX Web 服务.用于兼容旧的Web ASMX 服务.WSHttpBinding: 比 Basi ...

随机推荐

  1. 剑指offer编程题java实现(正在更新)

    面试题三:查找二维数组中元素问题 public static void main(String[] args){ int[][] num = {{1,2,8,9},{2,4,9,12},{4,7,10 ...

  2. iOS开发点滴:iPhone屏幕适配

    最近开始做iOS开发,遇到一些小问题和解决方法,记录下.   今天是iPhone屏幕适配 iPhone5出来之后屏幕就有iPhone就有了2种尺寸:3.5寸和4寸,xcode 5 的IB设计器里面界面 ...

  3. [Xamarin] 使用Webview 來做APP (转帖)

    有時候,企業要求的沒有這麼多,他原本可能官方網站就已經有支援Mobile Web Design 他只需要原封不動的開發一個APP 也或是,他只是要型錄型,或是問卷調查的型的APP,這時候透過類似像if ...

  4. 避免Castle Windsor引起的内存泄露

    原文地址: http://nexussharp.wordpress.com/2012/04/21/castle-windsor-avoid-memory-leaks-by-learning-the-u ...

  5. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  6. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  7. innerHTML,outerHTML,innerText,outerText区别以及insertAdjacentHTML()方法

    在需要给文档插入大量的新的HTML标记的情况下,通过多次DOM操作先创建节点再指定它们之间的关系会非常麻烦而且效率不高,相对而言插入标记的方法会更加简单,速度也更快. 插入标记中有这四个属性inner ...

  8. JDK下载、安装、配置环境变量笔记

    自己总是在下载.安装.配置JDK的环境变量,但是这些需要的专业知识并不难,但有很多细节很重要,总是记不住,而且这些细节一旦出错影响还是很严重的,在网上查到的信息很多都比较零散,而且讲解得也不是很详细, ...

  9. 【Win 10应用开发】如何知道UAP在哪个平台上运行

    面向22世纪的现代化应用程序可以同时在多种设备上运行,于是有朋友会有一个疑问:有时候,我们还真的需要判断一下,UAP应用程序在哪个平台上运行.尽管大多情况下我们不必要这样做,但某些特殊情况还得考虑.比 ...

  10. Windows phone 全景视图

    Windows phone 全景视图下为了实现可以上下滑动需要使用listbox. 需要的布局什么的,在listbox中填写 <ListBox Name="ListBox_new&qu ...