Ron,

>> Isn't the automatic clean-up more a feature of the 
>> using statement rather than the ThreadContext?

Sorry if I wasn't clear. I consider them intertwined because I wouldn't use 
explicit (push/pop) ThreadContext without automatic clear-up. Too error prone.

I have tried to work out a scheme for anonymous delegates to provide context 
safe exceptions but sadly there is too much dang strong typing so either you 
have to declare a delegate type for anything you wish to execute contextually 
or do what I attach below which is ugly in so many different ways! Basically it 
requires that each level of context is a separate method, which is perfectly 
acceptable (and often the case anyway), and then call it as a delegate but 
sadly passing arguments requires filthy casting and its broken for ref, out, 
value-types and return values.

I post it to see if it sparks the creativity of anyone else to find a more 
effective scheme that is simpler and type-safe.

On a related note - the NDC Method 'CloneStack' doesn't work as I expected - it 
doesn't return a copy but a reference that is manipulated without consent plus 
the contained items are classes internal to log4net that don't have a sensible 
ToString so you can't do diddly with them. This is why I had to pop and re-push 
the ndc to copy it in NdcException. Doesn't FxCop warn about this?

anyway... on to the ugliness, believe it or not, it is functionally the same 
example Ron gave before:


using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Layout;

namespace ConsoleApplication
{
    public delegate void ExecuteInContext(params object[] p);
    
    // execute code passed as an anonymous delegate with context safe exceptions
    public abstract class Context
    {
        public static void Execute(string context, ExecuteInContext call, 
params object[] p)
        {            
            using (NDC.Push(context))
            {                                
                try
                {
                    call(p);
                }
                catch (Exception e)
                {
                    if (!(e is NdcException)) throw new NdcException(e);
                    else throw;
                }                
            }
        }
    }

    // Wrap a normal exception and append the ndc stack - requires more effort 
than it should
    public class NdcException : Exception
    {
        public NdcException(Exception e)
            : base(e.Message, e)
        {
            Stack<string> stack = new Stack<string>();            
            StringBuilder sb = new StringBuilder("Exception : ");
            while (NDC.Depth > 0)
            {
                stack.Push(NDC.Pop());
            }
            while (stack.Count > 0)
            {
                string s = stack.Pop();
                sb.AppendFormat("{0} ", s);
                NDC.Push(s);
            }
            Ndc = sb.ToString();
        }
        public string Ndc;
    }

    class Program
    {
        static ConsoleAppender consoleAppender;
        static ILog log;

        static void Main(string[] args)
        {
            consoleAppender = new ConsoleAppender();
            consoleAppender.Layout = new PatternLayout("MESSAGE [%message] NDC 
[%ndc]%newline");
            BasicConfigurator.Configure(consoleAppender);
            log = LogManager.GetLogger(typeof(Program));  
            
            int[] oddNumbers = new int[5] { 1, 3, 5, 7, 9 };
            int[] evenNumbers = new int[5] { 2, 4, 6, 8, 10 };
            
            try
            {
                foreach (int oddNumber in oddNumbers)
                {
                    Context.Execute(
                        "oddNumber: " + oddNumber,
                        delegate(object[] p) {EachEven((int) p[0], (int[]) 
p[1]);},
                        oddNumber, evenNumbers);                    
                }
            }
            // Provide a contextual exception message
            catch (NdcException ex)
            {
                using (NDC.Push(ex.Ndc))
                {
                    log.Fatal(ex.Message);
                }
            }
            catch (Exception ex)
            {
                log.Fatal("Should never happen: " + ex.Message, ex);
            }
        }

        public static void EachEven(int oddNumber, int[] evenNumbers)
        {
            foreach (int evenNumber in evenNumbers)
            {
                Context.Execute(
                    "evenNumber: " + evenNumber,
                    delegate(object[] p) {TestOdd((int) p[0], (int) p[1]);},
                    oddNumber, evenNumber);                
            }
        }

        public static void TestOdd(int oddNumber, int evenNumber)
        {            
            if (oddNumber == 7)
            {
                throw new ApplicationException("Oh no!");
            }
            log.DebugFormat("Sum: {0}", oddNumber + evenNumber);
        }        
    }    
}


Thanks,
Duncan

<<winmail.dat>>

Reply via email to