Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В примере AdvancedDispatchByBody показано, как реализовать альтернативный алгоритм назначения входящих сообщений операциям.
По умолчанию диспетчер модели службы выбирает соответствующий метод обработки для входящего сообщения на основе заголовка WS-Addressing "Action" сообщения или эквивалентной информации в HTTP-запросе SOAP.
Некоторые стеки веб-служб SOAP 1.1, которые не следуют рекомендациям по базовым профилям WS-I версии 1.1, отправляют сообщения не на основе URI действия, а на основе XML-имени первого элемента в теле SOAP. Аналогичным образом, клиентская сторона этих стеков может отправлять сообщения с пустым или произвольным заголовком HTTP SoapAction, который был разрешен спецификацией SOAP 1.1.
Чтобы изменить способ отправки сообщений на методы, пример реализует IDispatchOperationSelector интерфейс расширяемости для объекта DispatchByBodyElementOperationSelector
. Этот класс выбирает операции на основе первого элемента текста сообщения.
Конструктор класса ожидает словарь, заполненный парами XmlQualifiedName
и строками, в которых полные имена указывают на имя первого дочернего элемента тела SOAP, а строки указывают соответствующее имя операции. Название defaultOperationName
операции, получающей все сообщения, которые невозможно сопоставить с этим словарем:
class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
Dictionary<XmlQualifiedName, string> dispatchDictionary;
string defaultOperationName;
public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
{
this.dispatchDictionary = dispatchDictionary;
this.defaultOperationName = defaultOperationName;
}
}
IDispatchOperationSelectorреализации очень просты в сборке, так как в интерфейсе существует только один метод: SelectOperation Задание этого метода заключается в проверке входящего сообщения и возврате строки, равной имени метода в контракте службы для текущей конечной точки.
В этом примере селектор операций получает XmlDictionaryReader текст входящего сообщения с помощью GetReaderAtBodyContents. Этот метод уже устанавливает позицию парсера на первый дочерний элемент текста сообщения, что позволяет получить имя и URI пространства имен текущего элемента и объединить их в XmlQualifiedName
объект, который затем используется для поиска соответствующей операции из словаря, содержащегося селектором операций.
public string SelectOperation(ref System.ServiceModel.Channels.Message message)
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
XmlQualifiedName lookupQName = new
XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);
message = CreateMessageCopy(message,bodyReader);
if (dispatchDictionary.ContainsKey(lookupQName))
{
return dispatchDictionary[lookupQName];
}
else
{
return defaultOperationName;
}
}
Доступ к тексту сообщения с помощью GetReaderAtBodyContents или любого другого метода, обеспечивающего доступ к содержимому текста сообщения, приводит к тому, что сообщение помечается как "прочитано", что означает, что сообщение непригодно для дальнейшей обработки. Поэтому селектор операций создает копию входящего сообщения с методом, показанным в следующем коде. Поскольку позиция читателя не была изменена во время проверки, на нее можно ссылаться в только что созданном сообщении, в который копируются свойства сообщения и заголовки сообщения, что приводит к точному клону исходного сообщения.
private Message CreateMessageCopy(Message message,
XmlDictionaryReader body)
{
Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);
copy.Headers.CopyHeaderFrom(message,0);
copy.Properties.CopyProperties(message.Properties);
return copy;
}
Добавление селектора операций в службу
Селекторы операций диспетчера служб — это расширения диспетчера Windows Communication Foundation (WCF). Для выбора методов в канале обратного вызова дуплексных контрактов существуют также селекторы операций клиента, которые работают точно так же, как селекторы операций диспетчеризации, описанные здесь, но которые не рассматриваются подробно в этом примере.
Как и большинство расширений модели служб, селекторы операций отправки добавляются в диспетчер при помощи поведения. Поведение — это объект конфигурации, который добавляет одно или несколько расширений в среду выполнения отправки (или в клиентскую среду выполнения) или изменяет его параметры.
Так как селекторы операций имеют область контракта, соответствующим поведением, которое следует реализовать здесь, является IContractBehavior. Так как интерфейс реализуется в Attribute производном классе, как показано в следующем коде, поведение может быть декларативно добавлено в любой контракт службы. Всякий раз, когда ServiceHost открыт и построена среда выполнения отправки, все действия, обнаруженные как атрибуты для контрактов, операций и реализаций служб или как элементы в конфигурации услуги, автоматически добавляются, после чего их просят внести дополнения или изменить конфигурацию по умолчанию.
Для краткости в следующем фрагменте кода показана только реализация метода ApplyDispatchBehavior, которая влияет на изменения конфигурации диспетчера в этом примере. Другие методы не отображаются, так как они возвращаются к вызывающему, не выполняя никаких действий.
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior
{
// public void AddBindingParameters(...)
// public void ApplyClientBehavior(...)
// public void Validate(...)
Во-первых, реализация настраивает словарь поиска для селектора операций путем итерации элементов в конечной точке службы. Затем каждое описание операции проверяется на наличие поведения DispatchBodyElementAttribute
, реализации IOperationBehavior, которая также определена в этом примере. Хотя этот класс также является поведением, оно пассивно и не активно вносит изменения конфигурации во время выполнения диспетчеризации. Все его методы возвращаются вызывающему объекту без каких-либо действий. Поведение операции существует для того, чтобы метаданные, необходимые для нового механизма диспетчеризации, а именно квалифицированное имя элемента body, при наступлении которого выбирается операция, могли быть связаны с соответствующими операциями.
Если такое поведение найдено, в словарь добавляется пара значений, созданная из квалифицированного имени XML (QName
свойства) и имени операции (Name
свойства).
После заполнения словаря создается новый DispatchByBodyElementOperationSelector
с этой информацией и устанавливается в качестве селектора операций среды выполнения диспетчера:
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
Dictionary<XmlQualifiedName,string> dispatchDictionary =
new Dictionary<XmlQualifiedName,string>();
foreach( OperationDescription operationDescription in
contractDescription.Operations )
{
DispatchBodyElementAttribute dispatchBodyElement =
operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();
if ( dispatchBodyElement != null )
{
dispatchDictionary.Add(dispatchBodyElement.QName,
operationDescription.Name);
}
}
dispatchRuntime.OperationSelector =
new DispatchByBodyElementOperationSelector(
dispatchDictionary,
dispatchRuntime.UnhandledDispatchOperation.Name);
}
}
Реализация службы
Поведение, реализованное в этом примере, напрямую влияет на интерпретацию и отправку сообщений по сети, что является функцией контракта обслуживания. Следовательно, поведение должно быть объявлено на уровне контракта службы в любой реализации, которая решает его использовать.
Пример службы проекта применяет DispatchByBodyElementBehaviorAttribute
поведение контракта к IDispatchedByBody
контракту службы и помечает каждую из двух операций OperationForBodyA()
и OperationForBodyB()
поведением операции DispatchBodyElementAttribute
. При открытии хоста службы, реализующей этот контракт, диспетчер-сборщик собирает эти метаданные, как описано ранее.
Поскольку селектор операций выбирает исключительно на основе элемента тела сообщения и игнорирует "Action", необходимо указать среде выполнения не проверять заголовок "Action" в возвращаемых ответах, назначив подстановочный знак "*" свойству ReplyAction
OperationContractAttribute. Кроме того, необходимо иметь операцию по умолчанию, которая имеет свойство Action, заданное подстановочным знаком "*". Операция по умолчанию получает все сообщения, которые не могут быть обработаны и не имеют DispatchBodyElementAttribute
.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),
DispatchByBodyElementBehavior]
public interface IDispatchedByBody
{
[OperationContract(ReplyAction="*"),
DispatchBodyElement("bodyA","http://tempuri.org")]
Message OperationForBodyA(Message msg);
[OperationContract(ReplyAction = "*"),
DispatchBodyElement("bodyB", "http://tempuri.org")]
Message OperationForBodyB(Message msg);
[OperationContract(Action="*", ReplyAction="*")]
Message DefaultOperation(Message msg);
}
Пример реализации службы прост. Каждый метод упаковывает полученное сообщение в ответное сообщение и повторяет его обратно клиенту.
Запуск и сборка примера
При запуске примера содержимое ответов операции отображается в окне консоли клиента, подобно следующему (отформатированному) выводу.
Клиент отправляет три сообщения в службу, элемент содержимого тела которого называется bodyA
, bodyB
и bodyX
соответственно. Как можно сделать вывод из предыдущего описания и договора на оказание услуги, входящее сообщение с элементом bodyA
отправляется в метод OperationForBodyA()
. Так как для сообщения с элементом bodyX
body отсутствует явный целевой объект отправки, сообщение отправляется в DefaultOperation()
. Каждая из операций службы упаковывает текст полученного сообщения в элемент, характерный для метода, и возвращает его, что позволяет однозначно сопоставить входящие и исходящие сообщения в этом примере.
<?xml version="1.0" encoding="IBM437"?>
<replyBodyA xmlns="http://tempuri.org">
<q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>
</replyBodyA>
<?xml version="1.0" encoding="IBM437"?>
<replyBodyB xmlns="http://tempuri.org">
<q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>
</replyBodyB>
<?xml version="1.0" encoding="IBM437"?>
<replyDefault xmlns="http://tempuri.org">
<q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>
</replyDefault>
Настройка, сборка и запуск примера
Убедитесь, что вы выполнили процедуру настройки One-Time для образцов Windows Communication Foundation.
Чтобы создать решение, следуйте инструкциям по созданию примеров Windows Communication Foundation.
Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в запуска примеров Windows Communication Foundation.