Поделиться через


Создание контрактов служб

Этот раздел описывает, что такое контракты служб, как они определяются, какие операции доступны (также описаны последствия для лежащего в основе обмена сообщениями), какие типы данных используются; кроме того, здесь даются ответы на другие вопросы, которые помогут при разработке операций, удовлетворяющих требованиям вашего сценария.

Создание контракта службы

Службы являются группами операций. Для создания контракта службы необходимо смоделировать операции и определить их группировку. В приложениях Windows Communication Foundation (WCF) операции нужно определить, создав метод и пометив его атрибутом OperationContractAttribute. Затем, для создания контракта службы, необходимо сгруппировать операции либо объявив их в интерфейсе, отмеченном атрибутом ServiceContractAttribute, либо определив их в классе с таким же атрибутом. (Основной пример см. в разделе Как определить контракт службы Windows Communication Foundation.)

Методы, не имеющие атрибута OperationContractAttribute, не являются операциями служб и не могут использоваться клиентами служб WCF. Как и любой управляемый метод, они могут вызываться объектами в пределах их объявленной области доступа.

Аналогично, всегда можно создать класс контракта службы или интерфейс, который не объявляет никаких операций службы, — это то же самое, что класс или интерфейс, не имеющий методов. Все службы, построенные с применением контракта такого типа, не предоставляют клиентам операций для использования. Этот раздел рассказывает о принятии решений по следующим вопросам при создании контракта службы:

  • использовать ли классы или интерфейсы;

  • как задать типы данных, которыми нужно обмениваться;

  • какие типы шаблонов обмена можно использовать;

  • можно ли сделать явные требования безопасности частью контракта;

  • каковы ограничения для входных и выходных данных операций.

Классы или интерфейсы

И классы, и интерфейсы представляют собой группировку функциональности, поэтому и те, и другие можно использовать для определения контракта службы WCF. Однако рекомендуется использовать интерфейсы, так как они напрямую моделируют контракты служб. Не имея реализации, интерфейсы всего лишь определяют группу методов с определенными сигнатурами. Аналогично, нереализованный контракт службы определяет группу операций с определенными сигнатурами. Если реализовать интерфейс контракта службы, то будет реализована служба WCF.

Все преимущества управляемых интерфейсов относятся также к интерфейсам контрактов служб:

  • интерфейсы контрактов служб могут расширять любое число других интерфейсов контрактов служб;

  • отдельный класс может реализовывать любое число контрактов служб, реализуя интерфейсы этих контрактов служб;

  • можно изменить реализацию контракта службы, изменив реализацию интерфейса и не изменяя сам контракт;

  • можно создать несколько версий службы, реализуя старый и новый интерфейсы. Старые клиенты могут подключаться к исходной версии, а более новые клиенты — к более новой версии.

ms733070.note(ru-ru,VS.100).gifПримечание
При наследовании от других интерфейсов контрактов служб нельзя переопределять свойства операций, например имя или пространство имен. Если попытаться сделать это, будет создана новая операция в текущем контракте службы.

Пример использования интерфейса для создания контракта службы см. в разделе Как создавать службы с помощью интерфейса контракта.

Однако можно использовать класс для определения контракта службы и одновременной реализации этого контракта. Преимущество создания службы путем применения атрибутов ServiceContractAttribute и OperationContractAttribute напрямую к классу и методам класса, соответственно, — это скорость и простота. Недостаток состоит в том, что управляемые классы не поддерживают множественное наследование, и в результате могут реализовывать только один контракт службы за раз. Кроме того, любое изменение сигнатуры класса или метода изменяет открытый контракт для этой службы, что может предотвратить использование службы неизмененными клиентами. Дополнительные сведения см. в разделе Реализация контрактов служб.

Пример использования класса для создания контракта службы и одновременной его реализации см. в разделе Как создавать контракт Windows Communication Foundation с помощью класса.

К этому моменту у вас должно было сложиться представление о том, в чем заключается разница между определением контракта службы через интерфейс и через класс. Следующий шаг — определить, какие данные могут передаваться между службой и клиентами.

Параметры и возвращаемые значения

Каждая операция имеет возвращаемое значение и параметр, даже в случае void. Однако, в отличие от локального метода, в котором можно передавать ссылки на объекты от одного объекта к другому, операции служб не передают ссылки на объекты. Вместо этого они передают копии объектов.

