WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

在本篇文章中,我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契约关注于对服务操作的描述,数据契约关注于对于数据结构和格式的描述,而消息契约关注的是类型成员与消息元素的匹配关系。

我们知道只有可序列化的对象才能通过服务调用在客户端和服务端之间进行传递。到目前为止,我们知道的可序列化类型有两种:一种是应用了System.SerializableAttribute特性或者实现了System.Runtime.Serialization.ISerializable接口的类型;另一种是数据契约对象。对于基于这两种类型的服务操作,客户端通过System.ServiceModel.Dispatcher.IClientMessageFormatter将输入参数格式化成请求消息,输入参数全部内容作为有效负载置于消息的主体中;同样地,服务操作的执行结果被System.ServiceModel.Dispatcher.IDispatchMessageFormatter序列化后作为回复消息的主体。

在一些情况下,具有这样的要求:当序列化一个对象并生成消息的时候,希望将部分数据成员作为SOAP的报头,部分作为消息的主体。比如说,我们有一个服务操作采用流的方式进行文件的上载,除了以流的方式传输以二进制表示的文件内容外,还需要传输一个额外的基于文件属性的信息,比如文件格式、文件大小等。一般的做法是将传输文件内容的流作为SOAP的主体,将其属性内容作为SOAP的报头进行传递。这样的功能,可以通过定义消息契约来实现。

一、
消息契约的定义

消息契约和数据契约一样,都是定义在数据(而不是功能)类型上。不过数据契约旨在定义数据的结构(将数据类型与XSD进行匹配),而消息契约则更多地关注于数据的成员具体在SOAP消息中的表示。消息契约通过以下3个特性进行定义:System.ServiceModel.MessageContractAttribute、System.ServiceModel.MessageHeaderAttribute、System.ServiceModel.MessageBodyMemberAttribute。MessageContractAttribute应用于类型上,MessageHeaderAttribute和MessageBodyMemberAttribute则应用于属性或者字段成员上,表明相应的数据成员是一个基于SOAP报头的成员还是SOAP主体的成员。先来简单介绍一下这3个特性:

1、MessageContractAttribute

通过在一个类或者结构(Struct)上应用MessageContractAttribute使之成为一个消息契约。从MessageContractAttribute的定义来看,MessageContractAttribute大体上具有以下两种类型的属性成员:

1: [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false)]

2:
public
sealed
class MessageContractAttribute : Attribute

3: {

4:
//其他成员

5:
public
bool HasProtectionLevel { get; }

6:
public ProtectionLevel ProtectionLevel { get; set; }

7:?

8:
public
bool IsWrapped { get; set; }

9:
public
string WrapperName { get; set; }

10:
public
string WrapperNamespace { get; set; }

11: }

下面的代码中将Customer类型通过应用MessageContractAttribute使之成为一个消息契约。ID和Name属性通过应用MessageHeaderAttribute定义成消息报头(Header)成员,而Address属性则通过MessageBodyMemberAttribute定义成消息主体(Body)成员。后面的XML体现的是Customer对象在SOAP消息中的表现形式。

1: [MessageContract]

2:
public
class Customer

3: {

4: [MessageHeader(Name = "CustomerNo", Namespace = "http://www.artech.com/")]

5:
public Guid ID

6: { get; set; }

7:?

8: [MessageHeader(Name = "CustomerName", Namespace = "http://www.artech.com/")]

9:
public
string Name

10: { get; set; }

11:?

12: [MessageBodyMember(Namespace = "http://www.artech.com/")]

13:
public
string Address

14: { get; set; }

15: }

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2:
<s:Header>

3:
<a:Action
s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>

4:
<h:CustomerName
xmlns:h="http://www.artech.com/">Foo</h:CustomerName>

5:
<h:CustomerNo
xmlns:h="http://www.artech.com/">2f62405b-a472-4d1c-8c03-b888f9bd0df9</h:CustomerNo>

