为什么在WCF中需要KnownTypeAttribute

我正在学习WCF并且不了解KnowTypeAttribute的真正优势。 有人可以解释我为什么需要它吗?

通过KnownTypeAttribute,您可以为给定的数据协定指定可接受的派生类。 它指定在序列化或反序列化给定类型时应由DataContractSerializer识别的类型。

一个简单的例子。

[ServiceContract()] interface ITaskManager { [OperationContract()] MyCollection GetTaskByAssignedName(string name); } [DataContract()] [KnownType(typeof(DerivedTask))] class Task { } [DataContract()] class DerivedTask { } 

在服务契约中使用多态类型时,需要KnownTypeAttribute,因为多态性超出了服务方向的范例。

将KnownTypeAttribute属性应用于类型,以指示在序列化或反序列化应用该属性的类型的实例时应识别的DataContractSerializer类型。 理解数据协定的其他序列化程序也可以识别此属性。

请查看此处了解更多详情。

DataContractSerializer是基于契约的,这意味着它不依赖于任何特定类型的模型。 它只有数据(通常是xml)。 这意味着,如果你有一个像这样的模型:

  Customer SuperCustomer : Customer AwesomeCustomer : Customer 

那么序列化器需要事先知道每种类型在数据中看到的含义; 否则它将不知道要创建什么类型 。 这可以通过多种方式完成,其中最简单的方法是KnownTypeAttribute。

考虑替代方案; 所有序列化程序都知道“客户”,它希望在某些xml中看作... 。 相反,它得到了其他东西(无关紧要,但让我们说... 。现在它做了什么?它是否开始寻找可能看起来类型?这是非常不精确和风险的。还要考虑,它需要能够为这些数据生成WSDL / MEX导出 – 如果它只知道“Customer”,则无法警告调用者也期望SuperCustomer / AwesomeCustomer – 这意味着WSDL / MEX是不完整和无用的。

XmlSerializer(XmlIncludeAttribute)和protobuf-net(ProtoIncludeAttribute)使用相同的方法,可能是我最基于合同的序列化程序。

替代方案是基于类型的序列化器(BinaryFormatter,NetDataContractSerializer等) – 在此它包含数据中的类型 ,意味着Your.Namespace.Type, Your.Assembly, blah – 这意味着它不需要事先知道(因为它在数据中是明确的,但也意味着它不可能适用于不同的模型(或者实际上是跨平台的)。

此属性用于在服务的元数据中包含其他类,以便客户端可以看到它们。 我们举个例子如下:

 [DataContract] public class BaseModel { [DataMember] public string Id { get; set; } } [DataContract] public class ChildModel: BaseModel { [DataMember] public string Foo { get; set; } } 

和以下服务合同:

 [ServiceContract] public interface IMyService { [OperationContract] BaseModel Get(); } 

并且你实现它像这样:

 public class MyService: IMyService { public BaseModel Get() { return new ChildModel(); } } 

现在,当WCF公开此服务的元数据时,它会查看服务契约和涉及的操作,因此它会发现返回BaseModel类型的Get操作。 因此,BaseModel类会自动在元数据中公开。 问题是,当您尝试调用服务时,实际的实现返回WCF的ChildModel并不知情。 该服务的客户端既不具备此类型的知识。

因此,您需要明确指出您在实现中使用的此类,但不是合同的一部分。 这可以通过使用KnownType属性来完成:

 [DataContract] [KnownType(typeof(ChildModel))] public class BaseModel { [DataMember] public string Id { get; set; } } 

指定此已知类型的另一种方法是使用配置文件: