2

我有一个设置为使用会话的 wcf 服务(托管在 IIS 中) 。它似乎工作。当Application_PostAcquireRequestState被调用时,我有一个会话 ID。

我最终像这样使用它(在我的 Global.asax 中):

if (Context.Handler is IRequiresSessionState)
{
    log4net.ThreadContext.Properties["sessionId"] = Session.SessionID;
}

这似乎工作正常。该值存储在我的 log4net 属性中。

但是当我的服务操作开始时(我的实际 WCF 服务代码),log4net 属性再次为空。

由于该属性是按线程 ( ThreadContext) 存储的,因此我只能假设这意味着会话是在一个线程上设置的,然后在另一个线程上执行。我对吗?

无论如何要在正确的线程上设置我的 log4net 属性(不必记住在每个服务操作开始时进行上述调用)?

4

3 回答 3

2

是的,IIS 可以使用多线程来服务多个 WCF 请求。有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/cc512374.aspx 。

您可以考虑为每个 WCF 请求使用不同的记录器实例。

于 2013-05-01T14:25:15.327 回答
1

自定义属性不需要是字符串。因此,您可以在全局上下文中存储以下类的实例:

public class SessionIdProperty 
{
    public override string ToString()
    {
        // error handling omitted
        return Session.SessionID;
    }
}

这样 log4net 可以Session在记录消息时直接访问该对象。Log4net 对非字符串属性调用 ToString() 方法。

于 2013-05-04T12:29:39.653 回答
1

在多种情况下,WCF 可能会更改您的线程:

  1. Global.asx 线程不能保证用于服务调用(实际上不太可能)。
  2. 如果在同一会话期间有多个调用,则线程也可能在对同一服务实例的调用之间发生变化。

理论上,像这样的状态信息应该存储在 Operation Context 对象中。然而,因为 log4net 使用线程本地存储,它成为一个尴尬的解决方案。

无论如何要在正确的线程上设置我的 log4net 属性(不必记住在每个服务操作开始时进行上述调用)?

是的。创建一个自定义IOperationInvoker。我所知道的最好的例子是Carlos Figueira 的博客如果将此作为服务行为应用,则应始终为服务代码定义 log4net 属性。

一个警告:当添加到线程本地存储时,一定要清理干净。这就是 log4net.ThreadContext.Stacks[].Push() 返回 IDisposable 的原因。换句话说,您的Invoke方法应该看起来像(不完整且未经测试):

public object Invoke(object instance, object[] inputs, out object[] outputs)
{
    using (log4net.ThreadContext.Stacks[key].Push(value))
    {
        return this.originalInvoker.Invoke(instance, inputs, out outputs);
    }
}

请参阅 Carlos 的博客以了解您为什么称其为“originalInvoker”。请注意,如果要支持异步操作,则需要实现其他方法。

于 2013-05-01T15:14:39.983 回答