深入理解WCF系統體系(之二:WCF客戶端如何構建?(上))


  前一節(《WCF體系架構(之一:Client與Server信息交互處理流程初略介紹)》)大致介紹了WCF服務消息處理的大致流程,WCF服務的消費者可以是WEB客戶端、也可以是其他語言開發的應用程序。
  對於WEB客戶端以及其他語言的應用程序,跨平台的性能主要是通過HTTP協議+SOAP消息機制實現。本節主要詳細介紹消息在WCF客戶端應用程序消息處理流程
------------------------------------------------------------------
-目錄:
-1、WCF通過客戶端代理調用 服務
-2、實際代理如何映射到服務目標對象上
-3、WCF客戶端框架的核心ClientRuntime建立過程
-4、ImmutableClientRuntime對象的作用
-5、客戶端操作選擇器MethodInfoOperationSelector
-6、ProxyOperationRuntime的作用
------------------------------------------------------------------


1、WCF如何處理客戶端消息調                          


  如果有.Net Remoting開發經驗,大家一定還記得在Remoting的客戶端,調用服務通過透明代理(TransparentProxy)來對服務進行調用,然后透明代理將對服務的調用轉交給實際代理(RealProxy)。在WCF同樣如此。那透明代理對應的實際代理又是什么類型的呢。?看看下面的測試例子就知道了。

var instanceContext = new InstanceContext(new CalculatorCallback());
using (var channkeFactory = new DuplexChannelFactory<ICalculator>(instanceContext, "calculator"))
{ 
    ICalculator proxy = channkeFactory.CreateChannel(); 
    Console.WriteLine("是否是透明代   理:"+RemotingServices.IsTransparentProxy(proxy));
    Console.WriteLine("透明代理類型:" + proxy.GetType());
}

 輸出結果如下:

 

2、實際代理如何映射到服務目標對象上              


首先看看實際代理對象ServiceChannelProxy字段的定義:

internal sealed class ServiceChannelProxy : RealProxy, IRemotingTypeInfo
{
  // Fields
  private const string activityIdSlotName = "E2ETrace.ActivityID";
  private Type interfaceType;
  private MethodDataCache methodDataCache;
  private MbrObject objectWrapper;
  private Type proxiedType;
  private ImmutableClientRuntime proxyRuntime;
  private ServiceChannel serviceChannel;
  /*
  */
  public override IMessage Invoke(IMessage message)
  {
    IMessage message3;
    try
    {
      IMethodCallMessage methodCall = message as IMethodCallMessage;
      if (methodCall == null)
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString("SFxExpectedIMethodCallMessage")));
      }
      MethodData methodData = this.GetMethodData(methodCall);
      switch (methodData.MethodType)
      {
        case MethodType.Service:
          return this.InvokeService(methodCall, methodData.Operation);

        case MethodType.BeginService:
          return this.InvokeBeginService(methodCall, methodData.Operation);

        case MethodType.EndService:
          return this.InvokeEndService(methodCall, methodData.Operation);

        case MethodType.Channel:
          return this.InvokeChannel(methodCall);

        case MethodType.Object:
          return this.InvokeObject(methodCall);

        case MethodType.GetType:
          return this.InvokeGetType(methodCall);
       }
      throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Invalid proxy method type", new object[0])));
    }
    catch (Exception exception)
    {
      if (Fx.IsFatal(exception))
      {
        throw;
      }
      message3 = this.CreateReturnMessage(exception, message as IMethodCallMessage);
    }
    return message3;
}

  private IMethodReturnMessage InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
{
  object[] objArray;
  object[] ins = operation.MapSyncInputs(methodCall, out objArray);
  object ret = this.serviceChannel.Call(operation.Action, operation.IsOneWay, operation, ins, objArray);
  object[] returnArgs = operation.MapSyncOutputs(methodCall, objArray, ref ret);
  return this.CreateReturnMessage(ret, returnArgs, methodCall);
  }
}

  先對這幾個對象做個介紹:

  1. interfaceType與proxiedType就是服務契約的Type,methodDataCache存放方法信息的容器
  2. objectWrapper:建立代理對象與服務對象之間映射關系的對象
  3. proxyRuntime:是恆定客戶端運行時(這個是我自己翻譯的,只在此有意義。為了區分它與客戶端運行時ClientRuntime的區別而已)。同ClientRuntime一樣,它也是WCF客戶端體系中很重要的一個對象,它涉及到調用信息的序列化、反序列化、IClientOperationsSelector、IClientMessageInspector、IChannelInitializer等核心對象,稍后會介紹到。
  4. serviceChannel:服務信道。

