Передача по элементу тела сообщенияDispatch by Body Element

В образце демонстрируется реализация альтернативного алгоритма для присвоения операциям входящих сообщений.This sample demonstrates how to implement an alternate algorithm for assigning incoming messages to operations.

По умолчанию диспетчер модели службы выбирает соответствующий метод обработки для входящего сообщения, основанный на заголовке "Действие" механизма WS-Addressing сообщения или аналогичной информации запроса HTTP SOAP.By default, the service model dispatcher selects the appropriate handling method for an incoming message based on the message's WS-Addressing "Action" header or the equivalent information in the HTTP SOAP request.

Некоторые стеки веб-служб SOAP 1.1 не соответствуют рекомендациям WS-I Basic Profile 1.1, поэтому не распределяют сообщения, основываясь не на URI действия, а на полном XML-имени первого элемента внутри тела SOAP.Some SOAP 1.1 Web services stacks that do not follow the WS-I Basic Profile 1.1 guidelines do not dispatch messages based on the Action URI, but rather based on the XML qualified name of the first element inside the SOAP body. Аналогично, клиентская сторона этих стеков может отправлять сообщения с пустым или произвольным заголовком HTTP SoapAction, который разрешен спецификацией SOAP 1.1.Likewise, the client side of these stacks might send messages with an empty or arbitrary HTTP SoapAction header, which was permitted by the SOAP 1.1 specification.

Чтобы изменить способ отправки сообщений методам, образец реализует интерфейс расширения IDispatchOperationSelector для события DispatchByBodyElementOperationSelector.To change the way messages are dispatched to methods, the sample implements the IDispatchOperationSelector extensibility interface on the DispatchByBodyElementOperationSelector. Этот класс выбирает операции, основываясь на первом элементе тела сообщения.This class selects operations based on the first element of the message body.

Конструктор класса ожидает словарь, который заполняется парами XmlQualifiedName и строками, в котором полные имена указывают имя первого дочернего элемента тела SOAP, а строки указывают на имя соответствующей операции.The class constructor expects a dictionary populated with pairs of XmlQualifiedName and strings, whereby the qualified names indicate the name of the first child of the SOAP body and the strings indicate the matching operation name. defaultOperationName - это имя операции, которая принимает все сообщения, которые не были найдены в этом словаре.The defaultOperationName is the name of the operation that receives all messages that cannot be matched against this dictionary:

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.IDispatchOperationSelector implementations are very straightforward to build as there is only one method on the interface: SelectOperation. Этот метод отвечает за проверку входящих сообщений и возврат строки, которая равна имени метода контракта службы для текущей конечной точки.The job of this method is to inspect an incoming message and to return a string that equals the name of a method on the service contract for the current endpoint.

В этом образце селектор операций приобретает класс XmlDictionaryReader для тела входящего сообщения, используя метод GetReaderAtBodyContents.In this sample, the operation selector acquires an XmlDictionaryReader for the incoming message's body using GetReaderAtBodyContents. Этот метод уже расположил средство чтения на первом дочернем элементе тела сообщения, поэтому достаточно получить имя текущего элемента, универсальный код ресурса (URI) пространства имен и объединить их в XmlQualifiedName, чтобы затем использовать для поиска соответствующей операции в словаре, хранящемся в селекторе операций.This method already positions the reader on the first child of the message's body so that it is sufficient to get the current element's name and namespace URI and combine them into an XmlQualifiedName that is then used for looking up the corresponding operation from the dictionary held by the operation selector.

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 или любого другого метода, который обеспечивает доступ к содержимому тела сообщения, приводит к тому, что сообщение помечается как "read", то есть дальнейшая обработка сообщения является недопустимой.Accessing the message body with GetReaderAtBodyContents or any of the other methods that provide access to the message's body content causes the message to be marked as "read", which means that the message is invalid for any further processing. Поэтому селектор операций создает копию входящего сообщения с помощью метода, который показан в следующем коде.Therefore, the operation selector creates a copy of the incoming message with the method shown in the following code. Поскольку позиция средства чтения не изменялась во время проверки, новое созданное сообщение может ссылаться на нее. В это сообщение также копируются свойства и заголовки сообщения, результаты которого являются точной копией исходного сообщения.Because the reader's position has not been changed during the inspection, it can be referenced by the newly created message to which the message properties and the message headers are also copied, which results in an exact clone of the original message:

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;  
}  

Добавление селектора операции в службуAdding an Operation Selector to a Service

