20

У меня есть много методов, которые вызывают с помощью Delegate.DynamicInvoke. Некоторые из этих методов делают вызовы базы данных, и я хотел бы иметь возможность поймать SqlException и не поймать TargetInvocationException и охотиться через своих внутренних пользователей, чтобы найти то, что на самом деле пошло не так.Как восстановить внутреннее исключение исключения TargetInvocationException без потери трассировки стека

Я использовал этот метод, чтобы повторно выдать, но она очищает трассировку стеки:

try 
{ 
     return myDelegate.DynamicInvoke(args); 
} 
catch(TargetInvocationException ex) 
{ 
    Func<TargetInvocationException, Exception> getInner = null; 
    getInner = 
     delegate(TargetInvocationException e) 
     { 
     if (e.InnerException is TargetInvocationException) 
      return getInner((TargetInvocationException) e.InnerException); 

     return e.InnerException; 
     }; 

    Exception inner = getInner(ex); 
    inner.PreserveStackTrace(); 
    throw inner; 
} 

Метод PreserveStackTrace является методом расширения я устроился благодаря другому сообщению (я не знаю, что это на самом деле делает) , Однако, это не представляется сохранение следа либо:

public static void PreserveStackTrace(this Exception e) 
{ 
    var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain); 
    var mgr = new ObjectManager(null, ctx); 
    var si = new SerializationInfo(e.GetType(), new FormatterConverter()); 

    e.GetObjectData(si, ctx); 
    mgr.RegisterObject(e, 1, si); 
    mgr.DoFixups(); 
} 

ответ

27

Если вы просто хотите, чтобы вновь бросить внутреннее исключение с сохранением его стека, вы можете сделать это с помощью метода, как это:

public static void Rethrow(this Exception ex) 
{ 
    typeof(Exception).GetMethod("PrepForRemoting", 
     BindingFlags.NonPublic | BindingFlags.Instance) 
     .Invoke(ex, new object[0]); 
    throw ex; 
} 

Этот метод используется Rx (и подвергается ими как метод расширения Exception.PrepareForRethrow), а также используется системой Async CTP с помощью системы автоматической разворачивания (без публично открытого API).

Обратите внимание, что эта техника технически не поддерживается. Надеюсь, Microsoft добавит официальный API для этого в будущем. Предложение has been opened на Microsoft Connect, если вы хотите проголосовать за него.

Обновление: Официальный API был добавлен в .NET 4.5: ExceptionDispatchInfo.

+0

Интересный момент для заметок: ExceptionDispatchInfo в 4.5 означает, что вы видите разные стеки вызовов из Rx, когда ретушируете исключения в зависимости от того, настроены ли вы на 4.0 или 4.5. Необработанный OnErrors будет восстановлен с исходной трассировкой стека при таргетинге на 4.5, но не на 4.0. –

2

Необходимо помнить, почему .NET обертывает исключение с помощью TargetInvocationException вместо того, чтобы просто пропускать исходное исключение. Для этого есть веская причина, неясно, откуда взялась настоящая причина исключения. Было ли это потому, что вызов DynamicInvoke() запущен? Не исключено, что компилятор ничего не может сделать, чтобы обеспечить правильные аргументы. Или вызываемый метод цели бросает сам по себе?

Вы должны знать как, чтобы судить о реальной причине исключения. Умышленное скрытие TargetInvocationException даст вам hard время, чтобы диагностировать источник проблемы, если это действительно проблема с вызовом DynamicInvoke(). Избегайте этого.

+1

Я ценю рассуждения, но это затрудняет запись потребляющего кода, поскольку гибкость явно улавливания конкретных ошибок ушла. –

+1

Уловки исключений в этом сценарии чрезвычайно сложны в любом случае. Вы ничего не знаете о цели делегата, вы не можете догадаться, как он изменил состояние программы. И при этом вы не можете восстановить состояние при обработке исключения. –

+0

В одном случае, когда TIE путает материю, вы имеете контроль над «внутренним» кодом, который генерирует исключение, и внешним кодом, который хочет обрабатывать исключения. Если внутренний код вводится с использованием чего-то, что обертывает его в динамическом прокси, теперь становится сложнее поймать исключения, которые вы бросаете из внутреннего кода. Это происходит, например, с ORM, где некоторые из классов сущностей могут быть обернуты, что желательно, чтобы окружающий код не нуждался в заботе. –

Смежные вопросы