Подтвердить что ты не робот

Почему KnownTypeAttribute нужен в WCF

Я изучаю WCF и не понимаю реального преимущества KnowTypeAttribute. Может ли кто-нибудь объяснить мне, зачем нам это нужно?

4b9b3361

Ответ 1

KnownTypeAttribute позволяет назначить приемлемый производный класс для данного Контракта данных. Он определяет типы, которые должны распознаваться DataContractSerializer при сериализации или десериализации заданного типа.

Один простой пример.

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

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

}

[DataContract()]
class DerivedTask
{

}

При работе с полиморфными типами в контракте на обслуживание требуется атрибут KnownTypeAttribute, поскольку полиморфизм находится за пределами парадигмы ориентации службы.

Применить атрибут KnownTypeAttribute к типу, который указывает на Типы DataContractSerializer, которые должны быть распознаны, когда сериализация или десериализация экземпляра типа, к которому атрибут применяется. Этот атрибут может быть также распознан другими сериализаторы, которые понимают контракты данных.

Посмотрите здесь для более подробной информации.

Ответ 2

DataContractSerializer основан на контракте, то есть он не привязан к какой-либо конкретной модели. Все, что у него есть, это данные (обычно xml). Это означает, что если у вас есть такая модель, как:

 Customer
 SuperCustomer : Customer
 AwesomeCustomer : Customer

то сериализатор должен заранее знать, что каждый тип означает, если он видит его в данных; иначе он не будет знать, какой тип создать. Это делается несколькими способами, самым простым из которых является 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 - это означает, что ему не нужно знать заранее (поскольку он явственен в данных), но также означает, что он не может возможно работать для разных моделей (или, действительно, кросс-платформенных).

Ответ 3

Этот атрибут используется для включения дополнительных классов в метаданные службы, чтобы клиенты могли их видеть. Возьмем, например, следующее:

[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 предоставляет метаданные этой службы, он смотрит на контракт на обслуживание и задействованные операции, поэтому обнаруживает операцию Get, которая возвращает тип BaseModel. Таким образом, класс 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>