【问题标题】:LogicalCallContext flows across await in Console App but not VS UnitTestLogicalCallContext 在控制台应用程序中流过等待,但不是 VS UnitTest
【发布时间】:2016-02-11 20:56:52
【问题描述】:

我正在使用 Logical CallContext 将信息流回一系列等待。有趣的是,在我的测试控制台应用程序中,一切正常。但是,当在 VS UnitTest 的上下文中运行我的单元测试时,调用上下文似乎不会流经等待。

在方法内部:SendRequestAsyncImpl 正在设置调用上下文,当我在方法返回的那一刻从断点查询逻辑调用上下文时,调用上下文设置得很好。

但是,在下面的行中等待返回后:

Message response = await SendRequestAsyncImpl(m, true).ConfigureAwait(false);

逻辑调用上下文为空。我想也许可以通过设置 ConfigureAwait(true) 而不是 false 来解决问题。但这并不能解决问题。

我尝试流向什么都没关系,甚至在 SendRequestAsyncImpl 中设置一个简单的值类型,例如:

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

等待后不可检索。

为什么这在我的控制台应用程序中有效?但不是来自我的单元测试?有什么不同? (我见过一些其他涉及 AppDomain 问题的堆栈溢出问题。但我什至无法在 await 中编组一个 bool。编组数据的能力似乎不是这里的问题。)

【问题讨论】:

  • 您的单元测试项目的目标框架版本是什么?
  • 好问题。它的目标是 .NET 4.5
  • 如果你用await Task.Delay(100);替换你的SUT,它会流动吗?
  • 我可以在 await 之前调用 LogicalSetData,在 await 之后,值仍然是设置的。只有在等待的方法内部调用 LogicalSetData 时,它才不会流动。这是你要问的问题吗?
  • 逻辑调用上下文不会流出任何方法。它会流入到方法并跨越await点,但不会流出流出

标签: c# async-await remoting vs-unit-testing-framework


【解决方案1】:

所以在阅读了 Stephen Clearly 的 cmets 之后,无论是在这个问题上还是在文章中:http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html 答案都变得清晰了。

在同步代码中,逻辑 CallContext 确实从方法中流出。 在异步方法中,CallContext 不会回流。这是有道理的,因为 .NET 如何知道我希望如何在 Task.WhenAll(...) 之类的之后合并 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()) 和 Dictionary myDic = (Dictionary)CallContext.LogicalGetData( "MyDic");
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-18
  • 2020-06-28
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
  • 1970-01-01
相关资源
最近更新 更多