Udostępnij za pośrednictwem


Wysyłanie według elementu treści

W przykładzie AdvancedDispatchByBody pokazano, jak zaimplementować alternatywny algorytm przypisywania komunikatów przychodzących do operacji.

Domyślnie dyspozytor modelu usługi wybiera odpowiednią metodę obsługi dla komunikatu przychodzącego na podstawie nagłówka "Akcja" komunikatu lub równoważnych informacji w żądaniu HTTP SOAP.

Niektóre stosy usług sieci Web protokołu SOAP 1.1, które nie są zgodne z wytycznymi WS-I Basic Profile 1.1, nie wysyłają komunikatów na podstawie identyfikatora URI akcji, ale raczej na podstawie kwalifikowanej nazwy XML pierwszego elementu wewnątrz treści protokołu SOAP. Podobnie strona klienta tych stosów może wysyłać komunikaty z pustym lub dowolnym nagłówkiem protokołu HTTP SoapAction, który był dozwolony przez specyfikację protokołu SOAP 1.1.

Aby zmienić sposób wysyłania komunikatów do metod, przykład implementuje IDispatchOperationSelector interfejs rozszerzalności w systemie DispatchByBodyElementOperationSelector. Ta klasa wybiera operacje na podstawie pierwszego elementu treści komunikatu.

Konstruktor klasy oczekuje słownika wypełnionego XmlQualifiedName parami i ciągami, gdzie nazwy kwalifikowane wskazują nazwę pierwszego podrzędnego treści protokołu SOAP, a ciągi wskazują zgodną nazwę operacji. Jest defaultOperationName to nazwa operacji, która odbiera wszystkie komunikaty, których nie można dopasować do tego słownika:

class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
    Dictionary<XmlQualifiedName, string> dispatchDictionary;
    string defaultOperationName;

    public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
    {
        this.dispatchDictionary = dispatchDictionary;
        this.defaultOperationName = defaultOperationName;
    }
}

IDispatchOperationSelector implementacje są bardzo proste do skompilowania, ponieważ w interfejsie istnieje tylko jedna metoda: SelectOperation. Zadaniem tej metody jest sprawdzenie komunikatu przychodzącego i zwrócenie ciągu, który jest taki sam jak nazwa metody w kontrakcie usługi dla bieżącego punktu końcowego.

W tym przykładzie selektor operacji uzyskuje element XmlDictionaryReader dla treści wiadomości przychodzącej przy użyciu polecenia GetReaderAtBodyContents. Ta metoda umieszcza już czytelnika w pierwszym elemencie podrzędnym treści komunikatu, dzięki czemu wystarczy uzyskać nazwę bieżącego elementu i identyfikator URI przestrzeni nazw, a następnie połączyć je w element XmlQualifiedName , który jest następnie używany do wyszukiwania odpowiedniej operacji z słownika przechowywanego przez selektor operacji.

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

Uzyskiwanie dostępu do treści komunikatu za pomocą GetReaderAtBodyContents dowolnej z innych metod zapewniających dostęp do treści wiadomości powoduje oznaczenie komunikatu jako "odczyt", co oznacza, że komunikat jest nieprawidłowy w przypadku dalszego przetwarzania. W związku z tym selektor operacji tworzy kopię komunikatu przychodzącego przy użyciu metody pokazanej w poniższym kodzie. Ponieważ pozycja czytelnika nie została zmieniona podczas inspekcji, może odwoływać się do nowo utworzonego komunikatu, do którego są również kopiowane właściwości wiadomości i nagłówki wiadomości, co powoduje dokładne sklonowanie oryginalnej wiadomości:

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

Dodawanie selektora operacji do usługi

Selektory operacji wysyłania usługi to rozszerzenia dyspozytora programu Windows Communication Foundation (WCF). W przypadku wybierania metod w kanale wywołania zwrotnego kontraktów dwukierunkowych istnieją również selektory operacji klienta, które działają bardzo podobnie jak selektory operacji wysyłania opisane tutaj, ale które nie są jawnie omówione w tym przykładzie.

Podobnie jak w przypadku większości rozszerzeń modelu usługi selektory operacji wysyłania są dodawane do dyspozytora przy użyciu zachowań. Zachowanie to obiekt konfiguracji, który dodaje jedno lub więcej rozszerzeń do środowiska uruchomieniowego wysyłania (lub do środowiska uruchomieniowego klienta) albo w inny sposób zmienia jego ustawienia.

