zoukankan      html  css  js  c++  java
  • 深入理解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揭秘》

  • 相关阅读:
    git命令log与reflog的比较
    git基础仓库提交到新仓库,保存老仓库历史,并同步老仓库跟新到新仓库中
    classpath*与classpath
    fastjson将对象和json互转,@JSONField的使用及不生效
    feign接口自动生成工具
    IIS .Net Core 413错误和Request body too large解决办法
    thinphp 上传文件到七牛
    php 整合微信、支付宝扫码付款
    Jenkins:整合SonarQube8
    Jenkins:流水线打包运行boot项目
  • 原文地址:https://www.cnblogs.com/tyb1222/p/2426973.html
Copyright © 2011-2022 走看看