我们如何为异步.net方法创建一个callcontext?

时间:2014-09-24 09:29:30

标签: c# .net async-await cross-cutting-concerns threadcontext

在同步环境中,很容易创建一个范围上下文,允许您将带外上下文附加到当前线程。例如,当前的TransactionScope或线程静态日志记录上下文。

using (new MyContext(5))
    Assert.Equal(5, MyContext.Current);

Assert.Equal(null, MyContext.Current);

使用a combination of IDisposable and a thread-static field很容易实现上下文。

显然,当使用异步方法时,这会分崩离析,因为上下文基于线程静态字段。所以,这失败了:

using (new MyContext(5))
{
    Assert.Equal(5, MyContext.Current);

    await DoSomethingAsync();

    Assert.Equal(5, MyContext.Current);
}

当然我们也希望将上下文传递给调用链中的异步方法,所以这也应该有效:

using (new MyContext(5))
{
    Assert.Equal(5, MyContext.Current);

    await AssertContextIs(5);
}

有没有人知道如何实施?使用async / await模式时丢失带外上下文会使代码的某些部分变得非常难看。

考虑异步WebAPI调用,您希望将基于请求的上下文用于日志记录。您希望调度堆栈深处的记录器能够知道请求ID,而无需使用参数将整个请求ID一直传递到调用堆栈。

感谢您的帮助!

2 个答案:

答案 0 :(得分:5)

最佳解决方案是在ASP.NET中使用HttpContext.Current.Items,或在OWIN中使用IOwinRequest.[Get|Set]。是的,这确实意味着将请求对象传递到任何地方,如果您认为您的“上下文值”属于该请求,这是有意义的。如果你对了解OWIN的“库”方法感到不舒服,那么编写一个“上下文包装器”对象并将其传递给它是很容易的。

但是,如果您确定不想传递任何内容,那么您可以use LogicalCallContext with immutable data正如我在博客中描述的那样。如果您只关心日志记录,那么您可能会发现我的AsyncDiagnostics library很有用 - 它使用PostSharp来注入方法名称,您也可以将自己的信息添加到“异步堆栈”中。

答案 1 :(得分:0)

如果您在托管环境中(您正在谈论WebAPI),您应该使用请求上下文(HttpContext.Current),而不是线程,因为您不管理线程,并且不知道何时/是否原始主题将无法获得如何返回它。

使用await关键字正确处理请求上下文。