Это важно, потому что каждый тип, используемый для параметра или возвращаемого значения, должен быть сериализуем. Это значит, должно быть возможно преобразование объекта этого типа в поток байтов и обратно из потока байтов в объект.

Примитивные типы сериализуемы по умолчанию, как и многие типы в .NET Framework.

ms733070.note(ru-ru,VS.100).gifПримечание
Значения имен параметров в сигнатуре операции являются частью контракта и чувствительны к регистру. Если требуется использовать одно имя параметра локально, но изменить имя опубликованных метаданных, см. раздел System.ServiceModel.MessageParameterAttribute.

Контракты данных

Ориентированные на службы приложения, например приложения Windows Communication Foundation (WCF), спроектированы так, чтобы взаимодействовать с наиболее широким рядом клиентских приложений как на платформе Microsoft, так и на других платформах. Для наиболее широких возможностей взаимодействия рекомендуется помечать типы атрибутами DataContractAttribute и DataMemberAttribute для создания контракта данных — части контракта службы, описывающей данные, которыми могут обмениваться операции службы.

Контракты данных — включаемые по требованию контракты стилей: тип или элемент данных не сериализуются, если не применен явным образом атрибут контракта данных. Контракты данных не связаны с областью доступа управляемого кода: закрытые элементы данных могут быть сериализованы и отправлены куда-либо для открытого доступа. (Простой пример контракта данных см. в разделе Как создать базовый контракт данных для класса или структуры.) WCF использует определение базовых сообщений SOAP, которые обеспечивают функциональность операций и сериализацию типов данных в тело сообщений и обратно. Если типы данных сериализуемы, нет необходимости думать об инфраструктуре обмена базовыми сообщениями при создании операций.

Хотя стандартное приложение WCF использует атрибуты DataContractAttribute и DataMemberAttribute для создания контрактов данных для операций, можно использовать другие механизмы сериализации. Стандартные механизмы ISerializable, SerializableAttribute и IXmlSerializable все обеспечивают сериализацию типов данных в базовые сообщения SOAP, которые переносят данные от одного приложения к другому. Если типы данных требуют специальной поддержки, можно применить дополнительные стратегии сериализации. Дополнительные сведения вариантах механизмов для сериализации типов данных в приложениях WCF см. разделе Задание передачи данных в контрактах служб.

Важно заметить, что имена CLR в определении контракта службы и ее операций важны и не должны иметь ошибок. Чтобы избежать ошибок в типах, использованных для определения контракта службы, используйте атрибуты ObfuscationAttribute и ObfuscateAssemblyAttribute.

Сопоставление параметров и возвращаемых значений с обменом сообщениями

Операции службы поддерживаются при помощи базового обмена сообщениями SOAP, в которых передаются данные приложения в двух направлениях, а также данные, необходимые приложению для поддержки стандартной безопасности, транзакций и функций, связанных с сеансами. Поэтому сигнатура операции службы диктует определенный базовый шаблон обмена сообщениями, который сможет поддерживать передачу данных и функциональность, необходимую для операции. В модели программирования WCF можно задать три шаблона: запрос-ответ, односторонний и дуплексный шаблон.

Запрос-ответ

Шаблон запрос-ответ означает, что отправитель запроса (клиентское приложение) получает ответ, который соответствует запросу. Это шаблон обмена сообщениями по умолчанию, так как он поддерживает как операцию передачи одного или более параметров операции, так и возвращение одного или более выходных значений от операции вызывавшей стороне. Например, следующий пример кода на языке C# показывает основную операцию службы, которая получает и возвращает строку.

[OperationContractAttribute]
string Hello(string greeting);

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute()>
Function Hello (ByVal greeting As String) As String

Сигнатура этой операции диктует вид базового обмена сообщениями. Если бы соответствия не было, платформа WCF не смогла бы определить, для какой операции предназначается возвращаемое значение.

Обратите внимание, что если не задать другой базовый шаблон сообщений, даже операции службы, которые возвращают void (Nothing в Visual Basic) обмениваются сообщениями по шаблону запрос-ответ. Результат для операции такой: если клиент не вызывает операцию асинхронно, клиент приостанавливает обработку до получения возвращаемого сообщения, даже несмотря на то, что в нормальном случае это сообщение пустое. Следующий пример кода на C# показывает операцию, которая не отвечает, пока клиент не получит пустое сообщение в ответ.

