LogicalCallContext проходит через await в консольном приложении, но не через VS UnitTest

Я использую 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. Но я даже не могу маршалировать bool через await. Возможность маршалировать данные здесь не проблема.)


person Ayo I    schedule 11.02.2016    source источник
comment
На какую версию фреймворка нацелен ваш проект модульного тестирования?   -  person Stephen Cleary    schedule 12.02.2016
comment
Хороший вопрос. Он нацелен на .NET 4.5.   -  person Ayo I    schedule 12.02.2016
comment
Если вы замените свой SUT на await Task.Delay(100);, тогда он будет течь?   -  person Stephen Cleary    schedule 12.02.2016
comment
Я могу вызвать LogicalSetData перед ожиданием, и после ожидания значение все еще установлено. Только если LogicalSetData вызывается внутри ожидаемого метода, он не проходит. Это вопрос, который вы задаете?   -  person Ayo I    schedule 12.02.2016
comment
Я просматриваю вашу статью (blog.stephencleary. com/2013/04/) по этой теме. Я заметил, что внутри механизма выполнения VSTest вызывается Task.WhenAny(...). Вы упомянули в одном из комментариев, что Task.WhenAll(...) приводит к потере CallContext, потому что неясно, как объединить CallContext нескольких разных задач, когда все они возвращаются. Это имеет смысл. Может ли присутствие Task.WhenAny(...) в VSTest вызывать такое поведение?   -  person Ayo I    schedule 12.02.2016
comment
Контекст логического вызова не вытекает из какого-либо метода. Он будет входить в методы и через await точек, но не будет выходить.   -  person Stephen Cleary    schedule 12.02.2016


Ответы (1)


Итак, после прочтения комментариев Стивена Клирли, как по этому вопросу, так и по статье: http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html ответ стал ясен.

В синхронном коде логический CallContext действительно вытекает обратно из метода. В асинхронных методах CallContext не передается обратно. Это имеет смысл, поскольку откуда .NET узнает, как я хочу, чтобы CallContext был объединен после чего-то вроде Task.WhenAll(...). Выполнение приведенного ниже кода иллюстрирует:

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. Поскольку восходящий и нисходящий потоки получают ссылку на одну и ту же коллекцию, это позволяет мне передавать контекст из моего ожидаемого метода, извлекая его из коллекции после возврата ожидаемого метода.

Надеюсь, это поможет кому-то еще в будущем.

person Ayo I    schedule 12.02.2016
comment
Спасибо, я использовал CallContext.LogicalSetData(MyDic, new Dictionary‹string, string›()) и Dictionary‹string, string› myDic = (Dictionary‹string, string›)CallContext.LogicalGetData(MyDic); - person Rob Sedgwick; 14.07.2021