9

我只想用一些 id 绑定 WCF 传入和传出消息,将记录到数据库的内容。

由于它计划在高多线程环境中使用,因此出现了一些问题。


后编辑

这是我想要记录的方式:

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

2 回答 2

7

如果我理解正确,您想使用 IDcorrelationState将请求与回复联系起来。为此,请将您的 ID 作为对象返回BeforeSendRequest,并将其作为参数传递给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
}

更新

问题是如何将参数传递给 BeforeSendRequest 方法。

在这种情况下,如果您的请求消息中没有您需要的所有信息,那么显而易见的解决方案是我们使用ThreadStatic字段将信息从您的调用者传递到BeforeSendRequest.

在调用您的服务之前设置该字段,然后提取并使用BeforeSendRequest.

更新 2

我可以使用的另一个选项是将参数设置为消息本身,但是如何在 BeforeSendRequest 的请求参数中访问它?

我不明白这个问题。如果我理解正确,您希望能够将参数从调用者传递给BeforeSendRequest方法。您知道如何使用correlationState将其从BeforeSendRequestto传递。AfterReceiveReply

做到这一点的方法是使用一个ThreadStatic字段。例如,您可以创建以下类:

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

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

然后您的调用者将在调用 Web 服务之前进行设置,并且它对和CallerContext.State都可用。例如BeforeSendRequestAfterReceiveReply

...
try
{
    CallerContext.State = myId;

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

这将在BeforeSendRequest

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

    ... do your stuff here

    return correlationState;
}

请注意,使用 try/finally 重置CallerContext.State为 null 并不是绝对必要的,但可以防止不相关的代码访问您存储在那里的可能敏感数据。

于 2013-06-05T07:34:29.653 回答
4

我认为最简单的解决方案是将 threadstatic 变量放入LogMessageInspector. 在 中设置值BeforeSendRequest并再次使用它AfterReceiveReply

如果您使用异步操作,我会变得更复杂一些,但您所要做的就是密切关注您如何访问和传递这个本地变量。

MessageInspectors 旨在处理 Message 本身,因此最好的解决方案是在消息中包含一些相关的内容,例如 requestId、OrderId 等。

编辑:

我会想象服务合同看起来像这样:

[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 将在客户端生成,现在您可以对消息执行 xpath 并获取 RequestID 以关联请求和响应。

于 2013-06-02T09:57:48.787 回答