dettagli della mia implementazione della risposta di Tim
avevo bisogno di scrivere questo per il cliente Attualmente sto lavorando per così ho pensato che potrei pure postare qui anche. Spero che aiuti qualcuno. Ho creato un client e un servizio di esempio che ho utilizzato per provare alcune di queste idee. L'ho ripulito e l'ho aggiunto a github. È possibile download it here.
Le cose seguenti necessari per essere attuati per consentire WCF da utilizzare nel modo in cui avevo bisogno:
- WCF di non aspettarsi un messaggio SOAP
- La possibilità di formattare i messaggi di richiesta e di risposta esattamente come richiesto
- Tutti i messaggi da considerare per l'elaborazione
- I messaggi in arrivo per essere indirizzati al corretto funzionamento di gestirli
1. Configurare WCF di non aspettarsi un messaggio SOAP
Il primo passo è stato sempre il messaggio in arrivo attraverso il TextMessageEncoder. Ciò è stato ottenuto utilizzando un'associazione personalizzata con l'impostazione MessageVersion.None sull'elemento textMessageEncoding.
<customBinding>
<binding name="poxMessageBinding">
<textMessageEncoding messageVersion="None" />
<httpTransport />
</binding>
</customBinding>
2. Formato correttamente il messaggio
Il formattatore messaggio è richiesto come il messaggio in arrivo rifiutato di essere de-serializzati dal formattatore XML esistente senza l'aggiunta di attributi aggiuntivi sui contratti dei messaggi. Questo normalmente non sarebbe un problema, ma l'esecuzione dei miei schemi ebXML tramite XSD.exe genera un file cs di 33000 righe e non ho voluto modificarlo in alcun modo. Inoltre, le persone si dimenticheranno di aggiungere nuovamente gli attributi in futuro, quindi questo rende la manutenzione più facile.
Il formattatore personalizzato si aspetta di convertire il messaggio in arrivo nel tipo del primo parametro e di convertire il tipo di ritorno in un messaggio di risposta. Ispeziona il metodo di implementazione per determinare i tipi del primo parametro e il valore restituito nel costruttore.
public SimpleXmlFormatter(OperationDescription operationDescription)
{
// Get the request message type
var parameters = operationDescription.SyncMethod.GetParameters();
if (parameters.Length != 1)
throw new InvalidDataContractException(
"The SimpleXmlFormatter will only work with a single parameter for an operation which is the type of the incoming message contract.");
_requestMessageType = parameters[0].ParameterType;
// Get the response message type
_responseMessageType = operationDescription.SyncMethod.ReturnType;
}
Quindi utilizza semplicemente XmlSerializer per serializzare e deserializzare i dati. Una parte interessante di questo per me è stata l'uso di una BodyWriter personalizzata per serializzare l'oggetto nell'oggetto Messaggio. Di seguito è parte dell'implementazione per il serializzatore di servizio. L'implementazione del client è il contrario.
public void DeserializeRequest(Message message, object[] parameters)
{
var serializer = new XmlSerializer(_requestMessageType);
parameters[0] = serializer.Deserialize(message.GetReaderAtBodyContents());
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return Message.CreateMessage(MessageVersion.None, _responseMessageType.Name,
new SerializingBodyWriter(_responseMessageType, result));
}
private class SerializingBodyWriter : BodyWriter
{
private readonly Type _typeToSerialize;
private readonly object _objectToEncode;
public SerializingBodyWriter(Type typeToSerialize, object objectToEncode) : base(false)
{
_typeToSerialize = typeToSerialize;
_objectToEncode = objectToEncode;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartDocument();
var serializer = new XmlSerializer(_typeToSerialize);
serializer.Serialize(writer, _objectToEncode);
writer.WriteEndDocument();
}
}
3.Elabora tutti i messaggi in arrivo
Per consentire a WCF di consentire l'elaborazione di tutti i messaggi in arrivo, era necessario impostare la proprietà ContractFilter su endpointDispatcher su un'istanza di MatchAllMessageFilter. Ecco uno snippet che illustra questo aspetto dalla mia configurazione del comportamento degli endpoint.
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
// Do more config ...
}
4. Selezione il corretto funzionamento
Ciò è stato possibile attraverso la creazione di una classe che implementa IDispatchOperationSelector. Nel metodo SelectOperation ispeziono il messaggio in arrivo. Qui sto cercando due cose: 1. Un controllo di integrità controlla che lo spazio dei nomi dell'elemento principale corrisponda allo spazio dei nomi del contratto di servizio 2. Il nome dell'elemento root (notare l'uso di LocalName per rimuovere qualsiasi prefisso dello spazio dei nomi)
Il nome dell'elemento radice è il valore restituito e che verrà associato a qualsiasi operazione sul contratto con un nome corrispondente o dove l'attributo azione ha un valore corrispondente.
public string SelectOperation(ref Message message)
{
var messageBuffer = message.CreateBufferedCopy(16384);
// Determine the name of the root node of the message
using (var copyMessage = messageBuffer.CreateMessage())
using (var reader = copyMessage.GetReaderAtBodyContents())
{
// Move to the first element
reader.MoveToContent();
if (reader.NamespaceURI != _namespace)
throw new InvalidOperationException(
"The namespace of the incoming message does not match the namespace of the endpoint contract.");
// The root element name is the operation name
var action = reader.LocalName;
// Reset the message for subsequent processing
message = messageBuffer.CreateMessage();
// Return the name of the action to execute
return action;
}
}
avvolgendolo All Up
Per rendere più semplice per distribuire ho creato un comportamento endpoint per gestire la configurazione del selettore messaggio formattatore, il filtro del contratto e il funzionamento. Potrei anche aver creato un binding per concludere la configurazione del binding personalizzato ma non pensavo che quella parte fosse troppo difficile da ricordare.
Una scoperta interessante per me era che il comportamento dell'endpoint può impostare il formattatore dei messaggi per tutte le operazioni nell'endpoint per utilizzare un formattatore di messaggi personalizzato. Ciò evita la necessità di configurarli separatamente. Ho preso questo da uno dei Microsoft samples.
documentazione link utili
Le migliori referenze che ho trovato finora sono gli articoli di riviste Service Station MSDN ("del sito: msdn.microsoft.com stazione di servizio WCF" Google).
WCF Bindings in Depth - informazioni molto utili sulle associazioni configurazione
Extending WCF with Custom Behaviours - La migliore fonte di informazioni sui punti di integrazione dispatcher che ho ancora trovato e contengono alcuni diagrammi veramente utili che illustrano tutti i punti di integrazione e dove si verificano in l'ordine di elaborazione.
Microsoft WCF samples - C'è molto qui che non è documentato molto bene altrove. Ho trovato la lettura attraverso il codice sorgente per alcuni di questi molto istruttivo.
Potrebbe fornire un messaggio di richiesta e risposta completo (possibilmente con xsd) su gist.github.com o un servizio simile? – larsw
@larsw Non posso fornire schemi ai miei clienti ma ho creato schemi per i miei messaggi di esempio e ho aggiornato la mia domanda con un collegamento a loro. La cosa fondamentale per me qui è l'aiuto con l'approccio generale e penso che i miei messaggi e schemi di esempio dovrebbero essere sufficienti per questo. – MikeD