Ответ 1
Если вы запустите .NET 4, вы можете использовать событие FirstChanceException из AppDomain.
Проблема:
Я хотел бы поймать любые исключения из любого метода в классе, чтобы я мог записывать данные, специфичные для класса, в исключение для ведения журнала, прежде чем он будет передан в стек. Я знаю, что могу использовать 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;
}
}
Если вы запустите .NET 4, вы можете использовать событие FirstChanceException из AppDomain.
Вы можете выполнить это, наследуя от 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();
}
}