原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构

细算起来,已经有好几个月没有真正的写过文章了。近半年以来,一直忙于我的第一本WCF专著《WCF技术剖析》的写作,一直无暇管理自己的Blog。到目前为止《WCF技术剖析(卷1)》的写作暂告一段落,初步预计于下个月由武汉博文视点出版。在《WCF技术剖析》写作期间,对WCF又有了新的感悟,为此以书名开始本人的第三个WCF系列。本系列的目的在于对《WCF技术剖析》的补充,会对书中的一些内容进行展开讲述,同时会囊括很多由于篇幅的原因忍痛割弃的内容。

本系列的第一篇,我将会对WCF的基本架构作一个大致的讲解。不过,一改传统对WCF的工作流程进行平铺直叙,我将另辟蹊径,借助于我们熟悉的ASP.NET作为请求处理平台,通过一个简单的托管程序模拟整个WCF客户端和服务端的架构。Source Code下载:Artech.WcfFrameworkSimulator.zip

WCF框架处理流程和涉及的组件

我们的模拟程序将你搭建一个迷你版的WCF框架,为了展示WCF整个处理流程中使用到一些特殊组件。我们首先来简单介绍一下对于一个简单的WCF服务调用,WCF的客户端和服务端框架的处理流程,和该流程的每一个阶段都使用那些重要组件。

下面的列表列出了WCF服务端框架对于处理一个简单的WCF服务调用请求所提供的功能,以及相应的功能承载的组件:

  • 请求消息的接收和回复消息的发送:服务端在传输层监听与接收来自客户的请求,并将经过编码后的回复消息通过传输层发送到客户端
  • 请求消息的解码和回复消息的编码:将接收到的字节数组通过解码生成请求消息对象,并将回复消息通过编程转化成字节组。消息的编码和解码通过MessageEncoder完成,而MessageEncoderFactory负责创建该对象
  • 请求消息的反序列化和回复消息的序列化:对请求消息进行反序列化,为服务操作的执行生成相应的输入参数,以及将服务操作执行的结果(返回值或者ref/out参数)序列化,并生成回复消息。序列化和反序列化通过DispatchMessageFormatter完成
  • 服务对象的创建:创建或者激活服务对象实例,InstanceProvider用于服务对象的创建或获取
  • 服务操作的执行:调用创建的服务对象的操作方法,并传入经过反序列化生成的输入参数。OperationInvoker完成对服务操作的最终执行

较之服务端的流程,客户端的流程显得相对简单,仅仅包含以下三个必需的阶段:

  • 请求消息的序列化和回复消息的反序列化:生成请求消息并将输入参数序列化到请求消息中,以及对回复消息进行反序列化,转化成方法调用的返回值或者ref/out参数。序列化和反序列化通过ClienthMessageFormatter完成
  • 请求消息的编码和回复消息的解码:对请求消息进行编码生成字节数组供传输层发送,以及将传输层接收到的字节数组解码生成恢复消息。消息的编码和解码通过MessageEncoder完成,而MessageEncoderFactory负责创建该对象
  • 请求消息的发送和回复消息的接收:在传输层将经过编码的请求消息发送到服务端,以及将接收来自服务端的恢复消息

图1 精简版WCF客户端与服务端组件

图1反映了进行服务调用的必要步骤和使用到的相关WCF组件。在本案例演示中,我们需要做的就是手工创建这些组件,并通过我们自己的代码利用它们搭建一个简易版的WCF框架。如果读者能够对本案例的实现有一个清晰的理解,相信对于整个WCF的框架就不会感到陌生了。

图2显示了本案例解决方案的基本结构,总共分三个项目。Contracts用于定义服务契约,被服务端和客户端引用。客户端通过一个Console应用模拟,而服务端则通过一个ASP.NET Website实现。

图2 WCF框架模拟案例应用结构

步骤一、通过服务契约类型创建相关组件