Селекторы операций диспетчеризации службы являются расширениями диспетчера Windows Communication Foundation (WCF).Service dispatch operation selectors are extensions to the Windows Communication Foundation (WCF) dispatcher. Для выбранных методов на канале обратного вызова дуплексных контрактов также есть селекторы операций клиента, которые работают схоже с описанными здесь селекторами операции отправки, но которые не представлены в этом образце в явном виде.For selecting methods on the callback channel of duplex contracts, there are also client operation selectors, which work very much like the dispatch operation selectors described here, but which are not explicitly covered in this sample.

Подобно большинству расширений моделей служб, селекторы операций отправки добавляются диспетчеру с использованием поведения.Like most service model extensions, dispatch operation selectors are added to the dispatcher using behaviors. Поведение — это объект конфигурации, который либо добавляет одно или несколько расширений в среду выполнения диспетчеризации (или в клиентскую среду выполнения), либо изменяет его параметры.A behavior is a configuration object, which either adds one or more extensions to the dispatch runtime (or to the client runtime) or otherwise changes its settings.

Поскольку селекторы операций имеют область контрактов, реализуется поведение IContractBehavior.Because operation selectors have contract scope, the appropriate behavior to implement here is the IContractBehavior. Поскольку интерфейс реализован для производного класса Attribute, как показано в коде, поведение может быть добавлено декларативно в любой контракт службы.Because the interface is implemented on a Attribute derived class as shown in the following code, the behavior can be declaratively added to any service contract. Если класс ServiceHost уже открыт и среда выполнения распределения построена, все поведение, обнаруженное в качестве атрибутов в контрактах, операциях и реализациях служб или в качестве элемента в конфигурации службы, автоматически добавляется и впоследствии запрашивается для участия в расширениях или изменении конфигурации по умолчанию.Whenever a ServiceHost is opened and the dispatch runtime is built, all behaviors found either as attributes on contracts, operations, and service implementations or as element in the service configuration are automatically added and subsequently asked to contribute extensions or modify the default configuration.

Для краткости следующий фрагмент кода показывает только реализацию метода ApplyDispatchBehavior, который оказывает результирующее воздействие на изменение конфигурации для диспетчера в этом образце.For brevity, the following code excerpt only shows the implementation of the method ApplyDispatchBehavior, which effects the configuration changes for the dispatcher in this sample. Другие методы не показаны, поскольку они возвращаются вызывающему объекту без выполнения какой-либо работы.The other methods are not shown because they return to the caller without doing any work.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]  
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior  
{  
    // public void AddBindingParameters(...)
    // public void ApplyClientBehavior(...)  
    // public void Validate(...)  

Реализация метода ApplyDispatchBehavior настраивает словарь поиска для селектора операций путем итераций среди элементов класса OperationDescription в конечной точке службы ContractDescription.First, the ApplyDispatchBehavior implementation sets up the lookup dictionary for the operation selector by iterating over the OperationDescription elements in the service endpoint's ContractDescription. Затем каждое описание операции проверяется на присутствие поведения DispatchBodyElementAttribute и реализации интерфейса IOperationBehavior, который тоже определен в этом образце.Then, each operation description is inspected for the presence of the DispatchBodyElementAttribute behavior, an implementation of IOperationBehavior that is also defined in this sample. До тех пор пока этот класс является поведением, он пассивен и не принимает активного участия в изменении конфигурации среды времени выполнения.While this class is also a behavior, it is passive and does not actively contribute any configuration changes to the dispatch runtime. Любой из его методов возвращается вызывающему объекту без выполнения каких-либо действий.All of its methods return to the caller without taking any actions. Существует только поведение операции; в результате могут быть ассоциированы с соответствующими операциями только метаданные, необходимые для нового механизма диспетчеризации - полное имя элемента тела, на котором выбрано вхождение операции.The operation behavior only exists so that the metadata required for the new dispatch mechanism, namely the qualified name of the body element on whose occurrence an operation is selected, can be associated with the respective operations.

Если такое поведение найдено, создается пара значений "полное XML-имя" (свойство QName) и "имя операции" (свойство Name), которая добавляется в словарь.If such a behavior is found, a value pair created from the XML qualified name (QName property) and the operation name (Name property) is added to the dictionary.

Как только словарь заполняется, с использованием этой информации строится новый селектор DispatchByBodyElementOperationSelector и задается как селектор операций для данной среды выполнения распределения.Once the dictionary is populated, a new DispatchByBodyElementOperationSelector is constructed with this information and set as the operation selector of the dispatch runtime:

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);  
    }  
}  

