为什么在WCF中需要KnownTypeAttribute

ABC*_*BCD 12 .net c# wcf

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

Mar*_*ell 22

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

 Customer
 SuperCustomer : Customer
 AwesomeCustomer : Customer
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • +1.这不是一个"答案".这是'SuperAnswer`.事实上,'AwesomeAnswer`.:d (4认同)

Cha*_*thJ 9

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

一个简单的例子.

[ServiceContract()]
interface ITaskManager
{
    [OperationContract()]
    MyCollection<Task> GetTaskByAssignedName(string name);
}

[DataContract()]
[KnownType(typeof(DerivedTask))]
class Task
{

}

[DataContract()]
class DerivedTask
{

}
Run Code Online (Sandbox Code Playgroud)

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

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

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


Dar*_*rov 6

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

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

[DataContract]
public class ChildModel: BaseModel
{
    [DataMember]
    public string Foo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

以及以下服务合同:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    BaseModel Get();
}
Run Code Online (Sandbox Code Playgroud)

并且你实现它像这样:

public class MyService: IMyService
{
    public BaseModel Get()
    {
        return new ChildModel();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

[DataContract]
[KnownType(typeof(ChildModel))]
public class BaseModel
{
    [DataMember]
    public string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)