WCF在整个服务调用生命周期的不同阶段,会使用到不同的组件。我们通过一个方法将服务端和客户端所需的所有组件都创建出来,为此,我们在Contracts项目中添加了一个Utility类型,在Create<T>方法中创建所有的组件并通过输出参数的形式返回,泛型类型T表示的是服务契约类型。在该方法中,输出参数encoderFactory被服务端和客户端用于消息的编码和解码,clientFormatters和dispatchFormatters以字典的形式包含了基于服务操作的IClientMessageFormatter和IDispatchMessageFormatter,其中clientFormatters和dispatchFormatters的Key分别为操作名称和操作对应的Action。同样通过字典形式返回的operationInvokers和methods用于在服务端执行相应的操作方法,Key同样为操作对应的Action。

   1: public static class Utility

   2: {

   3:     public static void Create<T>(out MessageEncoderFactory encoderFactory,

   4:         out IDictionary<string, IClientMessageFormatter> clientFormatters,

   5:         out IDictionary<string, IDispatchMessageFormatter> dispatchFormatters,

   6:         out IDictionary<string, IOperationInvoker> operationInvokers,

   7:         out IDictionary<string, MethodInfo> methods)

   8:     {

   9:         //省略实现

  10:     }

  11: }

具体的实现如下,由于在WCF框架中使用的MessageEncoderFactory(TextMessageEncoderFactory)、MessageFormatter(DataContractSerializerOperationFormatter)和OperationInvoker(SyncMethodInvoker)都是一些内部类型,所以只能通过反射的方式创建它们。而操作名称和Action也主要通过反射的原理解析应用在服务方法上的OperationContractAttribute得到。
   1: public static void Create<T>(out MessageEncoderFactory encoderFactory,

   2:     out IDictionary<string, IClientMessageFormatter> clientFormatters,

   3:     out IDictionary<string, IDispatchMessageFormatter> dispatchFormatters,

   4:     out IDictionary<string, IOperationInvoker> operationInvokers,

   5:     out IDictionary<string, MethodInfo> methods)

   6: {

   7:     //确保类型T是应用了ServiceContractAttribute的服务契约

   8:     object[] attributes = typeof(T).GetCustomAttributes(typeof(ServiceContractAttribute), false);

   9:     if (attributes.Length == 0)

  10:     {

  11:         throw new InvalidOperationException(string.Format("The type \"{0}\" is not a ServiceContract!", typeof(T).AssemblyQualifiedName));

  12:     } 

  13:  

  14:     //创建字典保存IClientMessageFormatter、IDispatchMessageFormatter、IOperationInvoker和MethodInfo

  15:     clientFormatters = new Dictionary<string, IClientMessageFormatter>();

  16:     dispatchFormatters = new Dictionary<string, IDispatchMessageFormatter>();

  17:     operationInvokers = new Dictionary<string, IOperationInvoker>();

  18:     methods = new Dictionary<string, MethodInfo>(); 

  19:  

  20:     //MessageEncoderFactory

  21:     string encoderFactoryType = "System.ServiceModel.Channels.TextMessageEncoderFactory,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

  22:     encoderFactory = (MessageEncoderFactory)Activator.CreateInstance(Type.GetType(encoderFactoryType), MessageVersion.Default, Encoding.UTF8, int.MaxValue, int.MaxValue, new XmlDictionaryReaderQuotas()); 

  23:  

  24: //得到OperationDecription列表

  25: string defaultNamespace = "http://tempuri.org/";

  26:     ServiceContractAttribute serviceAttribute = (ServiceContractAttribute)attributes[0];

  27:     string serviceNamepace = string.IsNullOrEmpty(serviceAttribute.Namespace) ? defaultNamespace : serviceAttribute.Namespace;

  28:     string serviceName = string.IsNullOrEmpty(serviceAttribute.Name) ? typeof(T).Name : serviceAttribute.Name;

  29:     var operations = ContractDescription.GetContract(typeof(T)).Operations; 

  30:  

  31:     //得到具体的IClientMessageFormatter、IDispatchMessageFormatter和IOperationInvoker的具体类型

  32:     //IClientMessageFormatter+IDispatchMessageFormatter:DataContractSerializerOperationFormatter

  33:     //IOperationInvoker:SyncMethodInvoker

  34:     string formatterTypeName = "System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

  35:     Type formatterType = Type.GetType(formatterTypeName);

  36:     ConstructorInfo formatterConstructor = formatterType.GetConstructor(new Type[] { typeof(OperationDescription), typeof(DataContractFormatAttribute), typeof(DataContractSerializerOperationBehavior) });

  37:     string operationInvokerTypeName = "System.ServiceModel.Dispatcher.SyncMethodInvoker,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

  38:     Type operationInvokerType = Type.GetType(operationInvokerTypeName); 

  39:  

  40:     foreach (MethodInfo method in typeof(T).GetMethods())

  41:     {

  42:         attributes = method.GetCustomAttributes(typeof(OperationContractAttribute), true);

  43:         if (attributes.Length > 0)

  44:         {

  45:             OperationContractAttribute operationAttribute = (OperationContractAttribute)attributes[0];

  46:             string operationName = string.IsNullOrEmpty(operationAttribute.Name) ? method.Name : operationAttribute.Name;

  47:             //通过OperationContractAttribute得到Action

  48:             string action;

  49:             if (string.IsNullOrEmpty(operationAttribute.Action))

  50:             {

  51:                 action = string.Format("{0}{1}/{2}", serviceNamepace, serviceName, operationName);

  52:             }

  53:             else

  54:             {

  55:                 action = operationAttribute.Action;

  56:             } 

  57:  

  58:             OperationDescription operation = operations.Where(op => op.Name == operationName).ToArray<OperationDescription>()[0];

  59:             //通过反射创建DataContractSerializerOperationFormatter对象

  60:             object formatter = formatterConstructor.Invoke(new object[] { operation, new DataContractFormatAttribute(), null });

  61:             clientFormatters.Add(operationName, formatter as IClientMessageFormatter);

  62:             dispatchFormatters.Add(action, formatter as IDispatchMessageFormatter); 

  63:  

  64:             //通过反射创建SyncMethodInvoker对象

  65:             IOperationInvoker operationInvoker = (IOperationInvoker)Activator.CreateInstance(operationInvokerType, method);

  66:             operationInvokers.Add(action, operationInvoker);

  67:             methods.Add(action, method);

  68:         }

  69: }

步骤二、创建服务契约和实现服务

接下来为本案例创建一个服务契约和实现该契约。服务契约定义在Contracts项目,具体的服务实现在模拟服务端的ASP.NET Web站点中。简单起见,依然沿用计算服务的例子。

   1: [ServiceContract(Namespace = "http://www.artech.com/")]

   2:     public interface ICalculator

   3:     {

   4:         [OperationContract]

   5:         double Add(double x, double y);

   6:     }

 

   1: public class CalculatorService : ICalculator

   2: {

   3:     public double Add(double x, double y)

   4:     {

   5:         return x + y;

   6:     }

   7: }

步骤三、实现服务端对服务调用请求的处理

我们通过一个ASP.NET的Web Page来模拟WCF服务端对服务请求的处理,下面的Calculator类型相关的代码实际上就是Calculator.aspx的后台代码(Code Behind)。整个处理流程不算复杂。在构造函数中,调用Utility的Create<ICalculator>方法,将所需的组件进行初始化,而具体的服务调用请求处理的逻辑在直接写在Web Page的Load事件中。

首先,通过MessageCoderFactory创建MessageEncoder对接收到的以HttpRequest形式体现的服务调用请求进行解码,并生成请求消息。通过请求消息得到当前服务操作的Action属性后,在初始化过程中得到的基于服务契约所有MethodInfo列表中,根据该Action得到当前操作对应的MethodInfo对象。借助于MethodInfo对象得到操作方法的输入参数和输出参数数量后,创建两个对象数组,分别用于保存通过DispatchMessageFormatter对象对于请求消息进行反序列化得到的输入参数,和通过OperationInvoker执行操作方法得到的输出参数。在OperationInvoker执行操作方法之前,通过反射的方式直接创建服务对象,这一步在真正的WCF框架中是通过InstanceProvider实现的。

通过OperationInvoker执行操作方法的结果有两种形式:返回值和输出参数(包括引用参数)。它们通过被传入DispatchMessageFormatter被序列化并生成回复消息对象。回复消息通过MessageCoderFactory创建MessageEncoder进行编码后通过HttpResponse返回。

   1:  

   2: ublic partial class Calculator : System.Web.UI.Page

   3:  

   4:    private static MessageVersion messageversion = MessageVersion.Default;

   5:    private static MessageEncoderFactory encoderFactory;

   6:    private static IDictionary<string, IDispatchMessageFormatter> dispatchFormatters;

   7:    private static IDictionary<string, IOperationInvoker> operationInvokers;

   8:    private static IDictionary<string, MethodInfo> methods;

   9:  

  10:    protected Calculator()

  11:    {

  12:        IDictionary<string, IClientMessageFormatter> clientFormatters;

  13:        Utility.Create<ICalculator>(out encoderFactory, out clientFormatters, out dispatchFormatters, out operationInvokers, out methods);

  14:    }

  15:  

  16:    protected void Page_Load(object sender, EventArgs e)

  17:    {

  18:        //对HttpPRequest进行解码生成请求消息对象

  19:        Message request = encoderFactory.Encoder.ReadMessage(this.Request.InputStream, int.MaxValue, "application/soap+xml; charset=utf-8");

  20:  

  21:        //通过请求消息得到代表服务操作的Action

  22:        string action = request.Headers.Action;

  23:  

  24:        //通过Action从MethodInfo字典中获取服务操作对应的MethodInfo对象

  25:        MethodInfo method = methods[action];

  26:  

  27:        //得到输出参数的数量

  28:        int outArgsCount = 0;

  29:        foreach (var parameter in method.GetParameters())

  30:        {

  31:            if (parameter.IsOut)

  32:            {

  33:                outArgsCount++;

  34:            }

  35:        }

  36:  

  37:        //创建数组容器,用于保存请求消息反序列后生成的输入参数对象

  38:        int inputArgsCount = method.GetParameters().Length - outArgsCount;

  39:        object[] parameters = new object[inputArgsCount];

  40:        dispatchFormatters[action].DeserializeRequest(request, parameters);

  41:  

  42:        List<object> inputArgs = new List<object>();

  43:        object[] outArgs = new object[outArgsCount];

  44:        //创建服务对象,在WCF中服务对象通过InstanceProvider创建

  45:        object serviceInstance = Activator.CreateInstance(typeof(CalculatorService));

  46:        //执行服务操作

  47:        object result = operationInvokers[action].Invoke(serviceInstance, parameters, out outArgs);

  48:        //将操作执行的结果(返回值或者输出参数)序列化生成回复消息

  49:        Message reply = dispatchFormatters[action].SerializeReply(messageversion, outArgs, result);

  50:        this.Response.ClearContent();

  51:        this.Response.ContentEncoding = Encoding.UTF8;

  52:        this.Response.ContentType = "application/soap+xml; charset=utf-8";

  53:        //对回复消息进行编码,并将编码后的消息通过HttpResponse返回

  54:        encoderFactory.Encoder.WriteMessage(reply, this.Response.OutputStream);

  55:        this.Response.Flush();

  56:    }

  57:  

步骤四、实现客户端对服务调用请求的处理

由于在客户端对服务请求的处理是通过一个RealProxy(ServiceChannelFactory)实现的,为了真实模拟WCF处理框架,在这里通过一个自定义RealProxy来实现客户端相关的服务调用请求的处理。下面代码中定义的ServiceRealProxy<IContract>就是这样一个自定义RealProxy。

用于处理服务调用请求的相关组件对象,比如MessageEncoderFactory和IClientMessageFormatter字典,以及所需的属性,比如消息的版本和服务的目的地址,通过构造函数指定。而具体的请求处理实现在重写的Invoke方法之中。首先通过解析应用在当前方法的上面的OperationContractAttribute得到服务操作的名称,以此为Key从IClientMessageFormatter字典中得到当前服务操作对应的IClientMessageFormatter对象。当前操作方法调用的输入参数通过IClientMessageFormatter对象进行序列化后生成请求消息。为请求消息添加必要的寻址报头后,通过MessageEncoderFactory创建的MessageEncoder对请求消息进行编码。经过编码的消息以HttpRequest的形式发送到服务端,从而完成了服务调用请求的发送。

服务调用的结果通过HttpResponse的形式返回后,先通过MessageEncoder对其解码,并生成回复消息。回复消息通过IClientMessageFormatter进行反序列化后,在消息中以XML InfoSet实行体现的结果被转化成具体的对象,这些对象被最终影射为方法调用的返回值和输出参数(包含引用参数)。

   1: namespace Artech.WcfFrameworkSimulator.Client

   2: {

   3:     public class ServiceRealProxy<IContract> : RealProxy

   4:     {

   5:         private Uri _remoteAddress;

   6:         private IDictionary<string, IClientMessageFormatter> _messageFormatters;

   7:         private MessageVersion _messageVersion = MessageVersion.Default;

   8:         private MessageEncoderFactory _messageEncoderFactory;

   9:  

  10:         public ServiceRealProxy(MessageVersion messageVersion, Uri address, IDictionary<string, IClientMessageFormatter> messageFormaters, MessageEncoderFactory messageEncoderFactory)

  11:             : base(typeof(IContract))

  12:         {

  13:             object[] attribute = typeof(IContract).GetCustomAttributes(typeof(ServiceContractAttribute), false);

  14:             if (attribute.Length == 0)

  15:             {

  16:                 throw new InvalidOperationException(string.Format("The type \"{0}\" is not a ServiceContract!", typeof(IContract).AssemblyQualifiedName));

  17:             }

  18:             this._messageVersion = messageVersion;

  19:             this._remoteAddress = address;

  20:             this._messageFormatters = messageFormaters;

  21:             this._messageEncoderFactory = messageEncoderFactory;

  22:         }

  23:  

  24:         public override IMessage Invoke(IMessage msg)

  25:         {

  26:             IMethodCallMessage methodCall = (IMethodCallMessage)msg;

  27:  

  28:             //Get Operation name.

  29:             object[] attributes = methodCall.MethodBase.GetCustomAttributes(typeof(OperationContractAttribute), true);

  30:             if (attributes.Length == 0)

  31:             {

  32:                 throw new InvalidOperationException(string.Format("The method \"{0}\" is not a valid OperationContract.", methodCall.MethodName));

  33:             }

  34:             OperationContractAttribute attribute = (OperationContractAttribute)attributes[0];

  35:             string operationName = string.IsNullOrEmpty(attribute.Name) ? methodCall.MethodName : attribute.Name;

  36:  

  37:             //序列化请求消息

  38:             Message requestMessage = this._messageFormatters[operationName].SerializeRequest(this._messageVersion, methodCall.InArgs);

  39:  

  40:             //添加必要的WS-Address报头

  41:             EndpointAddress address = new EndpointAddress(this._remoteAddress);

  42:             requestMessage.Headers.MessageId = new UniqueId(Guid.NewGuid());

  43:             requestMessage.Headers.ReplyTo = new EndpointAddress("http://www.w3.org/2005/08/addressing/anonymous");

  44:             address.ApplyTo(requestMessage);

  45:  

  46:             //对请求消息进行编码,并将编码生成的字节发送通过HttpWebRequest向服务端发送

  47:             HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(this._remoteAddress);

  48:             webRequest.Method = "Post";

  49:             webRequest.KeepAlive = true;

  50:             webRequest.ContentType = "application/soap+xml; charset=utf-8";

  51:             ArraySegment<byte> bytes = this._messageEncoderFactory.Encoder.WriteMessage(requestMessage, int.MaxValue, BufferManager.CreateBufferManager(long.MaxValue, int.MaxValue));

  52:             webRequest.ContentLength = bytes.Array.Length;

  53:             webRequest.GetRequestStream().Write(bytes.Array, 0, bytes.Array.Length);

  54:             webRequest.GetRequestStream().Close();

  55:             WebResponse webResponse = webRequest.GetResponse();

  56:  

  57:             //对HttpResponse进行解码生成回复消息.

  58:             Message responseMessage = this._messageEncoderFactory.Encoder.ReadMessage(webResponse.GetResponseStream(), int.MaxValue);

  59:  

  60:             //回复消息进行反列化生成相应的对象,并映射为方法调用的返回值或者ref/out参数

  61:             object[] allArgs = (object[])Array.CreateInstance(typeof(object), methodCall.ArgCount);

  62:             Array.Copy(methodCall.Args, allArgs, methodCall.ArgCount);

  63:             object[] refOutParameters = new object[GetRefOutParameterCount(methodCall.MethodBase)];

  64:             object returnValue = this._messageFormatters[operationName].DeserializeReply(responseMessage, refOutParameters);

  65:             MapRefOutParameter(methodCall.MethodBase, allArgs, refOutParameters);

  66:  

  67:             //通过ReturnMessage的形式将返回值和ref/out参数返回

  68:             return new ReturnMessage(returnValue, allArgs, allArgs.Length, methodCall.LogicalCallContext, methodCall);

  69:         }

  70:  

  71:         private int GetRefOutParameterCount(MethodBase method)

  72:         {

  73:             int count = 0;

  74:             foreach (ParameterInfo parameter in method.GetParameters())

  75:             {

  76:                 if (parameter.IsOut || parameter.ParameterType.IsByRef)

  77:                 {

  78:                     count++;

  79:                 }

  80:             }

  81:             return count;

  82:         }

  83:  

  84:         private void MapRefOutParameter(MethodBase method, object[] allArgs, object[] refOutArgs)

  85:         {

  86:             List<int> refOutParamPositionsList = new List<int>();

  87:             foreach (ParameterInfo parameter in method.GetParameters())

  88:             {

  89:                 if (parameter.IsOut || parameter.ParameterType.IsByRef)

  90:                 {

  91:                     refOutParamPositionsList.Add(parameter.Position);

  92:                 }

  93:             }

  94:             int[] refOutParamPositionArray = refOutParamPositionsList.ToArray();

  95:             for (int i = 0; i < refOutArgs.Length; i++)

  96:             {

  97:                 allArgs[refOutParamPositionArray[i]] = refOutArgs[i];

  98:             }

  99:         }

 100:     }

 101: }

在真正的WCF客户端框架下,客户端通过ChannelFactory<T>创建服务代理对象进行服务的调用,在这里我们也创建一个完成相似功能的工厂类型: SerivceProxyFactory<T>,泛型类型T代表服务契约类型。

用于创建服务代理的Create方法很简单:先通过Utility.Create<T>方法创建客户端进行服务调用必须的相关组件对象,通过这些对象连同该方法的参数(消息版本和服务目的地址)创建ServiceRealProxy<T>对象,最终返回的是该RealProxy的TransparentProxy。

   1: namespace Artech.WcfFrameworkSimulator.Client

   2: {

   3:     public static class SerivceProxyFactory<T>

   4:     {

   5:         public static T Create(MessageVersion messageVersion, Uri remoteAddress)

   6:         {

   7:             MessageEncoderFactory encoderFactory; 

   8:             IDictionary<string, IClientMessageFormatter> clientFormatters; 

   9:             IDictionary<string, IDispatchMessageFormatter> dispatchFormatters; 

  10:             IDictionary<string, IOperationInvoker> operationInvokers; 

  11:             IDictionary<string, MethodInfo> methods; 

  12:             Utility.Create<T>(out encoderFactory, out clientFormatters, out dispatchFormatters, out operationInvokers, out methods); 

  13:             ServiceRealProxy<T> realProxy = new ServiceRealProxy<T>(messageVersion, remoteAddress, clientFormatters, encoderFactory); 

  14:             return (T)realProxy.GetTransparentProxy();

  15:         }

  16:     }

  17: }

那么在最终的客户端代码中就可以借助SerivceProxyFactory<T>创建服务代理进行服务调用了,而这里服务的目标地址实际上是上面用于模拟WCF服务端框架的.aspx Web Page的地址。

   1: namespace Artech.WcfFrameworkSimulator.Client

   2: {

   3:     class Program

   4:     {

   5:         static void Main(string[] args)

   6:         {

   7:             ICalculator calculator = SerivceProxyFactory<ICalculator>.Create(MessageVersion.Default, new Uri("http://localhost/Artech.WcfFrameworkSimulator/Calculator.aspx")); 

   8:             double result = calculator.Add(1, 2); 

   9:             Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, result);

  10:         }

  11:     }

  12: }