Ponieważ selektory operacji mają zakres kontraktu, odpowiednie zachowanie do zaimplementowania w tym miejscu to IContractBehavior. Ponieważ interfejs jest implementowany w klasie pochodnej Attribute , jak pokazano w poniższym kodzie, zachowanie można deklaratywnie dodać do dowolnego kontraktu usługi. Za każdym razem, gdy środowisko ServiceHost uruchomieniowe wysyłania jest tworzone, wszystkie zachowania znalezione jako atrybuty kontraktów, operacji i implementacji usług lub jako element w konfiguracji usługi są automatycznie dodawane, a następnie monitowane o współtworzenie rozszerzeń lub modyfikowanie konfiguracji domyślnej.

W przypadku zwięzłości poniższy fragment kodu pokazuje tylko implementację metody ApplyDispatchBehavior, która wpływa na zmiany konfiguracji dyspozytora w tym przykładzie. Inne metody nie są wyświetlane, ponieważ wracają do wywołującego bez wykonywania żadnej pracy.

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

Najpierw implementacja ApplyDispatchBehavior konfiguruje słownik odnośników dla selektora operacji, iterując OperationDescription elementy w punkcie końcowym ContractDescriptionusługi . Następnie każdy opis operacji jest sprawdzany pod kątem obecności DispatchBodyElementAttribute zachowania, implementacji IOperationBehavior , która jest również zdefiniowana w tym przykładzie. Chociaż ta klasa jest również zachowaniem, jest pasywna i nie aktywnie przyczynia się do żadnych zmian konfiguracji w środowisku uruchomieniowym wysyłania. Wszystkie jego metody wracają do obiektu wywołującego bez wykonywania żadnych akcji. Zachowanie operacji istnieje tylko tak, aby metadane wymagane dla nowego mechanizmu wysyłania, a mianowicie kwalifikowana nazwa elementu treści, na którym wybrano wystąpienie operacji, można skojarzyć z odpowiednimi operacjami.

Jeśli takie zachowanie zostanie znalezione, do słownika zostanie dodana para wartości utworzona na podstawie kwalifikowanej nazwy XML (QName właściwości), a nazwa operacji (Name właściwość).

Po wypełnieniu słownika zostanie utworzony nowy DispatchByBodyElementOperationSelector element zawierający te informacje i ustawiony jako selektor operacji środowiska uruchomieniowego wysyłania:

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

Implementowanie usługi

Zachowanie zaimplementowane w tym przykładzie bezpośrednio wpływa na sposób interpretowania i wysyłania komunikatów z przewodu, co jest funkcją kontraktu usługi. W związku z tym zachowanie powinno zostać zadeklarowane na poziomie kontraktu usługi w dowolnej implementacji usługi, która wybierze jej użycie.

Przykładowy usługa projektów stosuje zachowanie kontraktu DispatchByBodyElementBehaviorAttribute do kontraktu IDispatchedByBody usługi i oznacza każde z dwóch operacji OperationForBodyA() oraz OperationForBodyB() zachowanie DispatchBodyElementAttribute operacji. Po otwarciu hosta usługi dla usługi, która implementuje ten kontrakt, te metadane są pobierane przez konstruktora dyspozytora zgodnie z wcześniejszym opisem.

Ponieważ selektor operacji wysyła wyłącznie na podstawie elementu treści komunikatu i ignoruje wartość "Akcja", należy poinformować środowisko uruchomieniowe, aby nie sprawdzać nagłówka "Akcja" w zwróconych odpowiedziach, przypisując symbol wieloznaczny "*" do ReplyAction właściwości OperationContractAttribute. Ponadto wymagana jest domyślna operacja, która ma właściwość "Action" ustawioną na symbol wieloznaczny "*". Domyślna operacja odbiera wszystkie komunikaty, których nie można wysłać i nie ma elementu 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);
}

Przykładowa implementacja usługi jest prosta. Każda metoda opakowuje odebraną wiadomość do wiadomości odpowiedzi i zwraca ją z powrotem do klienta.

Uruchamianie i kompilowanie przykładu

Po uruchomieniu przykładu zawartość treści odpowiedzi operacji jest wyświetlana w oknie konsoli klienta podobnym do następujących (sformatowanych) danych wyjściowych.

Klient wysyła do usługi trzy komunikaty, których element zawartości treści ma nazwę bodyA, bodyBi bodyX, odpowiednio. Jak można odroczyć od poprzedniego opisu i pokazany kontrakt usługi, przychodzący komunikat z elementem bodyAOperationForBodyA() jest wysyłany do metody. Ponieważ nie ma jawnego elementu docelowego wysyłania komunikatu z elementem bodyX treści, komunikat jest wysyłany do elementu DefaultOperation(). Każda operacja usługi opakowuje treść odebranego komunikatu do elementu specyficznego dla metody i zwraca ją, co jest wykonywane w celu korelowania komunikatów wejściowych i wyjściowych wyraźnie dla tego przykładu:

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

Aby skonfigurować, skompilować i uruchomić przykład

  1. Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.

  2. Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).

  3. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.