其中有個 MbrObject類型的 objectWrapper定義。MbrObject的定義如下:

private class MbrObject : MarshalByRefObject
{
  // Fields
  private RealProxy proxy;
  private Type targetType;

  // Methods
  internal MbrObject(RealProxy proxy, Type targetType)
  {
    this.proxy = proxy;
    this.targetType = targetType;
  }

  public override bool Equals(object obj)
  {
    return object.ReferenceEquals(obj, this.proxy.GetTransparentProxy());
  }

  public override int GetHashCode()
  {
    return this.proxy.GetHashCode();
  }

  public override string ToString()
  {
    return this.targetType.ToString();
  }
}

  看看它的構造函數使用的參數可知:通過實際代理對象以及目標代理類型,將實際代理對象映射到了實現契約接口的對象上。

  還有個問題:RealProxy是映射到了最終服務對象上,那是通過什么樣的方式或者說是如何進行映射的呢。?
ServiceChannelProxy對象在WCF體系內部構造ServiceChannelProxy對象時有個構造函數,它建立了代理對象與實際服務對象:this.objectWrapper = new MbrObject(this, proxiedType);這樣就建立了代理對象同服務對象之間的映射

  查看代理信息就發現了他們之間的關系,如下圖:

 


客戶端調用服務端方法最終通過ServiceChannelProxy進行調用。以上只列出了同步調用的方法,在ServiceChannelFactory中還有異步調用的方法,詳情請參見ServiceChannelFactory類。
從InvokeService中可以看出,進行調用的時候,使用了serviceChannel.Call進行調用。同樣,在異步方法中也是通過ServiceChannel對象的BeginInvoke與EndInvoke進行調用。也就是說最終的調用是通過ServiceChannel完成。

 

3、WCF客戶端框架的核心ClientRuntime建立過程


  ClientRuntime是與WCF服務端框架中DispatchRuntime對應的客戶端框架的核心。那么ClientRuntime是如何建立的。?

  ClientRuntime建立的過程比較復雜。下面通過序號標明ClientRuntime建立的過程。

3.1、建立Channel                                                       


  無論在WCF的客戶端還是服務端,我們通常都會選擇一種或者多種通信協議。綁定協議包含許多綁定元素(BindingElementCollection)。以NetTcpBinding來說,它就包含 以下四種綁定元素:
TransactionFlowBindingElement context;
BinaryMessageEncodingBindingElement encoding;
ReliableSessionBindingElement session;
TcpTransportBindingElement transport;

每個綁定元素穿件信道工廠,BindingElementCollection創建的是信道工廠堆棧,信道就是由這些ChannelFactory Stack按照順序依次建立起來的Channel Stack。


3.2、建立過程ChannelFactory                                       


  先看看以下例子:var channkeFactory = new DuplexChannelFactory<ICalculator>(instanceContext, "calculator");這樣就信道工廠就建立。在這個例子中用回調對象對象與EndpointName為參數建立ChannelFactory。

DuplexChannelFactory有很多構造器,
public class DuplexChannelFactory<TChannel> : ChannelFactory<TChannel>
{
  // Methods
  public DuplexChannelFactory(object callbackObject) : base(typeof(TChannel))
  {
    using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
    {
      if (DiagnosticUtility.ShouldUseActivity)
      {
        ServiceModelActivity.Start(activity, SR.GetString("ActivityConstructChannelFactory", new object[] { TraceUtility.CreateSourceString(this) }), ActivityType.Construct);
      }
      if (callbackObject == null)
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackObject");
      }
      this.CheckAndAssignCallbackInstance(callbackObject);
      base.InitializeEndpoint((string) null, null);
    }
  }

public DuplexChannelFactory(object callbackObject, string endpointConfigurationName) : this(callbackObject, endpointConfigurationName, (EndpointAddress) null)
{
}