6:
</s:Header>

7:
<s:Body>

8:
<Customer
xmlns="http://tempuri.org/">

9:
<Address
xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

10:
</Customer>

11:
</s:Body>

12:
</s:Envelope>

如果我们将IsWrapped的属性设为false,那么套在Address节点外的Customer节点将会从SOAP消息中去除。

1: [MessageContract(IsWrapped = false)]

2:
public
class Customer

3: {

4:
//省略成员

5: }

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: ......

3:
<s:Body>

4:
<Address
xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

5:
</s:Body>

6:
</s:Envelope>

我们同样可以自定义这个主体封套(Wrapper)的命名和命名空间。下面我们就通过将MessageContractAttribute的WrapperName和WrapperNamespace属性设为Cust和http://www.artech.com/。

1: [MessageContract(IsWrapped = true, WrapperName = "Cust", WrapperNamespace = "http://www.artech.com/")]

2:
public
class Customer

3: {

4:
//省略成员

5: }

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2: ......

3:
<s:Body>

4:
<Cust
xmlns="http://www.artech.com/">

5:
<Address>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>

6:
</Cust>

7:
</s:Body>

8:
</s:Envelope>

2、MessageHeaderAttribute

MessageHeaderAttribute和MessageBodyMemberAttribute分别用于定义消息报头成员和消息主体成员,它们都有一个共同的基类:System.ServiceModel.MessageContractMemberAttribute。MessageContractMemberAttribute定义了以下属性成员:HasProtectionLevel、ProtectionLevel、Name和Namespace。

1:
public
abstract
class MessageContractMemberAttribute : Attribute

2: {

3:
public
bool HasProtectionLevel { get; }

4:
public ProtectionLevel ProtectionLevel { get; set; }

5:?

6:
public
string Name { get; set; }

7:
public
string Namespace { get; set; }

8: }

通过在属性或者字段成员上应用MessageHeaderAttribute使之成为一个消息报头成员。MessageHeaderAttribute定义了以下3个属性,如果读者对SOAP规范有一定了解的读者,相信对它们不会陌生。

注:在《WCF技术剖析(卷1)》中的第六章有对SOAP 1.2的基本规范有一个大致的介绍,读者也可以直接访问W3C网站下载官方文档。

  • Actor:表示处理该报头的目标节点(SOAP Node),SOAP1.1中对应的属性(Attribute)为actor,SOAP 1.2中就是我们介绍的role属性
  • MustUnderstand:表述Actor(SOAP 1.1)或者Role(SOAP 1.2)定义的SOAP节点是否必须理解并处理该节点。对应的SOAP报头属性为mustUnderstand
  • Relay:对应的SOAP报头属性为relay,表明该报头是否需要传递到下一个SOAP节点

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]

2:
public
class MessageHeaderAttribute : MessageContractMemberAttribute

3: {

4:
public
string Actor { get; set; }

5:
public
bool MustUnderstand { get; set; }

6:
public
bool Relay { get; set; }

7: }

同样使用上面定义的Customer消息契约,现在我们相应地修改了ID属性上的MessageHeaderAtribute设置:MustUnderstand = true, Relay=true, Actor=http://www.w3.org/ 2003/05/soap-envelope/role/ultimateReceiver。实际上将相应的SOAP报头的目标SOAP节点定义成最终的消息接收者。由于http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver是SOAP 1.2的预定义属性,所以这个消息契约之后在基于SOAP 1.2的消息版本中有效。后面给出的为对应的SOAP消息。

1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public
class Customer

