Я изучаю WCF и не понимаю реального преимущества KnowTypeAttribute. Может ли кто-нибудь объяснить мне, зачем нам это нужно?
Почему KnownTypeAttribute нужен в WCF
Ответ 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>