public DuplexChannelFactory(object callbackObject, string endpointConfigurationName, EndpointAddress remoteAddress) : base(typeof(TChannel))
{
  using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
  {
    if (DiagnosticUtility.ShouldUseActivity)
    {
      ServiceModelActivity.Start(activity, SR.GetString("ActivityConstructChannelFactory", new object[] { TraceUtility.CreateSourceString(this) }), ActivityType.Construct);
    }
    if (callbackObject == null)
    {
      throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackObject");
    }
    if (endpointConfigurationName == null)
    {
      throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
    }
    this.CheckAndAssignCallbackInstance(callbackObject);
    base.InitializeEndpoint(endpointConfigurationName, remoteAddress);
  }
 }
/*
......
*/
}

  

以上列舉了上述例子中使用的構造器。從中可知:在創建ChannelFactory的過程中,通過ChannelFactory<TChannel>的InitializeEndpoint對Endpoint進行了初始化。


3.3、根據建立的Endpoint創建ServiceChannelFactory              

 

internal abstract class ServiceChannelFactory : ChannelFactoryBase
{
  public static ServiceChannelFactory BuildChannelFactory(ServiceEndpoint serviceEndpoint, bool useActiveAutoClose)
  {
    ChannelRequirements requirements;
    BindingParameterCollection parameters;
    if (serviceEndpoint == null)
    {
      throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceEndpoint");
    }
    serviceEndpoint.EnsureInvariants();
    serviceEndpoint.ValidateForClient();
    ChannelRequirements.ComputeContractRequirements(serviceEndpoint.Contract, out requirements);
    ClientRuntime clientRuntime = DispatcherBuilder.BuildProxyBehavior(serviceEndpoint, out parameters);
    Binding binding = serviceEndpoint.Binding;
    Type[] requiredChannels = ChannelRequirements.ComputeRequiredChannels(ref requirements);
    CustomBinding binding2 = new CustomBinding(binding);
    BindingContext context = new BindingContext(binding2, parameters);
    InternalDuplexBindingElement internalDuplexBindingElement = null;
    InternalDuplexBindingElement.AddDuplexFactorySupport(context, ref internalDuplexBindingElement);
    binding2 = new CustomBinding(context.RemainingBindingElements);
    binding2.CopyTimeouts(serviceEndpoint.Binding);
    foreach (Type type in requiredChannels)
    {
      if ((type == typeof(IOutputChannel)) && binding2.CanBuildChannelFactory<IOutputChannel>(parameters))
      {
        return new ServiceChannelFactoryOverOutput(binding2.BuildChannelFactory<IOutputChannel>(parameters), clientRuntime, binding);
      }
      if ((type == typeof(IRequestChannel)) && binding2.CanBuildChannelFactory<IRequestChannel>(parameters))
      {
        return new ServiceChannelFactoryOverRequest(binding2.BuildChannelFactory<IRequestChannel>(parameters), clientRuntime, binding);
      }
      if ((type == typeof(IDuplexChannel)) && binding2.CanBuildChannelFactory<IDuplexChannel>(parameters))
      {
        if (requirements.usesReply && binding.CreateBindingElements().Find<TransportBindingElement>().ManualAddressing)
        {
          throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("CantCreateChannelWithManualAddressing")));
        }
      return new ServiceChannelFactoryOverDuplex(binding2.BuildChannelFactory<IDuplexChannel>(parameters), clientRuntime, binding);
      }
      if ((type == typeof(IOutputSessionChannel)) && binding2.CanBuildChannelFactory<IOutputSessionChannel>(parameters))
      {
        return new ServiceChannelFactoryOverOutputSession(binding2.BuildChannelFactory<IOutputSessionChannel>(parameters), clientRuntime, binding, false);
      }
      if ((type == typeof(IRequestSessionChannel)) && binding2.CanBuildChannelFactory<IRequestSessionChannel>(parameters))
      {
        return new ServiceChannelFactoryOverRequestSession(binding2.BuildChannelFactory<IRequestSessionChannel>(parameters), clientRuntime, binding, false);
      }
      if ((type == typeof(IDuplexSessionChannel)) && binding2.CanBuildChannelFactory<IDuplexSessionChannel>(parameters))
      {
        if (requirements.usesReply && binding.CreateBindingElements().Find<TransportBindingElement>().ManualAddressing)
        {
          throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("CantCreateChannelWithManualAddressing")));
        }
        return new ServiceChannelFactoryOverDuplexSession(binding2.BuildChannelFactory<IDuplexSessionChannel>(parameters), clientRuntime, binding, useActiveAutoClose);
      }
    }
    foreach (Type type2 in requiredChannels)
    {
      if ((type2 == typeof(IOutputChannel)) && binding2.CanBuildChannelFactory<IOutputSessionChannel>(parameters))
      {
        return new ServiceChannelFactoryOverOutputSession(binding2.BuildChannelFactory<IOutputSessionChannel>(parameters), clientRuntime, binding, true);
}
      if ((type2 == typeof(IRequestChannel)) && binding2.CanBuildChannelFactory<IRequestSessionChannel>(parameters))
      {
        return new ServiceChannelFactoryOverRequestSession(binding2.BuildChannelFactory<IRequestSessionChannel>(parameters), clientRuntime, binding, true);
      }
      if (((type2 == typeof(IRequestSessionChannel)) && binding2.CanBuildChannelFactory<IRequestChannel>(parameters)) && (binding2.GetProperty<IContextSessionProvider>(parameters) != null))
      {
        return new ServiceChannelFactoryOverRequest(binding2.BuildChannelFactory<IRequestChannel>(parameters), clientRuntime, binding);
      }
    }
    Dictionary<Type, byte> dictionary = new Dictionary<Type, byte>();
    if (binding2.CanBuildChannelFactory<IOutputChannel>(parameters))
    {
      dictionary.Add(typeof(IOutputChannel), 0);
    }
    if (binding2.CanBuildChannelFactory<IRequestChannel>(parameters))
    {
      dictionary.Add(typeof(IRequestChannel), 0);
    }
    if (binding2.CanBuildChannelFactory<IDuplexChannel>(parameters))
    {
      dictionary.Add(typeof(IDuplexChannel), 0);
    }
    if (binding2.CanBuildChannelFactory<IOutputSessionChannel>(parameters))
    {
      dictionary.Add(typeof(IOutputSessionChannel), 0);
    }
    if (binding2.CanBuildChannelFactory<IRequestSessionChannel>(parameters))
    {
      dictionary.Add(typeof(IRequestSessionChannel), 0);
    }
    if (binding2.CanBuildChannelFactory<IDuplexSessionChannel>(parameters))
    {
      dictionary.Add(typeof(IDuplexSessionChannel), 0);
    }
    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ChannelRequirements.CantCreateChannelException(dictionary.Keys, requiredChannels, binding.Name));
  }
}

 

