Delen via


Verzending per hoofdtekstelement

Het AdvancedDispatchByBody-voorbeeld laat zien hoe u een alternatief algoritme implementeert voor het toewijzen van binnenkomende berichten aan bewerkingen.

Standaard selecteert de verzender van het servicemodel de juiste afhandelingsmethode voor een binnenkomend bericht op basis van de WS-Adresseringskoptekst 'Actie' van het bericht of de equivalente informatie in de HTTP SOAP-aanvraag.

Sommige SOAP 1.1-webservicesstacks die niet voldoen aan de WS-I Basic Profile 1.1-richtlijnen verzenden geen berichten op basis van de actie-URI, maar eerder op basis van de XML-gekwalificeerde naam van het eerste element in de SOAP-hoofdtekst. Op dezelfde manier kan de clientzijde van deze stacks berichten verzenden met een lege of willekeurige HTTP SoapAction-header, die is toegestaan door de SOAP 1.1-specificatie.

Als u de manier wilt wijzigen waarop berichten naar methoden worden verzonden, implementeert het voorbeeld de IDispatchOperationSelector uitbreidbaarheidsinterface op de DispatchByBodyElementOperationSelector. Deze klasse selecteert bewerkingen op basis van het eerste element van de berichttekst.

De klasseconstructor verwacht dat een woordenlijst is gevuld met paren XmlQualifiedName en tekenreeksen, waarbij de gekwalificeerde namen de naam van het eerste onderliggende element van de SOAP-hoofdtekst aangeven en de tekenreeksen de naam van de overeenkomende bewerking aangeven. Dit defaultOperationName is de naam van de bewerking die alle berichten ontvangt die niet kunnen worden vergeleken met deze woordenlijst:

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

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

IDispatchOperationSelector implementaties zijn zeer eenvoudig te bouwen omdat er slechts één methode op de interface is: SelectOperation. De taak van deze methode is om een binnenkomend bericht te inspecteren en een tekenreeks te retourneren die gelijk is aan de naam van een methode in het servicecontract voor het huidige eindpunt.

In dit voorbeeld verkrijgt de bewerkingskiezer een XmlDictionaryReader voor de hoofdtekst van het binnenkomende bericht met behulp van GetReaderAtBodyContents. Met deze methode wordt de lezer al op het eerste onderliggende element van de hoofdtekst van het bericht geplaatst, zodat het voldoende is om de naam en naamruimte-URI van het huidige element op te halen en deze te combineren in een XmlQualifiedName die vervolgens wordt gebruikt voor het opzoeken van de bijbehorende bewerking uit de woordenlijst die door de bewerkingskiezer wordt bewaard.

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

Als u de berichttekst opent met GetReaderAtBodyContents of een van de andere methoden die toegang bieden tot de hoofdtekst van het bericht, wordt het bericht gemarkeerd als 'lezen', wat betekent dat het bericht ongeldig is voor verdere verwerking. Daarom maakt de bewerkingskiezer een kopie van het binnenkomende bericht met de methode die wordt weergegeven in de volgende code. Omdat de positie van de lezer niet is gewijzigd tijdens de inspectie, kan er naar worden verwezen door het zojuist gemaakte bericht waarnaar de berichteigenschappen en de berichtkoppen ook worden gekopieerd, wat resulteert in een exacte kloon van het oorspronkelijke bericht:

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

Een bewerkingskiezer toevoegen aan een service

Selectors voor serviceverzendingsbewerkingen zijn extensies voor de WCF-dispatcher (Windows Communication Foundation). Voor het selecteren van methoden voor het callback-kanaal van duplexcontracten zijn er ook clientbewerkingkiezers, die zeer vergelijkbaar zijn met de hier beschreven verzendbewerkingskiezers, maar die niet expliciet in dit voorbeeld worden behandeld.

Net als bij de meeste servicemodelextensies worden verzendbewerkingkiezers aan de dispatcher toegevoegd met behulp van gedrag. Een gedrag is een configuratieobject, waarmee een of meer extensies worden toegevoegd aan de dispatch-runtime (of aan de clientruntime) of de instellingen op een andere manier worden gewijzigd.

Omdat bewerkingskiezers een contractbereik hebben, is het juiste gedrag om hier te implementeren de IContractBehavior. Omdat de interface is geïmplementeerd op een Attribute afgeleide klasse, zoals wordt weergegeven in de volgende code, kan het gedrag declaratief worden toegevoegd aan elk servicecontract. Wanneer een wordt ServiceHost geopend en de dispatch-runtime wordt gebouwd, worden alle gedragingen die worden gevonden als kenmerken voor contracten, bewerkingen en service-implementaties of als element in de serviceconfiguratie automatisch toegevoegd en vervolgens gevraagd om extensies bij te dragen of de standaardconfiguratie te wijzigen.

