Подтвердить что ты не робот

Возможно ли в .Net уловить все необработанные исключения из любого метода в классе до его передачи стека вызовов?

Проблема:

Я хотел бы поймать любые исключения из любого метода в классе, чтобы я мог записывать данные, специфичные для класса, в исключение для ведения журнала, прежде чем он будет передан в стек. Я знаю, что могу использовать try-catch в каждом методе класса, но есть много методов, и кажется, что должен быть более эффективный способ.

Пример того, что я делаю сейчас:

public class ClassA
{
    private int x;
    private int y;

    public void Method1()
    {
        try
        {
           //Some code
        }
        catch(Exception ex)
        {
            ex.Data.Add("x", x);
            ex.Data.Add("y", y);
            throw;
        }
    }

    public void Method2()
    {
        try
        {
            //Some code
        }
        catch (Exception ex)
        {
            ex.Data.Add("x", x);
            ex.Data.Add("y", y);
            throw;
        }
    }
}

Пример того, что я хотел бы сделать:

public class ClassB : IUnhandledErrorHandler
{
    private int x;
    private int y;

    public void Method1()
    {
        //Some code
    }

    public void Method2()
    {
        //Some code
    }

    void IUnhandledErrorHandler.OnError(Exception ex)
    {
        ex.Data.Add("x", x);
        ex.Data.Add("y", y);
        throw;
    }
}

public interface IUnhandledErrorHandler
{
    void OnError(Exception ex);
}

Примечание: Этот класс является сервисом в проекте WCF и реализует ServiceContract. Я попытался добавить ErrorHandler в службу ChannelDispatcher. Однако, когда ошибка достигает ErrorHandler, она уже выходит за пределы класса, в котором произошла ошибка, поэтому я не могу получить доступ к данным класса.

Решение:

public class ClassC
{
    public ClassC()
    {
        AppDomain.CurrentDomain.FirstChanceException += OnError;
    }

    private int x;
    private int y;

    public void Method1()
    {
        //Some code
    }

    public void Method2()
    {
        //Some code
    }

    private void OnError(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
    {
        e.Exception.Data["x"] = x;
        e.Exception.Data["y"] = y;
    }
}
4b9b3361

Ответ 1

Если вы запустите .NET 4, вы можете использовать событие FirstChanceException из AppDomain.

Ответ 2

Вы можете выполнить это, наследуя от System.ContextBoundObject. Это не так красиво, как вы надеялись, и я считаю, что, вероятно, с ним связаны некоторые существенные накладные расходы, но насколько я знаю, это единственный способ сделать то, что вы описываете.

В соответствии с запрошенной здесь дополнительной разработкой... как уже упоминалось, это не очень, но вот минимальная реализация, необходимая для выполнения этой работы. Основной метод, представляющий интерес, - метод AOPSink.SyncProcessMessage. Он вызывается перед любым методом объекта "Test", и вызов NextSink.SyncProcessMessage(msg) - это то, что на самом деле вызывает метод, называемый первоначально. Вы можете проверить (и изменить), какой метод был вызван, и параметры, переданные с помощью параметра IMessage, переданного в SyncProcessMessage, и вы можете проверить/изменить возвращаемое значение (или исключенное исключение), играя с сообщением IMessage, возвращаемым из NextSink.SyncProcessMessage. Для этой функции есть значительные накладные расходы, поэтому я бы не рекомендовал ее для объектов с высоким трафиком вне задач отладки.

[AOP]
class Test : ContextBoundObject
{     
    public void TestMethod()     
    {
        throw new Exception();
    }      
}

[AttributeUsage(AttributeTargets.Class)]
public class AOPAttribute : ContextAttribute
{
    public AOPAttribute() : base("AOPAttribute") { }
    public override void GetPropertiesForNewContext(IConstructionCallMessage ctor)
    {
        ctor.ContextProperties.Add(new AOPProperty());
    }
    public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
    { 
        return false; 
    }
}

public class AOPProperty : IContextProperty, IContributeServerContextSink
{
    public IMessageSink GetServerContextSink(IMessageSink nextSink)
    {
        return new AOPSink(nextSink);
    }
    public string Name { get { return "AOP"; } }
    public bool IsNewContextOK(Context ctx) { return true; }
    public void Freeze(Context ctx) { }
}

public class AOPSink : IMessageSink
{
    public AOPSink(IMessageSink nextSink) { this.NextSink = nextSink; }
    public IMessageSink NextSink { get; private set; }
    public IMessage SyncProcessMessage(IMessage msg)
    {
        // inspect method+params by playing with 'msg' here
        IMessage m = NextSink.SyncProcessMessage(msg);
        // inspect returnval/exception by playing with 'm' here
        return m;
    }
    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
    {
        throw new NotSupportedException();
    }
}