Is LogicalOperationStack incompatible with async in .Net 4.5

前端 未结 3 1355
萌比男神i
萌比男神i 2021-02-04 04:16

Trace.CorrelationManager.LogicalOperationStack enables having nested logical operation identifiers where the most common case is logging (NDC). Should it still work

3条回答
  •  伪装坚强ぢ
    2021-02-04 04:51

    Yes, LogicalOperationStack should work with async-await and it is a bug that it doesn't.

    I've contacted the relevant developer at Microsoft and his response was this:

    "I wasn't aware of this, but it does seem broken. The copy-on-write logic is supposed to behave exactly as if we'd really created a copy of the ExecutionContext on entry into the method. However, copying the ExecutionContext would have created a deep copy of the CorrelationManager context, as it's special-cased in CallContext.Clone(). We don't take that into account in the copy-on-write logic."

    Moreover, he recommended using the new System.Threading.AsyncLocal class added in .Net 4.6 instead which should handle that issue correctly.

    So, I went ahead and implemented LogicalFlow on top of an AsyncLocal instead of the LogicalOperationStack using VS2015 RC and .Net 4.6:

    public static class LogicalFlow
    {
        private static AsyncLocal _asyncLogicalOperationStack = new AsyncLocal();
    
        private static Stack AsyncLogicalOperationStack
        {
            get
            {
                if (_asyncLogicalOperationStack.Value == null)
                {
                    _asyncLogicalOperationStack.Value = new Stack();
                }
    
                return _asyncLogicalOperationStack.Value;
            }
        }
    
        public static Guid CurrentOperationId =>
            AsyncLogicalOperationStack.Count > 0
                ? (Guid)AsyncLogicalOperationStack.Peek()
                : Guid.Empty;
    
        public static IDisposable StartScope()
        {
            AsyncLogicalOperationStack.Push(Guid.NewGuid());
            return new Stopper();
        }
    
        private static void StopScope() =>
            AsyncLogicalOperationStack.Pop();
    }
    

    And the output for the same test is indeed as it should be:

    00000000-0000-0000-0000-000000000000
        ae90c3e3-c801-4bc8-bc34-9bccfc2b692a
        ae90c3e3-c801-4bc8-bc34-9bccfc2b692a
        ae90c3e3-c801-4bc8-bc34-9bccfc2b692a
    00000000-0000-0000-0000-000000000000
    

提交回复
热议问题