Применение атрибута ServiceKnownTypeAttribute
Определение класса ServiceKnownTypeAttribute следующее:
[AttributeUsageAttribute(AttributeTargets.Class|AttributeTargets.Method|
AttributeTargets.Interface, Inherited = true,
AllowMultiple = true)]
public sealed class ServiceKnownTypeAttribute : Attribute
{
public ServiceKnownTypeAttribute(Type type);
public ServiceKnownTypeAttribute(string methodName);
public ServiceKnownTypeAttribute(string methodName, Type declaringType);
//Остальные члены
}
Атрибут ServiceKnownTypeAttribute применяется для конкретной операции или контракту целиком.
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IService
{
[OperationContract]
[ServiceKnownType(typeof(Circle))]
[ServiceKnownType(typeof(Square))]
Shape CreateShape(ShapeType shapeType, int id);
//Такого поведения нельзя добиться с применением атрибута KnownTypeAttribute
[OperationContract]
[ServiceKnownType(typeof(Shape))]
[ServiceKnownType(typeof(Circle))]
[ServiceKnownType(typeof(Square))]
void ProcessShape(object shape);
}
[ServiceContract(SessionMode = SessionMode.Required)]
[ServiceKnownType(typeof(Circle))]
[ServiceKnownType(typeof(Square))]
public interface IService
{
[OperationContract]
Shape CreateShape(ShapeType shapeType, int id);
}
У этого способа есть несколько особенностей. Во-первых, хотя можно применить атрибут ServiceKnownType к отдельному методу, на самом деле эффект будет такой же, как и от применения этого атрибута к контракту целиком, т.е. будет затронут не только этот метода, а все методы контракта, в которых участвует соответствующий базовый класс. Во-вторых, если не будет применяться совместное использование типов между клиентом и сервером, то приведенные примеры приведут к генерации класса Shape с атрибутами KnownTypeAttribute, а не к применению атрибута ServiceKnownTypeAttribute к сгенерированному контракту. Остальные версии конструктор ServiceKnownTypeAttribute используются для указания метода, возвращающего перечень известных типов. Один из конструкторов принимает строковый параметр и тип.
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(Helper))]
public interface IService
{
[OperationContract]
Shape CreateShape(ShapeType shapeType, int id);
}
static class Helper
{
static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
return new Type[] { typeof(Circle), typeof(Square) };
}
}
Другой конструктор принимает только строку с именем метода и может применяться напрямую к классу службы (а не интерфейсу службы).
[ServiceContract]
[ServiceKnownType("GetKnownTypes")]
public class Service
{
static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
return new Type[] { typeof(Circle), typeof(Square) };
}
[OperationContract]
public Shape CreateShape(ShapeType shapeType, int id) { ... }
}
ПРИМЕЧАНИЕ
В отличие от атрибута KnownTypeAttribute метод, имя которого передается атрибуту ServiceKnownType, должен содержать параметр ICustomAttributeProvider. Кроме того, при попытке использовать метод с неверной сигнатурой вы получите исключение при попытке открытия службы, а не во время обновления метаданных службы клиентом
Использование конфигурационного файла приложения
Задание перечня известных типов с помощью атрибутов подразумевает, что вы заранее знаете, какие типы будут передаваться между клиентом и службой. В противном случае, понадобиться постоянная перекомпиляция, развертывание и обновление метаданных, для поддержания перечня известных типов в актуальном состоянии. Альтернативой может служить декларативное объявление известных типов в конфигурационном файле приложения сервиса. Для этого необходимо добавить информацию об известных типах в конфигурационный файл приложения или web.config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type = "Server.Shape, Server">
<knownType type = "Server.Circle, Server"/>
<knownType type = "Server.Square, Server"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
ПРИМЕЧАНИЕ
Для сборок со строгим именем (strongly named assemblies) строка, содержащая описание типа, помимо полного имени типа и названия сборки должна содержать номер версии, идентификатор регионального стандарта и маркер открытого ключа. Пример использования класса EventArgs из сборки mscorlib будет следующим: add type="System.EventArgs, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
В случае применения конфигурационного файла на стороне сервиса, известные типы будут добавляться в экспортируемые метаданные службы, и будут влиять на генерацию кода на стороне клиента. При совместном использовании типов между сервисом и клиентом в сгенерированном коде будет добавлен атрибут ServiceKnownTypeAttribute для всех методов, аргументы которых добавлены в перечень известных типов в конфигурационном файле сервиса.
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute(ConfigurationName="ServiceReference.IService")]
public interface IService {
[OperationContractAttribute(Action="http://tempuri.org/IService/CreateShape",
ReplyAction="http://tempuri.org/IService/CreateShapeResponse")]
[ServiceKnownTypeAttribute(typeof(Server.Circle))]
[ServiceKnownTypeAttribute(typeof(Server.Square))]
Server.Shape CreateShape(Server.ShapeType shapeType, int id);
}
При отсутствия совместного использования типов, будет использован атрибут KnownTypeAttribute для базового класса иерархии:
[DebuggerStepThroughAttribute()]
[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[DataContractAttribute(Name="Shape", Namespace="http://schemas.datacontract.org/2004/07/Server")]
[SerializableAttribute()]
[KnownTypeAttribute(typeof(Client.ServiceReference.Circle))]
[KnownTypeAttribute(typeof(Client.ServiceReference.Square))]
public partial class Shape : object, IExtensibleDataObject, INotifyPropertyChanged { }
Известные типы могут добавляться не только в конфигурационный файл сервиса, но и в конфигурационный файл клиентского приложения, но в этом случае предполагается совместное использование типом между клиентом и сервисом.
Комментариев нет:
Отправить комментарий