Ответ 1
Во-первых, следы стека не делают то, что думают большинство людей. Они могут быть полезны во время отладки, но не предназначены для использования во время выполнения, особенно на ASP.NET.
Кроме того, трассировка стека технически о том, куда возвращается код, а не о том, откуда пришел код. С простым (синхронным) кодом эти два значения одинаковы: код всегда возвращается к любому вызванному ему методу. Однако с асинхронным кодом эти два разных. Опять же, трассировка стека говорит вам, что будет дальше, но вас интересует, что произошло в прошлом.
Итак, фрейм стека не является правильным ответом для ваших нужд. Эрик Липперт объясняет это в своем ответе здесь.
статья MSDN, связанная с @ColeCampbell, описывает один из способов отслеживания "цепочек несчастных случаев" (откуда пришел код) с помощью async
код. К сожалению, этот подход ограничен (например, он не обрабатывает сценарии fork/join); однако это единственный подход, который я знаю о том, что он работает в приложениях Windows Store.
Поскольку вы работаете на ASP.NET с полной версией .NET 4.5, у вас есть доступ к более мощному решению для отслеживания цепочек случайности: контекст логического вызова. Тем не менее, ваши методы async
должны "выбирать", поэтому вы не получите их бесплатно, как в случае трассировки стека. Я просто написал это в блоге, который еще не опубликован, поэтому вы получаете предварительный просмотр.:)
Вы можете построить "стек" вызовов самостоятельно вокруг контекста логического вызова как такового:
public static class MyStack
{
// (Part A) Provide strongly-typed access to the current stack
private static readonly string slotName = Guid.NewGuid().ToString("N");
private static ImmutableStack<string> CurrentStack
{
get
{
var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>;
return ret ?? ImmutableStack.Create<string>();
}
set { CallContext.LogicalSetData(name, value); }
}
// (Part B) Provide an API appropriate for pushing and popping the stack
public static IDisposable Push([CallerMemberName] string context = "")
{
CurrentStack = CurrentStack.Push(context);
return new PopWhenDisposed();
}
private static void Pop() { CurrentContext = CurrentContext.Pop(); }
private sealed class PopWhenDisposed : IDisposable
{
private bool disposed;
public void Dispose()
{
if (disposed) return;
Pop();
disposed = true;
}
}
// (Part C) Provide an API to read the current stack.
public static string CurrentStackString
{
get { return string.Join(" ", CurrentStack.Reverse()); }
}
}
(ImmutableStack
доступен здесь). Затем вы можете использовать его следующим образом:
static async Task SomeWork()
{
using (MyStack.Push())
{
...
Console.WriteLine(MyStack.CurrentStackAsString + ": Hi!");
}
}
Самое приятное в этом подходе заключается в том, что он работает со всеми async
: fork/join, custom awaitables, ConfigureAwait(false)
и т.д. Недостатком является то, что вы добавляете некоторые накладные расходы. Кроме того, этот подход работает только на .NET 4.5; контекст логического вызова на .NET 4.0 не является async
-aware и будет работать не.
Обновление: Я выпустил пакет NuGet (описанный в моем блоге), который использует PostSharp для ввода нажатий и всплывает автоматически. Поэтому получить хороший след теперь будет намного проще.