原文: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. Django forms 关于select和checkbox设置初始选中值及让前端选中指定值

    Django的forms和models一样很牛逼.他有两种功能,一是生成form表单,还有就是form表单的验证. 这里主要说一下生成form表单时经常用到的需要设置 初始值 / 默认值 的情况. 1 ...

  2. Dapper 基础用法

    Dapper是.Net下的一个简单orm框架,具有以下特点: 1.简单,只需要一个文件即可(SqlMapper.cs) 2.快速,下面是一个查询结果集在500以上的运行速度对比 3.不要求特定的db ...

  3. c语言单链表,冒泡排序

    node *sort(node *head){ node *p,*p2,*p3; int n; int temp; n=length(head); if(head==NULL||head->ne ...

  4. QT学习 之 对话框 (四) 字体对话框、消息对话框、文件对话框、进程对话框(超详细中文注释)

    QMessageBox类: 含有Question消息框.Information消息框.Warning消息框和Critical消息框等 通常有两种方式可以来创建标准消息对话框: 一种是采用“基于属性”的 ...

  5. B-树和B+树的应用:数据搜索和数据库索引

    B-树和B+树的应用:数据搜索和数据库索引  B-树 1 .B-树定义 B-树是一种平衡的多路查找树,它在文件系统中很有用. 定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:⑴树中每 ...

  6. 【LigerUI】ajax请求firefox返回NS_BINDING_ABORTED

    在ligergrid中请求链接,post提交,返回NS_BINDING_ABORTED. 是因为ligergrid中默认是异步提交方式,如果没有设置同步的话, 在请求该链接的时候第一次请求还没有执行完 ...

  7. 【JavaScript】==与===对比

    1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进 ...

  8. C语言结构体变量内存分配与地址对齐

    地址对齐简单来说就是为了提高访问内存的速度. 数组的地址分配比较简单,由于数据类型相同,地址对齐是一件自然而然的事情. 结构体由于存在不同基本数据类型的组合,所以地址对齐存在不同情况,但总体来说有以下 ...

  9. JAVA的一些小笔记

    构造块优先于构造方法执行,而且每当有一个新的实例化对象产生时,就会重复执行构造块的程序. 静态块优先于构造块执行,而且不管有多少个实例化对象产生,静态块只会执行一次,它的主要作用是为类中的static ...

  10. 动态加载EXE和DLL

    程序中加载了一个DLL文件,但生成的EXE在脱离了DLL文件后仍然可以 单独使用,这是动态加载DLL技术.即:调用资源中的DLL. 此技术的好处:EXE可以使用DLL中的函数,但不会额外增加一 个DL ...