從ClientRuntime clientRuntime = DispatcherBuilder.BuildProxyBehavior(serviceEndpoint, out parameters);

可知:ClientRuntime 由DispatcherBuilder創建。
注:ServiceChannel由ServiceChannelFactory創建,同ServiceChannelProxy使用。在創建ServiceChannel的過程中對Channel進行初始化。處理函數如下:

public object CreateChannel(Type channelType, EndpointAddress address, Uri via)
{
    if (via == null)
    {
      via = this.ClientRuntime.Via;
      if (via == null)
      {
        via = address.Uri;
      }
    }
    ServiceChannel serviceChannel = this.CreateServiceChannel(address, via);
    serviceChannel.Proxy = CreateProxy(channelType, channelType, MessageDirection.Input, serviceChanne);
    serviceChannel.ClientRuntime.GetRuntime().InitializeChannel((IClientChannel) serviceChannel.Proxy);
    OperationContext current = OperationContext.Current;
    if ((current != null) && (current.InstanceContext != null))
    {
      current.InstanceContext.WmiChannels.Add((IChannel) serviceChannel.Proxy);
      serviceChannel.WmiInstanceContext = current.InstanceContext;
    }
    return serviceChannel.Proxy;
}

 

3.4、DispatcherBuilder創建ClientRuntime

internal class DispatcherBuilder
{
  internal static ClientRuntime BuildProxyBehavior(ServiceEndpoint serviceEndpoint, out BindingParameterCollection parameters)
  {
    parameters = new BindingParameterCollection();
    SecurityContractInformationEndpointBehavior.ClientInstance.AddBindingParameters(serviceEndpoint, parameters);
    AddBindingParameters(serviceEndpoint, parameters);
    ContractDescription contract = serviceEndpoint.Contract;
    ClientRuntime parent = new ClientRuntime(contract.Name, contract.Namespace);
    parent.ContractClientType = contract.ContractType;
    IdentityVerifier property = serviceEndpoint.Binding.GetProperty<IdentityVerifier>(parameters);
    if (property != null)
    {
      parent.IdentityVerifier = property;
    }
    for (int i = 0; i < contract.Operations.Count; i++)
    {
      OperationDescription operation = contract.Operations[i];
      if (!operation.IsServerInitiated())
      {
        BuildProxyOperation(operation, parent);
      }
      else
      {
        BuildDispatchOperation(operation, parent.CallbackDispatchRuntime, null);
      }
    }
    ApplyClientBehavior(serviceEndpoint, parent);
    return parent;
  }
/**/
}

  由Endpoint信息可以獲取到ContractDescription,進而獲取到Operations等等,從而賦值給ClientRuntime對象,完成ClientRuntime對象的建立。

