2015-05-04 3 views
1

Я пытаюсь раскрыть мои журналы, объединив свои сообщения в журнале. В идеале я добавлял к каждому сообщению какой-то идентификатор, указывающий, что он является частью группы команд, выполняющих какую-то часть работы.Контекст контекста persist для асинхронных вызовов

Если это однопоточное приложение, идентификатор потока будет кандидатом. Однако это решение сильно использует async Task s, что исключает его использование. Можно ли получить что-то вроде следующего для работы, где каждое выполнение Main() всегда будет распечатывать один и тот же идентификатор потока?

static async void Main() 
{ 
    var t = Task.Run(async() => await MyAsyncMethod()); 
    await t; 
} 

private static async Task MyAsyncMethod() 
{ 
    Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId); 

    await Task.Delay(1000); 

    Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId); 

    await Task.Delay(1000); 

    Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId); 
} 
+1

Создать идентификатор в начале метода и использовать его во всем? Или напишите какой-то идентификатор, связанный с тем, что обрабатывает ваш асинхронный метод. Например, если он обрабатывает некоторые работы, связанные с клиентом, это будет идентификатор клиента. –

+0

@SriramSakthivel Хорошие предложения, но я стараюсь уйти без рефакторинга решения, которое довольно велико. И в идеале свойство, которое я группирую, не будет связано с бизнесом, так как я могу одновременно работать с несколькими рабочими ролями одной и той же сущности, поэтому сообщения журнала чередуются, что вызывает у меня путаницу. –

+0

В этом случае просто инкрементный счетчик должен работать нормально. –

ответ

4

Лучший подход IMO что @SriramSakthivel предложил: просто генерировать идентификатор и передать его вокруг. Если у вас есть тип сообщения, вы можете просто добавить его к этому типу.

Однако, если это приведет к слишком большому изменению кода, вы можете передать его неявно. У меня есть blog post with the gory details; общая идея заключается в том, что вы будете хранить данные как часть контекста логического вызова:

public static class MyContext 
{ 
    private static readonly string name = Guid.NewGuid().ToString("N"); 

    private sealed class Wrapper : MarshalByRefObject 
    { 
    public Guid Value { get; set; } 
    } 

    public static Guid CurrentContext 
    { 
    get 
    { 
     var ret = CallContext.LogicalGetData(name) as Wrapper; 
     return ret == null ? Guid.Empty : ret.Value; 
    } 

    set 
    { 
     CallContext.LogicalSetData(name, new Wrapper { Value = value }); 
    } 
    } 
} 

Затем вы можете использовать его из кода как таковые:

private static async Task MyAsyncMethod() 
{ 
    MyContext.CurrentContext = Guid.NewGuid(); 

    Debug.WriteLine("Log message: {0}", MyContext.CurrentContext); 

    await Task.Delay(1000); 

    Debug.WriteLine("Log message: {0}", MyContext.CurrentContext); 

    await Task.Delay(1000); 

    Debug.WriteLine("Log message: {0}", MyContext.CurrentContext); 
} 

Примечания, что такой подход делает два важные предпосылки:

  1. Ваш код работает на .NET 4.5 или новее.
  2. Сохраняемые данные являются неизменяемыми (что истинно в этом случае, поскольку Guid является неизменным).

Похоже, что-то подобное will be builtin в .NET 4.6.

+0

Выглядит отлично! 'CurrentContext' должен быть общедоступным, но нет? –

+0

Исправлено, спасибо! –

Смежные вопросы