2013-03-25 10 views
7

Desidero solo associare i messaggi in entrata e in uscita di WCF con un ID, che verrà registrato nel database.Come passare il parametro (id) a message inspector in WCF?

Come previsto per l'utilizzo in ambienti con più thread multipli, si sono verificati alcuni problemi.


POST EDIT

Ecco come vorrei effettuare il login:

public class LogMessageInspector : IClientMessageInspector 
    { 
     public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
     { 
      Dictionary<string, object> logParams = (Dictionary<string, object>)correlationState; 
      logParams["description"] = reply.ToString(); 

      Logger log = LogManager.GetCurrentClassLogger(); 
      log.InfoEx(String.Format("response_{0}", logParams["_action"]), logParams); 
     } 

     public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
     {    
      string iteration_id = ""; 
// here comes seeking for custom, previously setted header with id 
      for (int i = 0; i < request.Headers.Count; i++) 
      { 
       if ((request.Headers[i].Name == "_IterationId") && (request.Headers[i].Namespace == "http://tempuri2.org")) 
       { 
        iteration_id = request.Headers.GetHeader<string>(i); 
        request.Headers.RemoveAt(i); 

        break; 
       } 
      } 

      string pair_id = StringGenerator.RandomString(10); 
      string action_name = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf("/") + 1); 
      Dictionary<string, object> logParams = new Dictionary<string,object>() { {"iteration_id", iteration_id}, { "description", request.ToString() }, { "request_response_pair", pair_id }, { "_action", action_name } }; 

      Logger log = LogManager.GetCurrentClassLogger(); 
      log.InfoEx(String.Format("request_{0}", action_name), logParams);    

      return logParams; 
     } 
    } 
+2

http://mattgemmell.com/2008/12/08/what-have-you-tried/ –

+0

@ SonerGönül In realtà, non sapevo nemmeno come iniziare a risolvere il problema :) – kseen

+0

Ogni chiamata WCF ha ID univoco associato: OperationContext.Current.IncomingMessageHeaders.MessageId – Alexander

risposta

4

Penso soluzione più semplice sarebbe quella di mettere variabile ThreadStatic in LogMessageInspector. Impostare il valore in BeforeSendRequest e utilizzarlo di nuovo in AfterReceiveReply.

Mi risulta un po 'più complesso se si utilizzano operazioni asincrone, ma tutto ciò che si dovrebbe fare è prestare maggiore attenzione a come si accede e si passa questa var locale.

MessageInspectors è progettato per funzionare su Message stesso, quindi la soluzione migliore sarebbe avere qualcosa nel messaggio correlato a, ad es. RequestID, OrderId, ecc

EDIT:

Immagino contratto di servizio per essere simile a questa:

[ServiceContract] 
public interface IService1 
{ 
    [OperationContract] 
    ServiceResponse Process(ServiceRequest request); 
} 


class ServiceRequest 
{ 
    public Guid RequestID { get; set; } 
    public string RequestData { get; set; } 
} 

class ServiceResponse 
{ 
    public Guid RequestID { get; set; } 
    public string ResponseData { get; set; } 
} 

RequestID verrebbe generato sul lato client, e ora si può fare sulla vostra XPath messaggi e ottenere RequestID per correlare richiesta e risposta.

+0

Potrebbe per favore darmi un campione su RequestID o OrderID. Impossibile trovare tali proprietà. – kseen

+0

@kseen si prega di consultare il mio aggiornamento. – Enes

+0

Sembra essere buono ma non c'è la possibilità di ottenere 'RequestID' in' BeforeSendRequest' – kseen

4

Se ho capito correttamente, si desidera utilizzare un ID come correlationState per collegare la richiesta con la risposta. Per fare ciò, restituisci il tuo ID come oggetto da BeforeSendRequest, e verrà passato come argomento a AfterReceiveReply.

public object BeforeSendRequest(ref Message request, IClientChannel channel) 
{ 
    var correlationState = ... // for example, a new GUID, or in your case 
           // an id extracted from the message 

    ... do your stuff here 

    return correlationState; 
} 

public void AfterReceiveReply(ref Message reply, object correlationState) 
{ 
    // the correlationState parameter will contain the value you returned from 
    // BeforeSendRequests 
} 

UPDATE

Il problema è come passare il parametro al metodo BeforeSendRequest.

In tal caso, se non si dispone di tutte le informazioni necessarie già nel vostro messaggio di richiesta, la soluzione più ovvia è per noi un campo ThreadStatic per comunicare informazioni dal chiamante a BeforeSendRequest.

Impostare il campo prima di chiamare il servizio, quindi estrarre e utilizzare il valore in BeforeSendRequest.

UPDATE 2

Un'altra opzione che posso utilizzare è quello di impostare il parametro al messaggio stesso, ma come posso accedere a parametro di richiesta in BeforeSendRequest?

Non capisco questa domanda. Se ho capito bene, vuoi essere in grado di passare un parametro dal chiamante al metodo BeforeSendRequest. Sai come passare da BeforeSendRequest a AfterReceiveReply, usando correlationState.

Il modo per fare ciò è utilizzare un campo ThreadStatic. Ad esempio, è possibile creare la seguente classe:

public static class CallerContext 
{ 
    [ThreadStatic] 
    private static object _state; 

    public static object State 
    { 
     get { return _state; } 
     set { _state = value; } 
    } 
} 

Poi il chiamante avrebbe fissato CallerContext.State prima di chiamare il servizio web, e sarebbe a disposizione sia BeforeSendRequest e AfterReceiveReply. Per esempio.

... 
try 
{ 
    CallerContext.State = myId; 

    ... call web service here ... 
} 
finally 
{ 
    CallerContext.State = null; 
} 

e questo sarà disponibile in BeforeSendRequest:

public object BeforeSendRequest(ref Message request, IClientChannel channel) 
{ 
    var correlationState = CallerContext.State; 

    ... do your stuff here 

    return correlationState; 
} 

notare che l'uso di try/finally per ripristinare CallerContext.State su null non è strettamente necessario, ma impedisce codice correlato di accedere ai dati sensibili che eventualmente tu negozio lì.

+0

Come si può vedere nel mio esempio di codice, lo faccio già. Il problema è come passare il parametro al metodo 'BeforeSendRequest'. – kseen

+0

Un'altra opzione che posso usare è impostare il parametro sul messaggio stesso, ma come posso accedervi nel parametro 'request' in' BeforeSendRequest'? – kseen

Problemi correlati