[OperationContractAttribute]
void Hello(string greeting);

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute()>
Sub Hello (ByVal greeting As String)

Приведенный пример кода может понизить производительность и скорость ответа клиента, если выполнение операции требует длительного времени, но у операций типа запрос-ответ есть преимущества, даже если они возвращают void. Наиболее очевидное состоит в том, что сообщения о неисправностях SOAP могут возвращаться в сообщении ответа, что будет указывать на возникновение какой-либо связанной со службой ошибки либо при подключении, либо при обработке. Неисправности SOAP, заданные в контракте службы, передаются клиентскому приложению в виде объекта FaultException, где параметр типа — это тип, заданный в контракте службы. Это обеспечивает простоту оповещения клиентов об ошибочных ситуациях в WCF. Дополнительные сведения исключениях, неисправностях SOAP и обработке ошибок см. в разделе Задание и обработка сбоев в контрактах и службах. Пример службы и клиента, взаимодействующих по шаблону запрос-ответ см. в разделе Как создать контракт типа «запрос-ответ». Дополнительные сведения работе с шаблоном запрос-ответ см. в разделе Request-Reply Services.

Односторонний

Если клиент приложения службы WCF не должен ждать завершения операции и не обрабатывает неисправности SOAP, для операции может быть определен односторонний шаблон обмена сообщениями. Односторонней операцией называется операция, которую клиент вызывает и продолжает работать, после того как WCF запишет сообщение в сеть. Обычно это означает, что клиент продолжает работу почти без перерыва, если объем данных, передаваемых в исходящем сообщение не очень велик, и если не возникает ошибка при передаче данных. Этот шаблон обмена сообщениями поддерживает событийное взаимодействие между клиентом и приложением службы.

Обмен сообщениями, при котором одно сообщение отправляется и ни одного не принимается не поддерживает операции службы, для которых задано возвращаемое значение, отличное от void; в таком случае возникает исключение InvalidOperationException.

Отсутствие обратного сообщения также означает, что не может быть возвращено сообщение о неисправностях SOAP, указывающее на ошибки при обработке или подключении. (Передача сведений об ошибках подключения для односторонних операций требует дуплексного шаблона обмена сообщениями.)

Чтобы задать односторонний обмен сообщениями для операции, возвращающей void, присвойте свойству IsOneWay значение true, как в следующем примере кода C#.

[OperationContractAttribute(IsOneWay=true)]
void Hello(string greeting);

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute(IsOneWay := True)>
Sub Hello (ByVal greeting As String)

Метод тот же, что и в предшествующем примере типа запрос-ответ, но значение true свойства IsOneWay означает, что несмотря на то, что метод тот же, операция службы не посылает ответное сообщение и клиенты продолжают работу немедленно после передачи исходящего сообщения на уровень канала. Пример см. в разделе Как создать односторонний контракт. Дополнительные сведения одностороннем шаблоне см. в разделе One-Way Services.

Дуплексный

Дуплексный шаблон характеризуется способностью и службы, и клиента отправлять сообщения друг другу, независимо от того, используется ли односторонний обмен сообщениями или запрос-ответ. Такая форма двустороннего общения полезна для служб, которым требуется напрямую обращаться к клиенту, или для асинхронного взаимодействия обеих сторон обмена сообщениями, в том числе для событийного поведения.

Дуплексный шаблон несколько более сложен, чем односторонний шаблон и шаблон запрос-ответ, так как имеет дополнительный механизм для взаимодействия с клиентом.

Для создания дуплексного контракта необходимо также создать контракт обратного вызова и присвоить тип этого контракта обратного вызова свойству CallbackContract атрибута ServiceContractAttribute контракта службы.

Для реализации дуплексного шаблона необходимо создать второй интерфейс, содержащий объявления методов, вызываемых на клиенте.

Пример создания службы и клиента, обращающегося к службе, см. в разделах Как создавать дуплексный контракт и Практическое руководство. Доступ к службам с дуплексным контрактом. Рабочий образец см. в разделе Дуплекс. Дополнительные сведения использовании дуплексных контрактов см. в разделе Дуплексные службы.

