среда, 2 сентября 2009 г.

Известные типы в WCF. Часть 2

Применение атрибута 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 { }
 
Известные типы могут добавляться не только в конфигурационный файл сервиса, но и в конфигурационный файл клиентского приложения, но в этом случае предполагается совместное использование типом между клиентом и сервисом.

Комментариев нет:

Отправить комментарий