2: {

3:
//其他成员

4: [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" )]

5:
public Guid ID

6: { get; set; }

7:

8: }

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2:
<s:Header>

3: ......

4:
<h:CustomerNo
s:role="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver"
s:mustUnderstand="1"
s:relay="1"
xmlns:h="http://www.artech.com/">5330c91a-7fd7-4bf5-ae3e-4ba9bfef3d4d</h:CustomerNo>

5:
</s:Header>

6: ......

7:
</s:Envelope>

http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver在SOAP1.1中对应的表示为:"http://schemas.xmlsoap.org/soap/actor/ultimateReceiver(具有不同的命名空间)。如果在SOAP 1.1下,ID成员对应的MessageHeaderAttribute应该做如下的改动。从对应的SOAP消息来看,在SOAP 1.2中的role属性变成了actor属性。

1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public
class Customer

2: {

3:
//其他成员

4: [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver" )]

5:
public Guid ID

6: { get; set; }

7: }

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

2:
<s:Header>

3: ......

4:
<h:CustomerNo
s:actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver"
s:mustUnderstand="1"
xmlns:h="http://www.artech.com/">e48a8897-c644-49f8-b5e7-cd16be4c75b7</h:CustomerNo>

5:
</s:Header>

6: ......

7:
</s:Envelope>

3、MessageBodyMemberAttribute

MessageBodyMemberAttribute应用于属性或者字段成员,应用了该特性的属性或者字段的内容将会出现在SOAP的主体部分。MessageBodyMemberAttribute的定义显得尤为简单,仅仅具有一个Order对象,用于控制成员在SOAP消息主体中出现的位置。默认的排序规则是基于字母排序。

可能细心的读者会问,为什么MessageHeaderAttribute中没有这样Order属性呢?原因很简单,MessageHeaderAttribute定义的是单个SOAP报头,SOAP消息报头集合中的每个报头元素是次序无关的。而MessageBodyMemberAttribute则是定义SOAP主体的某个元素,主体成员之间的次序也是契约的一个重要组成部分。所以MessageHeaderAttribute不叫MessageHeaderMemberAttribute。

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]

2:
public
class MessageBodyMemberAttribute : MessageContractMemberAttribute

3: {

4:
public
int Order { get; set; }

5: }

二、实例演示:基于消息契约的方法调用是如何格式化成消息的?

在WCF体系中,MessageFormatter负责序列化和反序列化任务(在《WCF技术剖析(卷1)》中的第5章对基于MessageFormatter的序列化机制有详细的介绍):ClientMessageFormatter和DispatchMessageFormatter分别在客户端和服务端,根据操作的描述(Operation Description),借助于相应的序列化器(Serializer)实现了方法调用与消息之间的转换。接下来,我将通过一个实实在在的案例程序为大家演示如何通过ClientMessageFormatter将输入参数转换为基于当前服务操作的Message。由于本节的主题是消息契约,所以在这里我们将转换对象限定为消息契约。不过,不论是消息参数还是一般的可序列化对象,其转换过程都是一样的。

步骤一:创建消息契约

本案例模拟一个订单处理的WCF应用,我们首先定义如下一个Order类型。Order是一个消息契约,属性OrderID和Date通过MessageHeaderAttribute定义成消息报头,作为主体的Details的类型OrderDetails被定义成集合数据契约。OrderDetails的元素类型是数据契约OrderDetail,代表订单中每笔产品明细。

1:
using System;

2:
using System.Collections.Generic;

3:
using System.Runtime.Serialization;

4:
using System.ServiceModel;

5:
namespace Artech.TypedMessage

6: {

7: [MessageContract]

8:
public
class Order

9: {

10: [MessageHeader(Namespace ="http://www.artech.com/")]

11:
public Guid OrderID

12: { get; set; }

13:?

14: [MessageHeader(Namespace ="http://www.artech.com/")]

15:
public DateTime Date

16: { get; set; }

17:?

18: [MessageBodyMember]

19:
public OrderDetails Details

20: { get; set; }

21:?

22:
public
override
string ToString()

23: {

24:
return
string.Format("Oder ID: {0}\nDate: {1}\nDetail Count: {2}",this.OrderID,this.Date.ToShortDateString(),this.Details.Count);

25: }

26: }

27:?

28: [CollectionDataContract(ItemName = "Detail",Namespace ="http://www.artech.com/")]

29:
public
class OrderDetails : List<OrderDetail>

30: { }

31:?

32: [DataContract(Namespace ="http://www.artech.com/")]

33:
public
class OrderDetail

34: {

35: [DataMember]

36:
public Guid ProductID

37: { get; set; }

38:?

39: [DataMember]

40:
public
int Quantity

41: { get; set; }

42: }

43: }

步骤二:创建MessageFormatter

本例的目的在于重现WCF如何通过ClientMessageFormatter实现将输入参数序列化成请求消息,以及通过DispatchMessageFormatter实现将请求消息反序列化成输入参数。根据使用的序列化器的不同,WCF中定义了两种典型的MessageFormatter:一种是基于DataContractSerializer的DataContractSerializerOperationFormatter;另一种则是基于XmlSerializer的XmlSerializerOperationFormatter。由于DataContractSerializerOperationFormatter是默认的MessageFormatter,所以我们这个案例就采用DataContractSerializerOperationFormatter。

我们的任务就是创建这个DataContractSerializerOperationFormatter。由于这是一个定义在System.ServiceModel.Dispatcher命名空间下的内部(internal)类型,所以我们只能通过反射的机制调用构造函数来创建这个对象。DataContractSerializerOperationFormatter定义了唯一的一个构造函数,3个输入参数类型分别为:OperationDescription,DataContractFormatAttribute和DataContractSerializerOperationBehavior。

1:
internal
class DataContractSerializerOperationFormatter : OperationFormatter

2: {

3:
//其他成员

4:
public DataContractSerializerOperationFormatter(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory);

5: }

为此我们定义下面一个辅助方法CreateMessageFormatter<TFormatter, TContract>。TFormatter代表MessageFormatter的两个接口:IClientMessageFormatter和IDispatchMessageFormatter(DataContractSerializerOperationFormatter同时实现了这两个接口),TContract则是服务契约的类型。参数operationName为当前操作的名称。代码不算复杂,主要的流程如下:通过服务契约类型创建ContractDescription,根据操作名称得到OperationDescription对象。通过反射机制调用DataContractSerializerOperationFormatter的构造函数创建该对象。

1:
static TFormatter CreateMessageFormatter<TFormatter, TContract>(string operationName)

2: {

3: ContractDescription contractDesc = ContractDescription.GetContract(typeof(TContract));

4: var operationDescs = contractDesc.Operations.Where(op => op.Name == operationName);

5:
if(operationDescs.Count() == 0)

6: {

7:
throw
new ArgumentException("operationName","Invalid operation name.");

8: }

9: OperationDescription operationDesc = operationDescs.ToArray()[0];

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

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

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

13:
return (TFormatter)constructor.Invoke(new
object[] { operationDesc, new DataContractFormatAttribute(), null });

14: }

MessageFormatter已经创建出来了,序列化与反序列化的问题就很简单了。为此我定义了以下两个辅助方法:SerializeRequest<TContract>和DeserializeRequest<TContract>,具体实现就是调用创建出来的MessageFormatter的同名方法。

1:
static Message SerializeRequest<TContract>(MessageVersion messageVersion, string operationName, params
object[] values)

2: {

3: IClientMessageFormatter formatter = CreateMessageFormatter<IClientMessageFormatter, TContract>(operationName);

4:
return formatter.SerializeRequest(messageVersion, values);

5: }

6:?

7:
static
void DeserializeRequest<TContract>(Message message, string operationName, object[] parameters)

8: {

9: IDispatchMessageFormatter formatter = CreateMessageFormatter<IDispatchMessageFormatter, TContract>(operationName);

10: formatter.DeserializeRequest(message, parameters);

11: }

步骤三:通过MessageFormmatter实现消息的格式化

现在我们通过一个简单的例子来演示通过上面创建的MessageFormatter实现对消息的格式化。由于MessageFormatter进行序列化和反序列化依赖于操作的描述(消息的结构本来就是由操作决定的),为此我们定义了一个服务契约IOrderManager。操作ProcessOrder将消息契约Order作为唯一的参数。

1:
using System.ServiceModel;

2:
namespace Artech.TypedMessage

3: {

4: [ServiceContract]

5:
public
interface IOrderManager

6: {

7: [OperationContract]

8:
void ProcessOrder(Order order);

9: }

10: }

在下面的代码中,先调用SerializeRequest<IOrderManager>方法将Order对象进行序列化并生成Message对象,该过程实际上体现了WCF的客户端框架是如何通过ClientMessageFormatter将操作方法调用连同输入参数转换成请求消息的。随后,调用DeserializeRequest<IOrderManager>方法将Message对象反序列化成Order对象,该过程则代表WCF的服务端框架是如何通过DispatchMessageFormatter将请求消息反序列化成输入参数的。

1: OrderDetail detail1 = new OrderDetail

2: {

3: ProductID = Guid.NewGuid(),

4: Quantity = 666

5: };

6:?

7: OrderDetail detail2 = new OrderDetail

8: {

9: ProductID = Guid.NewGuid(),

10: Quantity = 999

11: };

12:?

13: Order order = new Order

14: {

15: OrderID = Guid.NewGuid(),

16: Date = DateTime.Today,

17: Details = new OrderDetails { detail1, detail2 }

18: };

19:
//模拟WCF客户端的序列化

20: Message message = SerializeRequest<IOrderManager>(MessageVersion.Default, "ProcessOrder", order);

21: MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);