以上還有個問題:DispatchRuntime 是與ClientRuntime相對象的WCF服務端分發運行時,同ClientRuntime一樣,它是WCF服務端核心對象。在客戶端怎么會后服務端的的分發運行時呢。?原因很簡單,在WCF數據包模式以及Request-Reply模式下,DispatchRuntime是不需要的,但是在雙工模式時,Server端與Client端已經不明確,
Server與Client互發消息,即是服務端也是客戶端。所以不僅在ClientRuntime中存在DispatchRuntime,在DispatchRuntime同樣存在ClientRuntime。

ClientRuntime作為客戶端框架的核心,它決定着消息的格式化(IClientMessageFormatter)、客戶端操作選擇器(IClientOperationSelector)、客戶端消息檢查器(IClientMessageInspectors)等等。

4、ImmutableClientRuntime對象的作用                   


  客戶端對服務端的操作是通過TransparentProxy到RealProxy,也就是ServiceChannelProxy對象中。上面提到過,客戶端調用服務的實際代理ServiceChannelProxy對象有一個名稱為proxyRuntime的字段,類型就是ImmutableClientRuntime。
  ImmutableClientRuntime對象依賴於ClientRuntime,這點可以從ImmutableClientRuntime的構造函數中就可以看出。

internal ImmutableClientRuntime(ClientRuntime behavior)
{
    this.channelInitializers = EmptyArray<IChannelInitializer>.ToArray(behavior.ChannelInitializers);
    this.interactiveChannelInitializers = EmptyArray<IInteractiveChannelInitializer>.ToArray(behavior.InteractiveChannelInitializers);
    this.messageInspectors = EmptyArray<IClientMessageInspector>.ToArray(behavior.MessageInspectors);
    this.operationSelector = behavior.OperationSelector;
    this.useSynchronizationContext = behavior.UseSynchronizationContext;
    this.validateMustUnderstand = behavior.ValidateMustUnderstand;
    this.unhandled = new ProxyOperationRuntime(behavior.UnhandledClientOperation, this);
    this.addTransactionFlowProperties = behavior.AddTransactionFlowProperties;
    this.operations = new Dictionary<string, ProxyOperationRuntime>();
    for (int i = 0; i < behavior.Operations.Count; i++)
    {
      ClientOperation operation = behavior.Operations[i];
      ProxyOperationRuntime runtime = new ProxyOperationRuntime(operation, this);
      this.operations.Add(operation.Name, runtime);
    }
    this.correlationCount = this.messageInspectors.Length + behavior.MaxParameterInspectors;
}

 

ImmutableClientRuntime在RealProxy中起着至關重要的作用。如下圖是RealProxy的信息:

由上圖可知:

1、operations為Dictionary<string, ProxyOperationRuntime>類型,其中key為契約接口中標識位OperationContractAttribute的方法名,Value為ProxyOperationRuntime。

2、MessageInspector是實現了IClientMessageInspector接口的Microsoft.VisualStudio.Diagnostics.ServiceModelSink.StubClientEventSink類型。
3、客戶端OperationSelector是MethodInfoOperationSelector類型的,MethodInfoOperationSelector實現了IClientOperationSelector接口。
4、客戶端消息檢查器IClientMessageInspectors為Microsoft.VisualStudio.Diagnostics.ServiceModelSink.StubClientEventSink類型。


5、客戶端操作選擇器MethodInfoOperationSelector。             


通過工具看看MethodInfoOperationSelector的定義如下:

internal class MethodInfoOperationSelector : IClientOperationSelector
{
  // Fields
  private Dictionary<object, string> operationMap = new Dictionary<object, string>();