ms733070.Caution(ru-ru,VS.100).gifВнимание!
Когда служба получает дуплексное сообщение, она проверяет элемент ReplyTo входящего сообщения, чтобы определить, куда отправлять ответ. Если сообщение принимается по небезопасному каналу, ненадежный клиент может послать вредоносное сообщение с указанием компьютера для атаки в элементе ReplyTo, что приведет к отказу в обслуживании (DOS) этого компьютера.

Параметры Out и Ref

В большинстве случаев можно использовать параметры in (ByVal в Visual Basic) и out, а также параметры ref (ByRef в Visual Basic). Так как параметры out и ref оба указывают на то, что операция возвращает данные, сигнатура операции, такая, как показана далее, задает необходимость операции запрос-ответ, несмотря на то что сигнатура операции возвращает void.

[ServiceContractAttribute]
public interface IMyContract
{
  [OperationContractAttribute]
  public void PopulateData(ref CustomDataType data);
}

Ниже приведен эквивалентный код Visual Basic.

[Visual Basic]

<ServiceContractAttribute()> _
Public Interface IMyContract
  <OperationContractAttribute()> _
  Public Sub PopulateData(ByRef data As CustomDataType)
End Interface

Исключение составляют только те случаи, когда сигнатура имеет определенную структуру. Например, можно использовать привязку NetMsmqBinding для взаимодействия с клиентами, только если метод, использованный для объявления операции, возвращает void; может не быть выходного значения, является ли оно возвращаемым значением, параметром ref или out.

Помимо этого, использование параметров out или ref требует, чтобы у операции было базовое ответное сообщение для передачи обратно измененного объекта. Если операция является односторонней, во время выполнения создается исключение InvalidOperationException.

Определение уровня защиты сообщений для контракта

При создании контракта необходимо также определить уровень защиты сообщений служб, реализующих этот контракт. Это необходимо, только если к привязке применена безопасность сообщений в конечной точке контракта. Если для привязки безопасность отключена (то есть, если привязка, предоставляемая системой, присваивает объекту System.ServiceModel.SecurityMode значение System.ServiceModel.SecurityMode.None), то не нужно определять уровень безопасности сообщений для контракта. В большинстве случаев привязки, предоставляемые системой, с безопасностью на уровне сообщений предоставляют достаточный уровень защиты и нет необходимости рассматривать уровень защиты для каждой операции или для каждого сообщения.

Уровень защиты — это значение, которое определяет для сообщений (или частей сообщений), поддерживающих службу, подписываются ли они, подписываются и шифруются или отправляются без подписи и шифровки. Уровень защиты может быть задан в различных областях: на уровне службы, для конкретной операции, для сообщения операции или для части сообщения. Значения, заданные для более широкой области, становятся значениями по умолчанию для более узких областей, если не переопределить их явным образом. Если конфигурация привязки не может обеспечить минимальный необходимый уровень защиты для контракта, создается исключение. Если для контракта явно не задано значений уровня защиты, и если привязка имеет безопасность сообщений, уровнем защиты всех сообщений управляет конфигурация привязки. Это поведение установлено по умолчанию.

ms733070.Important(ru-ru,VS.100).gif Примечание
Решение явно задать для различных областей контракта уровень защиты меньше максимального System.Net.Security.ProtectionLevel.EncryptAndSign обычно является компромиссом между степенью защиты и улучшением производительности. В таких случаях решения должны опираться на вид операций и ценность передаваемых данных. Дополнительные сведения см. в разделе Защита служб.

Например, в следующем примере кода не задается ни свойствоProtectionLevel, ни свойство ProtectionLevel контракта.

[ServiceContract]
public interface ISampleService
{
  [OperationContractAttribute]
  public string GetString();

  [OperationContractAttribute]
  public int GetInt();  
}

Ниже приведен эквивалентный код Visual Basic.

[Visual Basic]

<ServiceContractAttribute()> _
Public Interface ISampleService

  <OperationContractAttribute()> _
  Public Function GetString()As String

  <OperationContractAttribute()> _
  Public Function GetData() As Integer

End Interface

При взаимодействии с реализацией ISampleService в конечной точке с привязкой WSHttpBinding по умолчанию (System.ServiceModel.SecurityMode по умолчанию, то есть значение Message), все сообщения зашифровываются и подписываются, так как это является уровнем защиты по умолчанию. Однако при использовании службы ISampleService с привязкой BasicHttpBinding по умолчанию (SecurityMode по умолчанию, то есть значение None), все сообщения отправляются в текстовом виде, так как эта привязка не имеет безопасности и, следовательно, уровень защиты игнорируется (то есть сообщения не шифруются и не подписываются). Если изменить значение свойства SecurityMode на Message, то сообщения будут зашифровываться и подписываться, так как теперь таков будет уровень защиты по умолчанию для привязки.