22: WriteMessage(buffer.CreateMessage(), "message.xml");

23:?

24:
//模拟WCF服务端的反序列化

25:
object[] DeserializedOrder = new
object[]{ null };

26: DeserializeRequest<IOrderManager>(buffer.CreateMessage(), "ProcessOrder", DeserializedOrder);

27: Console.WriteLine(DeserializedOrder[0]);

下面的XML表示调用SerializeRequest<IOrderManager>生成的SOAP消息。程序最终的输出结果也表明了反序列化的成功执行。

1:
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">

2:
<s:Header>

3:
<a:Action
s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>

4:
<h:Date
xmlns:h="http://www.artech.com/">2008-12-21T00:00:00+08:00</h:Date>

5:
<h:OrderID
xmlns:h="http://www.artech.com/">cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe</h:OrderID>

6:
</s:Header>

7:
<s:Body>

8:
<Order
xmlns="http://tempuri.org/">

9:
<Details
xmlns:d4p1="http://www.artech.com/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

10:
<d4p1:Detail>

11:
<d4p1:ProductID>bc2a186d-569a-4146-9b97-3693248104c0</d4p1:ProductID>

12:
<d4p1:Quantity>666</d4p1:Quantity>

13:
</d4p1:Detail>

14:
<d4p1:Detail>

