控制台应用程序中的LogicalCallContext可以在await中流动,但在VS UnitTest中不行。

7
我正在使用逻辑调用上下文将信息沿着一系列的await流动。有趣的是,在我的测试控制台应用程序中,一切都运作良好。然而,在VS UnitTest的上下文中运行我的单元测试时,调用上下文似乎不能在await之间流动。
在方法SendRequestAsyncImpl内部,设置了调用上下文,当我在方法返回时的断点处查询逻辑调用上下文时,调用上下文被正确设置了。
但是,在以下代码行中的await返回后:
Message response = await SendRequestAsyncImpl(m, true).ConfigureAwait(false);

逻辑调用上下文为空。我认为通过将ConfigureAwait(true)而不是false进行设置,问题可能会得到解决。但这并不能解决问题。

无论我尝试流动什么,甚至在SendRequestAsyncImpl中设置一个简单的值类型,例如:

System.Runtime.Remoting.Messaging.CallContext.LogicalSetData("flag", true);

在等待之后无法检索到。

为什么从我的控制台应用程序可以工作,但是从我的单元测试中不行?有什么不同之处吗?(我看到其他一些stackoverflow问题涉及AppDomain问题。但是我甚至不能跨等待传递一个bool。似乎在这里问题并不是能否传递数据的能力。)


你的单元测试项目针对哪个框架版本? - Stephen Cleary
好问题。它的目标是 .NET 4.5。 - Ayo I
如果你将SUT替换为await Task.Delay(100);,那么它是否会顺利执行? - Stephen Cleary
我可以在等待之前调用LogicalSetData,在等待之后,值仍然被设置。只有在awaited方法内部调用LogicalSetData时,它才不会流动。这是你要问的问题吗? - Ayo I
我正在阅读您关于这个主题的文章(http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html)。我注意到在VSTest执行引擎内部调用了Task.WhenAny(...)。您在其中一个评论中提到,Task.WhenAll(...)会导致丢失CallContext,因为不清楚如何合并多个不同任务的CallContext当它们都返回时。这很有道理。VSTest中Task.WhenAny(...)的存在是否会导致这种行为? - Ayo I
1
逻辑调用上下文不会从任何方法中流出。它将流入方法并穿越await点,但不会流出。 - Stephen Cleary
1个回答

7

阅读了Stephen Clearly在这个问题上以及这篇文章中的评论后,答案变得清晰明了。

在同步代码中,逻辑CallContext会从方法中传递回去。但在异步方法中,CallContext不会传递回去。这是有道理的,因为像Task.WhenAll(...)这样的方法执行后,.NET如何知道我希望如何合并CallContext。以下代码演示了这一点:

static void Main(string[] args)
{
    SynchronousCall();
    Task.WaitAll(Test(), Test2());
    var s = CallContext.LogicalGetData("SynchronousCall");
    var test = CallContext.LogicalGetData("Test");
    var test2 = CallContext.LogicalGetData("Test2");

    Console.WriteLine("s val: {0}", (s == null) ? "{null}" : s);
    Console.WriteLine("test val: {0}", (test == null) ? "{null}" : test);
    Console.WriteLine("test2 val: {0}", (test2 == null) ? "{null}" : test2);
}

private static void SynchronousCall()
{
    CallContext.LogicalSetData("SynchronousCall", true);
}

private static async Task<bool> Test()
{
    CallContext.LogicalSetData("Test", true);
    var b = await Task.Run<bool>(() => 
    {
        return true; 
    });
    return b;
}

private static async Task<bool> Test2()
{
    CallContext.LogicalSetData("Test2", true);
    var b = await Task.Run<bool>(() =>
    {
        return true;
    });
    return b;
}

打印以下内容:
s val: True
test val: {null}
test2 val: {null}

因此,正如您所看到的,同步方法允许CallContext流出,但异步方法则不允许。

我修改了我的方法,在可等待方法之前向CallContext注入一个线程安全的集合。我将信息注入该集合中,而不是直接注入CallContext中。由于上游和下游都获得对同一集合的引用,这使我能够通过从集合中检索它来从我的可等待方法中流出上下文

希望这可以帮助未来的某个人。


谢谢。我使用了CallContext.LogicalSetData("MyDic", new Dictionary<string, string>())和Dictionary<string, string> myDic = (Dictionary<string, string>)CallContext.LogicalGetData("MyDic"); - Rob Sedgwick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接