How to pass parameter (id) to message inspector in WCF?

I just want to associate incoming and outgoing WCF messages with some id that will be written to the database.

As planned for use in a multi-threaded environment, some problems arose.


WORK REPORT

This is how I want to register:

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; } } 
+7
source share
2 answers

I think the simplest solution would be to put a threadstatic variable in the LogMessageInspector . Set the value in BeforeSendRequest and use it again in AfterReceiveReply .

I get a little complicated if you use asynchronous operations, but all you have to do is pay attention to how you access and pass this local var.

MessageInspectors are designed to work with the message itself, so the best solution would be to have something in the message for correlation, for example. requestId, OrderId, etc.

EDIT:

I would suggest that a service contract looks something like this:

 [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 will be generated on the client side, and now you can make xpath in your messages and get RequestID for correlation of request and response.

+4
source

If I understood correctly, you want to use the identifier as correlationState to associate the request with the response. To do this, return your identifier as an object from BeforeSendRequest , and it will be passed as an AfterReceiveReply argument.

 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

The problem is how to pass the parameter to the BeforeSendRequest method.

In this case, if you do not have all the necessary information in your request, the obvious solution is ThreadStatic to transfer information from your caller to BeforeSendRequest .

Set the field before calling the service, then BeforeSendRequest and use the value in BeforeSendRequest .

UPDATE 2

Another possibility I can use is to set the parameter to the message itself, but how can I access it in the request parameter in BeforeSendRequest?

I do not understand this question. If I understand correctly, you want to pass the parameter from the calling BeforeSendRequest method. You know how to pass it from BeforeSendRequest to AfterReceiveReply using the AfterReceiveReply correlation.

The way to do this is to use the ThreadStatic field. For example, you can create the following class:

 public static class CallerContext { [ThreadStatic] private static object _state; public static object State { get { return _state; } set { _state = value; } } } 

Then your caller will set CallerContext.State before calling the web service and will be available to both BeforeSendRequest and AfterReceiveReply . For example.

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

and this will be available in BeforeSendRequest :

 public object BeforeSendRequest(ref Message request, IClientChannel channel) { var correlationState = CallerContext.State; ... do your stuff here return correlationState; } 

Note that using try / finally before CallerContext.State to null is not strictly necessary, but prevents access to unrelated code by the available, possibly confidential, data that you store there.

+4
source

All Articles