15:
<d4p1:ProductID>72687c23-c2b2-4451-b6c3-da6d040587fc</d4p1:ProductID>

16:
<d4p1:Quantity>999</d4p1:Quantity>

17:
</d4p1:Detail>

18:
</Details>

19:
</Order>

20:
</s:Body>

21:
</s:Envelope>

1: Oder ID: cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe

2: Date: 12/21/2008

3: Detail Count: 2

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

时间: 2024-10-24 19:14:30

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化的相关文章

WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理

原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境下的重要意义,并阐明了其根本原因.但是,是否直接调用ICommunicationObject的Close方法将服务代理关闭就万事大吉了呢?事情远不会这么简单,这其中还会涉及关于异常处理的一些操作,这就是本篇文章需要讨论的话题. 一.异常的抛出与Close的失败 一般情况下,当服务端抛出异常,客户客户

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

原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇] 在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道:当遇到某些异常,我们需要强行中止(Abort)信道,相关的原理,可以参考我的文章<服务代理不能得到及时关闭会有什么后果?>.在真正的企业级开发中,正如我们一般不会让开发人员手工控制数据库连接的开启和关闭一样,我们一般也不会让开发人员手工去创建.开启.中止和关闭信道,这些工作是框架应该完成的操作.这篇文章,我们就来介绍如果通过一些编程技巧,

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