Ter beknoptheid toont het volgende codefragment alleen de implementatie van de methode ApplyDispatchBehavior, die van invloed is op de configuratiewijzigingen voor de dispatcher in dit voorbeeld. De andere methoden worden niet weergegeven omdat ze zonder werk terugkeren naar de aanroeper.

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

Eerst stelt de ApplyDispatchBehavior implementatie de opzoekwoordenlijst voor de bewerkingskiezer in door de elementen in het OperationDescription service-eindpunt ContractDescriptionte doorlopen. Vervolgens wordt elke beschrijving van de bewerking gecontroleerd op de aanwezigheid van het DispatchBodyElementAttribute gedrag, een implementatie hiervan IOperationBehavior die ook in dit voorbeeld wordt gedefinieerd. Hoewel deze klasse ook een gedrag is, is het passief en draagt deze niet actief bij aan configuratiewijzigingen in de dispatch-runtime. Alle methoden keren terug naar de aanroeper zonder acties uit te voeren. Het bewerkingsgedrag bestaat alleen zodat de metagegevens die vereist zijn voor het nieuwe verzendmechanisme, namelijk de gekwalificeerde naam van het hoofdelement waarop een bewerking is geselecteerd, kunnen worden gekoppeld aan de respectieve bewerkingen.

Als een dergelijk gedrag wordt gevonden, wordt een waardepaar dat is gemaakt op basis van de gekwalificeerde XML-naam (QName eigenschap) en de naam van de bewerking (Name eigenschap) toegevoegd aan de woordenlijst.

Zodra de woordenlijst is ingevuld, wordt er een nieuwe DispatchByBodyElementOperationSelector samengesteld met deze informatie en ingesteld als de bewerkingsselector van de dispatchruntime:

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

De service implementeren

Het gedrag dat in dit voorbeeld wordt geïmplementeerd, is rechtstreeks van invloed op de manier waarop berichten van de kabel worden geïnterpreteerd en verzonden. Dit is een functie van het servicecontract. Daarom moet het gedrag worden gedeclareerd op het servicecontractniveau in elke service-implementatie die ervoor kiest om het te gebruiken.

Het voorbeeld projectservice past het DispatchByBodyElementBehaviorAttribute contractgedrag toe op het IDispatchedByBody servicecontract en labelt elk van de twee bewerkingen OperationForBodyA() en OperationForBodyB() met een DispatchBodyElementAttribute bewerkingsgedrag. Wanneer een servicehost voor een service die dit contract implementeert, wordt geopend, worden deze metagegevens opgehaald door de dispatcherbouwer zoals eerder is beschreven.

Omdat de bewerkingskiezer alleen wordt verzonden op basis van het berichttekstelement en de actie negeert, moet de runtime de header Actie niet controleren op de geretourneerde antwoorden door het jokerteken '*' toe te wijzen aan de ReplyAction eigenschap van OperationContractAttribute. Bovendien is het vereist om een standaardbewerking te hebben waarvoor de eigenschap 'Actie' is ingesteld op het jokerteken '*'. De standaardbewerking ontvangt alle berichten die niet kunnen worden verzonden en heeft geen 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);
}

De implementatie van de voorbeeldservice is eenvoudig. Elke methode verpakt het ontvangen bericht in een antwoordbericht en echot het terug naar de client.

Het voorbeeld uitvoeren en bouwen

Wanneer u het voorbeeld uitvoert, wordt de hoofdinhoud van de bewerkingsreacties weergegeven in het venster van de clientconsole, vergelijkbaar met de volgende (opgemaakte) uitvoer.

De client verzendt drie berichten naar de service waarvan het hoofdinhoudselement de naam bodyAheeft, bodyBrespectievelijk bodyX. Zoals kan worden uitgesteld uit de vorige beschrijving en het weergegeven servicecontract, wordt het binnenkomende bericht met het bodyA element verzonden naar de OperationForBodyA() methode. Omdat er geen expliciet verzenddoel is voor het bericht met het bodyX hoofdtekstelement, wordt het bericht verzonden naar de DefaultOperation(). Elk van de servicebewerkingen verpakt de ontvangen berichttekst in een element dat specifiek is voor de methode en retourneert deze, wat wordt gedaan om invoer- en uitvoerberichten duidelijk te correleren voor dit voorbeeld:

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

Het voorbeeld instellen, compileren en uitvoeren

  1. Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.

  2. Volg de instructies in Het bouwen van de Windows Communication Foundation-voorbeelden om de oplossing te bouwen.

  3. Als u het voorbeeld wilt uitvoeren in een configuratie met één of meerdere computers, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.