我正在学习WCF并且不了解KnowTypeAttribute的真正优势。有人可以解释我为什么需要它吗?
答案 0 :(得分:22)
DataContractSerializer是基于合同的,这意味着它不依赖于任何特定类型的模型。它只有数据(通常是xml)。这意味着如果你有一个类似的模型:
Customer
SuperCustomer : Customer
AwesomeCustomer : Customer
然后序列化器需要提前知道每种类型在数据中看到的含义;否则将无法知道要创建的类型。这可以通过多种方式完成,其中最简单的方法是KnownTypeAttribute。
考虑替代方案;所有序列化程序都知道“Customer”,它希望在某些xml中看到<customer>...</customer>
。相反,它得到了其他东西(无关紧要,但让我们说<superCustomer>...</superCustomer>
。现在它做了什么?它是否开始寻找可能看起来类型?这是非常不精确和有风险的。还要考虑,它需要能够为此数据生成WSDL / MEX导出 - 如果它只知道“Customer”,则无法警告调用者也期望SuperCustomer / AwesomeCustomer - 这意味着WSDL / MEX不完整且无用。
XmlSerializer(XmlIncludeAttribute)和protobuf-net(ProtoIncludeAttribute)以及可能是我最基于合同的序列化程序使用了相同的方法。
替代方法是基于 type 的序列化程序(BinaryFormatter,NetDataContractSerializer等) - 其中包含数据中的类型,意思是Your.Namespace.Type, Your.Assembly, blah
- 这意味着它不需要提前知道(因为它在数据中是明确的),但也意味着它不能可能为不同的模型(或者实际上是跨平台)工作。
答案 1 :(得分:9)
KnownTypeAttribute使您能够为给定的数据协定指定可接受的派生类。它指定在序列化或反序列化给定类型时应由DataContractSerializer识别的类型。
一个简单的例子。
[ServiceContract()]
interface ITaskManager
{
[OperationContract()]
MyCollection<Task> GetTaskByAssignedName(string name);
}
[DataContract()]
[KnownType(typeof(DerivedTask))]
class Task
{
}
[DataContract()]
class DerivedTask
{
}
在服务合同中使用多态类型时,需要KnownTypeAttribute,因为多态性超出了服务方向的范例。
将KnownTypeAttribute属性应用于要指示的类型 应该在何时识别的DataContractSerializer类型 序列化或反序列化类型的实例 属性已应用。其他人也可以识别此属性 理解数据合同的序列化器。
请查看here了解更多详情。
答案 2 :(得分:6)
此属性用于在服务的元数据中包含其他类,以便客户端可以看到它们。我们举个例子:
[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类会自动在元数据中公开。问题是,当您尝试调用服务时,实际的实现返回ChildModel
,因为WCF不知道。该服务的客户既不了解这种类型。
因此,您需要明确指出您在实现中使用的此类,但不是合同的一部分。这可以通过使用KnownType属性来完成:
[DataContract]
[KnownType(typeof(ChildModel))]
public class BaseModel
{
[DataMember]
public string Id { get; set; }
}
指定此已知类型的另一种方法是使用配置文件:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyCompany.BaseModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
<knownType type="MyCompany.ChildModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>