原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术剖析(卷1)>的读者,应该会知道在第7章中我通过类似于AOP的方式解决了相似的问题,现在我们来讨论这个解决方案. 通过<服务代理不能得到及时关闭会有什么后果?>的介绍,我们知道了及时关闭服务代理的重要意义,并且给出了正确的编程方式.如果严格按照上面的编程方式,就意味着对于每一个服务调用,都要

WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码和配置:通过ChannelFactory<TChannel>创建服务代理对象.在这篇文章中,我们采用一种独特的方式进行服务的调用.从本质上讲,我们只要能够创建于服务端相匹配的终结点,就能够实现正常的服务调用.在WCF客户端元数据架构体系中,利用MetadataExchangeClient可以获取服

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

http://www.cnblogs.com/artech/archive/2009/11/21/1607686.html 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖析(卷1)>写作期间,对WCF又有了新的感悟,为此以书名开始本人的第三个WCF系列.本系列的目的在于对<WCF技术剖析>的补充,会对书中的一些内容进行展开讲述,同时会囊括很多由于篇幅的原因忍痛割弃的内容. [第1篇] 通过一个ASP

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

原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构 细算起来,已经有好几个月没有真正的写过文章了.近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直无暇管理自己的Blog.到目前为止<WCF技术剖析(卷1)>的写作暂告一段落,初步预计于下个月由武汉博文视点出版.在<WCF技术剖析>写作期间,对WCF又有了新的感悟,为此以书名开始本人的第三个WCF系列.本系列的目的在于对<WCF技术剖析>的补充,会对书中的一些内容进行展

WCF技术剖析之十一:异步操作在WCF中的应用(上篇)

原文:WCF技术剖析之十一:异步操作在WCF中的应用(上篇) 按照操作执行所需的资源类型,我们可以将操作分为CPU绑定型(CPU Bound)操作和I/O绑定型(I/O Bound)操作.对于前者,操作的执行主要利用CPU进行密集的计算,而对于后者,大部分的操作处理时间花在I/O操作处理,比如访问数据库.文件系统.网络资源等.对于I/O绑定型操作,我们可以充分利用多线程的机制,让多个操作在自己的线程并发执行,从而提高系统性能和响应能力.服务调用就是典型的I/O绑定型操作,所以多线程在服务调用中具

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

原文:WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效 本篇文章来源于几天前一个朋友向我咨询的问题.问题是这样的,他说他采用ASP.NET应用程序的方式对定义的WCF服务进行寄宿(Hosting),并使用配置的方式对服务的BaseAddress进行了设置,但是在创建ServiceHost的时候却抛出InvalidOperationException,并提示相应Address Scheme的BaseAddress找不到.我意识到这可能和WC

WCF技术剖析之九:服务代理不能得到及时关闭会有什么后果?

原文:WCF技术剖析之九:服务代理不能得到及时关闭会有什么后果? 我们想对WCF具有一定了解的人都会知道:在客户端通过服务调用进行服务调用过程中,服务代理应该及时关闭.但是如果服务的代理不等得到及时的关闭,到底具有怎样的后果?什么要关闭服务代理?在任何时候都需要关闭服务代理吗?是否有一些例外呢?本篇文章将会围绕着这些问题展开. 一.会话信道(Sessionful Channel) V.S. 数据报信道(Datagram Channel) WCF通过信道栈实现了消息的编码.传输及基于某些特殊功能对