执行结果:

   1: x + y = 3 when x = 1 and y = 2

作者:Artech

出处:http://artech.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构的更多相关文章

  1. WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务

    原文:WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务 在<基于IIS的WCF服务寄宿(Hosting)实现揭秘>中,我们谈到在采用基于IIS(或者 ...

  2. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

  3. WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

    原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...

  4. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

  5. WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成

    原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...

  6. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  7. WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效

    原文:WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效 本篇文章来源于几天前一个朋友向我咨询的问题.问题是这样的,他说他采用ASP.NET应用 ...

  8. WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]

    原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术 ...

  9. WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]

    原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇] 在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道:当遇到某些异常,我们需要强行中止(Abor ...

随机推荐

  1. 基于QtQuick2.0应用程序运行于XP系统的诸多问题

    客户端 使用QtQuick技术开发酷炫的XP客户端经常遇到白屏或者无界面 if Qt is built using ANGLE, its shared libraries and the requir ...

  2. Windows failed to start.界面下修复win8引导

    首先要保证 系统本身是没有问题的 不是在装机的时候出现这种情况 那么可以按照以下方法来进行 首先要在另外一台电脑上将win8刻进u盘 启动时以u盘为第一启动项启动 进入win8装机界面 点击左下角的修 ...

  3. 第一种:NStread

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  4. IOS-图片操作集合

    编辑图片的几个方法 第一种 先用UIImage对象加载一张图片 然后转化成CGImageRef放到CGContext中去编辑 第二种  用CGImageCreate函数创建CGImageRef 然后把 ...

  5. 【Maven】项目添加Maven类库依赖

    1.右击项目-->Maven-->EnableDependencyManagement,按步骤完成操作. 2.右击项目-->Properties-->DeploymentAss ...

  6. iOS转场动画

    文顶顶 最怕你一生碌碌无为 还安慰自己平凡可贵 iOS开发UI篇—核心动画(转场动画和组动画) iOS开发UI篇—核心动画(转场动画和组动画) 一.转场动画简单介绍 CAAnimation的子类,用于 ...

  7. 一个库搞定各种分享--ShareSDK

    ShareSDK是为iOS.Android.WindowsPhone提供社会功能的一个组件,开发者只需10分钟即可集成到自己的APP中,它不仅支持分享给QQ好友.微信好友.微信朋友圈.新浪微博.腾迅微 ...

  8. 【 D3.js 入门系列 — 4 】 如何使用比例尺( scale )

    上一章中使用了一个很重要的概念 — 比例尺( scale ),本节将解说其使用方法. 1. 最大值和最小值 在介绍比例尺( scale )之前,先介绍两个经常和比例尺一起出现的函数,在[第3章]中也出 ...

  9. tomcat配置管理用户名密码

    tomcat6默认是将用户是注释的 配置文件在根目录下/conf/tomcat-users.xml文件中 配置默认如下: <!--  <role rolename="tomcat ...

  10. 15+优秀的jQuery视差插件

    jQuery视差效果的应用越来越广泛了,今天就给大家分享一些优秀的jQuery视差插件,它们确实太棒了! 原文地址:http://www.goodfav.com/jquery-parallax-plu ...