  // Methods
  internal MethodInfoOperationSelector(ContractDescription description, MessageDirection directionThatRequiresClientOpSelection)
  {
    for (int i = 0; i < description.Operations.Count; i++)
    {
      OperationDescription description2 = description.Operations[i];
      if (description2.Messages[0].Direction == directionThatRequiresClientOpSelection)
      {
        if ((description2.SyncMethod != null) && !this.operationMap.ContainsKey(description2.SyncMethod.MethodHandle))
        {
          this.operationMap.Add(description2.SyncMethod.MethodHandle, description2.Name);
        }
        if ((description2.BeginMethod != null) && !this.operationMap.ContainsKey(description2.BeginMethod.MethodHandle))
        {
        this.operationMap.Add(description2.BeginMethod.MethodHandle, description2.Name);
        this.operationMap.Add(description2.EndMethod.MethodHandle, description2.Name);
        }
      }
    }
  }

  public string SelectOperation(MethodBase method, object[] parameters)
  {
    if (this.operationMap.ContainsKey(method.MethodHandle))
    {
      return this.operationMap[method.MethodHandle];
    }
    return null;
  }

  // Properties
  public bool AreParametersRequiredForSelection
  {
    get
    {
      return false;
    }
  }
}

  

operationMap為存放方法的字段,key方法句柄信息,Value為方法名。
MethodInfoOperationSelector通過SelectOperation根據方法句柄值獲取方法名,然后通過方法名,獲取ProxyOperationRuntime對象。


查看OperationSelector的operationMap中Add方法的句柄值如下圖所示:


通過控制台輸出契約接口中方法的句柄值如下:


-6、ProxyOperationRuntime的作用                                     

了解它的作用,還是從源碼開始。

internal class ProxyOperationRuntime
{
    // Fields
    private string action;
    private MethodInfo beginMethod;
    private bool deserializeReply;
    internal static readonly object[] EmptyArray = new object[0];
    private ParameterInfo[] endOutParams;
    private readonly IClientFaultFormatter faultFormatter;
    private readonly IClientMessageFormatter formatter;
    private ParameterInfo[] inParams;
    private readonly bool isInitiating;
    private readonly bool isOneWay;
    private readonly bool isTerminating;
    private readonly string name;
    internal static readonly ParameterInfo[] NoParams = new ParameterInfo[0];
    private ParameterInfo[] outParams;
    private readonly IParameterInspector[] parameterInspectors;
    private readonly ImmutableClientRuntime parent;
    private string replyAction;
    private ParameterInfo returnParam;
    private bool serializeRequest;
    private MethodInfo syncMethod;