Реализация службыImplementing the Service

Реализованное в этом образце поведение непосредственно влияет на то, как сообщения из канала связи интерпретируется и отправляется, что является функцией контракта службы.The behavior implemented in this sample directly affects how messages from the wire are interpreted and dispatched, which is a function of the service contract. В результате поведение должно быть объявлено на уровне контракта службы в реализации любой службы, которая выбирает его для своего использования.Consequently, the behavior should be declared on the service contract level in any service implementation that chooses to use it.

Пример службы проекта применяет DispatchByBodyElementBehaviorAttribute поведение контракта к IDispatchedByBody контракту службы и помечает каждую из этих операций OperationForBodyA() и OperationForBodyB() DispatchBodyElementAttribute поведение операции.The sample project service applies the DispatchByBodyElementBehaviorAttribute contract behavior to the IDispatchedByBody service contract and labels each of the two operations OperationForBodyA() and OperationForBodyB() with a DispatchBodyElementAttribute operation behavior. Когда открыт узел службы, который реализует этот контракт, эти метаданные используются конструктором диспетчера в соответствии с приведенным описанием.When a service host for a service that implements this contract is opened, this metadata is picked up by the dispatcher builder as previously described.

Поскольку селектор операции при направлении принимает во внимание только элемент тела сообщений и игнорирует заголовок "Действие", требуется указать среде выполнения не проверять заголовок "Действие" возвращаемых ответов путем присвоения подстановочного знака "*" свойству ReplyAction класса OperationContractAttribute.Because the operation selector dispatches solely based on the message body element and ignores the "Action", it is required to tell the runtime not to check the "Action" header on the returned replies by assigning the wildcard "*" to the ReplyAction property of OperationContractAttribute. Кроме того, необходимо иметь операцию по умолчанию, для которой свойству Action задано значение подстановочного знака " * ".Furthermore, it is required to have a default operation that has the "Action" property set to the wildcard "*". Операция по умолчанию получает все сообщения, которые не могут быть отправлены и не имеют DispatchBodyElementAttribute.The default operation receives all messages which cannot be dispatched and does not have a 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);  
}  

Реализация образца службы очень проста.The sample service implementation is straightforward. Каждый метод упаковывает принятое сообщение в сообщение ответа и возвращает его обратно клиенту.Every method wraps the received message into a reply message and echoes it back to the client.

Построение и запуск образцаRunning and Building the Sample

При выполнении образца содержимое тела ответов операции отображается в окне консоли клиента аналогично приведенному (форматированному) результату работы.When you run the sample, the body content of the operation responses are displayed in the client console window similar to the following (formatted) output.

Клиент отправляет службе три сообщения с элементами содержимого тела bodyA, bodyB и bodyX соответственно.The client sends three messages to the service whose body content element is named bodyA, bodyB, and bodyX, respectively. Как отложилось из предыдущего описания и как показывает контракт службы, входящее сообщение с элементом bodyA отправляется методу OperationForBodyA().As can be deferred from the previous description and the service contract shown, the incoming message with the bodyA element is dispatched to the OperationForBodyA() method. Поскольку отсутствует явный целевой объект, чтобы определить сообщение с телом элемента bodyX, сообщение отправляется операции DefaultOperation().Because there is no explicit dispatch target for the message with the bodyX body element, the message is dispatched to the DefaultOperation(). Каждая из операций службы упаковывает полученное тело сообщения в элемент для данного метода и возвращает его. Это сделано для четкой корреляции входных и сообщений для этого образца.Each of the service operations wraps the received message body into an element specific to the method and returns it, which is done to correlate input and output messages clearly for this sample:

<?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>  

Настройка, сборка и выполнение образцаTo set up, build, and run the sample

  1. Убедитесь, что вы выполнили однократную процедуру настройки для Windows Communication Foundation примеров.Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. Чтобы выполнить сборку решения, следуйте инструкциям в разделе Создание примеров Windows Communication Foundation.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе выполнение примеров Windows Communication Foundation.To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

Важно!

Образцы уже могут быть установлены на компьютере.The samples may already be installed on your machine. Перед продолжением проверьте следующий каталог (по умолчанию).Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Если этот каталог не существует, перейдите к примерам Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для платформа .NET Framework 4 , чтобы скачать все Windows Communication Foundation (WCF) и WFWF примеры.If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. Этот образец расположен в следующем каталоге.This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Interop\AdvancedDispatchByBody