在.net中, 各种类型的集合均实现了IEnumerable或者IEnumerable<T>接口,一个数据契约的数据成员可以是一个集合类型,服务契约 也可以定义直接与集合交互的操作,但是这都是.net所特有的,WCf不能在元数据中公开它们,然后WCF专门为集合提供了编组原则。
在定义服务时候,无论使用的是IEnumerable<T>、IList<T>和ICollection<T>,他们的传输形式都使用了数组,如
namespace
ContractInterface
{
[
ServiceContract
]
public
interface
IContractManager
{
[
OperationContract
]
IEnumerable
<
Contract
> GetContracts();
}
}
导出的结果为
public
interface
IContractManager
{
Contract
[] GetContracts();
}
如果契约中的集合为具体集合类型而非接口,而且属于可序列化集合,那么只要提供的集合包含了Add方法,并且符合以下签名中的一种,WCF就能够自动地将集合规范为数组类型。
并非只有内建的集合类型才具有自动编组为数组的能力,任何自定义的集合只要符合相同的先决条件,都可以被编组为数组。
CollectionDataContract特性
前面所示的编组为具体类型的机制并不理想,原有有三
- 它要求集合必须可序列化,而不是使用DataContract特性。
- 当服务的一方处理集合类型时,服务的另外一方若处理数组类型,就会导致双方语义不对称,集合拥有数组不具备的优势。
- 对于集合是否包含Add方法,或者集合是否支持IEnumerable和IEnumerable<T>接口,并没有编译时或者运行时的有效验证。如果不符合条件,就会导致数据契约不可工作。
[
AttributeUsage
(
AttributeTargets
.Class |
AttributeTargets
.Struct, Inherited =
false
, AllowMultiple =
false
)]
public
sealed
class
CollectionDataContractAttribute
:
Attribute
{
public
CollectionDataContractAttribute();
public
bool
IsReference {
get
;
set
; }
public
string
ItemName {
get
;
set
; }
public
string
KeyName {
get
;
set
; }
public
string
Name {
get
;
set
; }
public
string
Namespace {
get
;
set
; }
public
string
ValueName {
get
;
set
; }
}
CollectionDataContract 和DataContract相似,它不能序列化集合,将它应用到一个集合上时,会将集合当作一个泛型的链表类型公开给客户端链表可能不会对原来的集合执行 任何操作,但它会提供一个类似接口的集合类型,而不是数组。例如,定义如下类型:
namespace
ContractInterface
{
[
CollectionDataContract
(Name=
"MyCollectionOf{0}"
)]
public
class
MyCollection
<T>:
IEnumerable
<T>
{
public
void
Add(T item)
{ }
public
IEnumerator
<T> GetEnumerator()
{
throw
new
NotImplementedException
();
}
}
}
服务契约修改为:
namespace
ContractInterface
{
[
ServiceContract
]
public
interface
IContractManager
{
[
OperationContract
]
List
<
Contract
> GetContracts();
[
OperationContract
]
MyCollection
<
Contract
> GetContract();
}
}
则,导出的客户端定义为:
MyCollectionOfContract
GetContract();
集合类重新编组为
public
class
MyCollectionOfContract
: System.Collections.Generic.
List
<
Contract
> {
}
在装载服务时,特性会检查Add方法和IEnumerable或者IEnumerable<T>是否存在,如果不存在而引发InvalidDataContractException异常。
注意,不能同时将DataContract和CollecctionDataContract应用到集合类型上,在装载服务的时候同样会检查这一点。