    // Methods
    internal ProxyOperationRuntime(ClientOperation operation, ImmutableClientRuntime parent)
    {
      if (operation == null)
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation");
      }
      if (parent == null)
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent");
      }
      this.parent = parent;
      this.formatter = operation.Formatter;
      this.isInitiating = operation.IsInitiating;
      this.isOneWay = operation.IsOneWay;
      this.isTerminating = operation.IsTerminating;
      this.name = operation.Name;
      this.parameterInspectors = EmptyArray<IParameterInspector>.ToArray(operation.ParameterInspectors);
      this.faultFormatter = operation.FaultFormatter;
      this.serializeRequest = operation.SerializeRequest;
      this.deserializeReply = operation.DeserializeReply;
      this.action = operation.Action;
      this.replyAction = operation.ReplyAction;
      this.beginMethod = operation.BeginMethod;
      this.syncMethod = operation.SyncMethod;
      if (this.beginMethod != null)
      {
        this.inParams = ServiceReflector.GetInputParameters(this.beginMethod, true);
        if (this.syncMethod != null)
        {
          this.outParams = ServiceReflector.GetOutputParameters(this.syncMethod, false);
        }
        else
        {
          this.outParams = NoParams;
        }
        this.endOutParams = ServiceReflector.GetOutputParameters(operation.EndMethod, true);
        this.returnParam = operation.EndMethod.ReturnParameter;
      }
      else if (this.syncMethod != null)
      {
        this.inParams = ServiceReflector.GetInputParameters(this.syncMethod, false);
        this.outParams = ServiceReflector.GetOutputParameters(this.syncMethod, false);
        this.returnParam = this.syncMethod.ReturnParameter;
      }
      if ((this.formatter == null) && (this.serializeRequest || this.deserializeReply))
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("ClientRuntimeRequiresFormatter0", new object[] { this.name })));
      }
    }
}
  internal void AfterReply(ref ProxyRpc rpc)
  {
    if (!this.isOneWay)
    {
      Message reply = rpc.Reply;
      if (this.deserializeReply)
      {
        rpc.ReturnValue = this.formatter.DeserializeReply(reply, rpc.OutputParameters);
      }
      else
      {
        rpc.ReturnValue = reply;
      }
      int parameterInspectorCorrelationOffset = this.parent.ParameterInspectorCorrelationOffset;
      try
      {
        for (int i = this.parameterInspectors.Length - 1; i >= 0; i--)
        {
          this.parameterInspectors[i].AfterCall(this.name, rpc.OutputParameters, rpc.ReturnValue, rpc.Correlation[parameterInspectorCorrelationOffset + i]);
          if (TD.ClientParameterInspectorAfterCallInvokedIsEnabled())
          {
            TD.ClientParameterInspectorAfterCallInvoked(this.parameterInspectors[i].GetType().FullName);
          }
        }
      }
      catch (Exception exception)
      {
        if (Fx.IsFatal(exception))
        {
          throw;
        }
        if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(exception))
        {
          throw;
        }
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
      }
      if (this.parent.ValidateMustUnderstand)
      {
        Collection<MessageHeaderInfo> headersNotUnderstood = reply.Headers.GetHeadersNotUnderstood();
        if ((headersNotUnderstood != null) && (headersNotUnderstood.Count > 0))
        {
          throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString("SFxHeaderNotUnderstood", new object[] { headersNotUnderstood[0].Name, headersNotUnderstood[0].Namespace })));
        }
      }
    }
  }

  internal void BeforeRequest(ref ProxyRpc rpc)
  {
    int parameterInspectorCorrelationOffset = this.parent.ParameterInspectorCorrelationOffset;
    try
    {
      for (int i = 0; i < this.parameterInspectors.Length; i++)
      {
        rpc.Correlation[parameterInspectorCorrelationOffset + i] = this.parameterInspectors[i].BeforeCall(this.name, rpc.InputParameters);
        if (TD.ClientParameterInspectorBeforeCallInvokedIsEnabled())
        {
          TD.ClientParameterInspectorBeforeCallInvoked(this.parameterInspectors[i].GetType().FullName);
        }
      }
    }
    catch (Exception exception)
    {
      if (Fx.IsFatal(exception))
      {
        throw;
      }
      if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(exception))
      {
        throw;
      }
      throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
    }
    if (this.serializeRequest)
    {
      rpc.Request = this.formatter.SerializeRequest(rpc.MessageVersion, rpc.InputParameters);
    }
    else
    {
      if (rpc.InputParameters[0] == null)
      {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxProxyRuntimeMessageCannotBeNull", new object[] { this.name })));
      }
      rpc.Request = (Message) rpc.InputParameters[0];
      if (!IsValidAction(rpc.Request, this.Action))
      {
        object[] args = new object[] { this.Name, rpc.Request.Headers.Action ?? "{NULL}", this.Action };
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxInvalidRequestAction", args)));
      }
    }
  }

由上圖可以得知:
1、對返回值進行序列化時,使用的類型是PrimitiveOperationFormatter,它同時實現了客戶端序列化接口IClientMessageFormatter與服務端序列化IDispatchMessageFormatter接口
2、錯誤契約使用的序列化器是DataContractSerializerFaultFormatter。
3、對服務調用參數的序列化以及參數檢查,對返回消息進行反序列化及參數檢查通過ProxyOperationRuntime對象完成。

  對實際代理ServiceChannelProxy對象中的ImmutableClientRuntime類型的字段realProxy以及realProxy的MessageInspector、operations添加監視,查看其信息如下:

圖1:realProxy監視信息

圖2:realProxy的消息檢查器MessageInspector監視信息

圖3:realProxy的operations監視信息

由以上3個圖中顯示的信息可知:

1、operations為Dictionary<string, ProxyOperationRuntime>類型,其中key為契約接口中標識位OperationContractAttribute的方法名,Value為ProxyOperationRuntime。

2、MessageInspector是實現了IClientMessageInspector接口的客戶端消息檢查器Microsoft.VisualStudio.Diagnostics.ServiceModelSink.StubClientEventSink類型。
3、客戶端OperationSelector是MethodInfoOperationSelector類型的,MethodInfoOperationSelector實現了IClientOperationSelector接口。

參考:http://www.cnblogs.com/artech/tag/WCF/

   《WCF揭秘》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM