如何将参数(id)传递给 WCF 中的消息检查器?

Posted

技术标签:

【中文标题】如何将参数(id)传递给 WCF 中的消息检查器?【英文标题】:How to pass parameter (id) to message inspector in WCF? 【发布时间】:2013-03-14 13:37:58 【问题描述】:

我只想用一些 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;
        
    

【问题讨论】:

mattgemmell.com/2008/12/08/what-have-you-tried @SonerGönül 事实上,我什至不知道如何开始解决这个问题 :) 每个 WCF 调用都有与之关联的唯一 ID:OperationContext.Current.IncomingMessageHeaders.MessageId @Alexander 我想定义自己的自定义 ID,与 DB 中的其他实体相关联。 您能否至少发布服务接口以及您计划如何生成该 ID? 【参考方案1】:

如果我理解正确,您想使用 ID 作为correlationState 将请求与回复联系起来。为此,请将您的 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 将其从BeforeSendRequest 传递到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 并不是绝对必要的,但可以防止无关代码访问您存储在那里的可能敏感数据。

【讨论】:

正如您在我的代码示例中看到的那样,我已经这样做了。问题是如何将参数传递给BeforeSendRequest 方法。 我可以使用的另一个选项是将参数设置为消息本身,但是如何在BeforeSendRequestrequest 参数中访问它?【参考方案2】:

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

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

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 以关联请求和响应。

【讨论】:

能否请您给我一些有关 RequestID 或 OrderID 的样本。找不到此类属性。 貌似不错但是没有机会在BeforeSendRequest得到RequestID

以上是关于如何将参数(id)传递给 WCF 中的消息检查器?的主要内容,如果未能解决你的问题,请参考以下文章

如何将装饰器中的变量传递给装饰函数中的函数参数?

HTTP 标头或 SOAP 标头中的 WCF 肥皂操作?

WCF如何传递令牌进行身份验证?

Java GraphQL - 将字段值传递给对象的解析器

如何将null值传递给简单注入器中的构造函数?

将 ID(Index) 传递给 onEditDetail() 参数