• 在 WCF 中使用高效的 BinaryFormatter 序列化


    在 WCF 中使用高效的 BinaryFormatter 序列化

    本文将定义一个 WCF 终结点行为扩展,以在 WCF 中使用更高效的 BinaryFormatter 进行二进制序列化,并实现对是否使用传统二进制序列化功能的可配置。

    • 介绍
    • 实现步骤
    • 使用方法
    • 效果

    介绍


    在 online_icon[6]OEA 框架中,是使用 WCF 作为数据传输框架。但是使用 WCF 内部的二进制序列化,序列化后的数据大小,要比使用传统的 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter 类进行序列化后的数据大小要大得多。作为使用 .NET 框架的系统内部互联,往往期望在使用 WCF 获取统一传输方案的同时,还能得到 BinaryFormatter 类的序列化性能。所以本篇文章将设计一个 WCF 终结点行为扩展,来配置是否使用 BinaryFormatter 进行数据的序列化。

    alert_note[10] 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 Binary Serializer - 序列化数据量效果图[7]

      以上是使用公司目前正在开发的系统的数据量进行测试的结果。可以看到,使用 WCF 直接二进制序列化时,32000 行数据序列化后大小是 28.34M(黄底),而启用这个扩展进行序列化后大小是 13.89M(浅绿底)。当同时使用 WCF 二进制序列化及 BinaryFormatter 序列化后,数据大小是10.42 M(绿底)。

      alert_note[11] Note

      同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。

     

    欢迎转载,转载请注明:

    转载自 胡庆访http://zgynhqf.cnblogs.com/ ]

     
  • 相关阅读:
    多态
    没有抽象方法的抽象类有什么意义
    抽象类继承(雇员练习)
    怎样在win7中 安装Tomcat7.0
    继承训练
    Java的接口和抽象类
    jQuery插件的学习
    jQuery学习之路-A
    android之路-android事件处理-OnTouchListener
    丢弃的东西,还能否找回?
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3153544.html
Copyright © 2020-2023  润新知