为什么在WCF中需要KnownTypeAttribute

时间:2011-09-16 12:44:51

标签: c# .net wcf

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

3 个答案:

答案 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>