Если требуется явным образом задать или настроить требования по безопасности для контракта, задайте для свойства ProtectionLevel (или для любого из свойств ProtectionLevel более узкой области) уровень, требуемый для контракта службы. В данном случае явная настройка требует, чтобы привязка поддерживала эту настройку по крайней мере для указанной области. Например, в следующем примере кода явно задается одно значение ProtectionLevel для операции GetGuid.

[C#]

[ServiceContract]
public interface IExplicitProtectionLevelSampleService
{
  [OperationContractAttribute]
  public string GetString();

  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]
  public int GetInt();  
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
  public int GetGuid();  
}

Ниже приведен эквивалентный код Visual Basic.

[Visual Basic]

<ServiceContract()> _ 
Public Interface IExplicitProtectionLevelSampleService 
    <OperationContract()> _ 
    Public Function GetString() As String 
    End Function 

    <OperationContract(ProtectionLevel := ProtectionLevel.None)> _ 
    Public Function GetInt() As Integer 
    End Function 

    <OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _ 
    Public Function GetGuid() As Integer 
    End Function 

End Interface

Служба, реализующая этот контракт IExplicitProtectionLevelSampleService и имеющая конечную точку, где используется привязка WSHttpBinding по умолчанию (System.ServiceModel.SecurityMode по умолчанию, то есть значение Message) ведет себя следующим образом:

  • Для операции GetString сообщения шифруются и подписываются.

  • Для операции GetInt сообщения не шифруются и не подписываются, то есть отправляются как простой текст.

  • System.Guid операции GetGuid возвращается в зашифрованном подписанном сообщении.

Дополнительные сведения уровнях защиты и их использовании см. в разделе Основные сведения об уровне защиты. Дополнительные сведения безопасности см. в разделе Защита служб.

Другие требования к сигнатуре операции

Некоторые функции приложения требуют определенного вида сигнатуры операции. Например, привязка NetMsmqBinding поддерживает устойчивые службы и клиенты, позволяющие перезапуск приложения при установленном подключении, при этом приложение продолжит работу с того места, где остановилось, и ни одно сообщение не будет потеряно. (Дополнительные сведения см. в разделе Очереди в Windows Communication Foundation.) Однако устойчивые операции должны принимать только один параметр in и не должны иметь возвращаемого значения.

Другой пример — использование типов Stream в операциях. Так как параметр Stream включает в себя тело сообщения целиком, если входные или выходные данные (то есть параметр ref, параметр out или возвращаемое значение) принадлежат типу Stream, это должны быть единственные входные и выходные данные, заданные для операции. Кроме того, параметр или тип возвращаемых данных должны являться объектами Stream, System.ServiceModel.Channels.Message или System.Xml.Serialization.IXmlSerializable. Дополнительные сведения потоках см. в разделе Большие наборы данных и потоковая передача.

Имена, пространства имен и запутывание

Имена и пространства имен типов .NET в определениях контрактов и операций важны при преобразовании контрактов в WSDL и при создании и отправке сообщений контрактов. Поэтому рекомендуется явно задавать имена и пространства имен контракта службы с помощью свойств Name и Namespace всех поддерживающих атрибутов контракта, например ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttribute и других атрибутов контракта.

Одним из следствий этого является то, что если имена и пространства имен не заданы явно, применение запутывания IL для сборки изменяет имена типов и пространства имен контракта, что приводит к измененному коду WSDL и обмену сообщениями, который обычно завершается ошибкой. Если вы не задаете явно имена и пространства имен контракта, но планируете использовать запутывание, используйте атрибуты ObfuscationAttribute и ObfuscateAssemblyAttribute, чтобы предотвратить изменение имен и пространств имен контракта.

См. также

Задачи

Как создать контракт типа «запрос-ответ»
Как создать односторонний контракт
Как создавать дуплексный контракт

Основные понятия

Задание передачи данных в контрактах служб
Задание и обработка сбоев в контрактах и службах
Использование сеансов
Синхронные и асинхронные операции
Надежные службы
Службы и транзакции