Как перебросить внутреннее исключение TargetInvocationException без потери трассировки стека - PullRequest
20 голосов
/ 29 декабря 2010

У меня есть много методов, которые вызывают с помощью Delegate.DynamicInvoke. Некоторые из этих методов делают вызовы из базы данных, и я хотел бы иметь возможность поймать SqlException, а не поймать 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(); 
}

Ответы [ 3 ]

29 голосов
/ 29 декабря 2010

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

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 для этого в будущем. * * * * * * * * * * * * * * * * * * * * * * *. В Microsoft Connect есть предложение , если вы хотите проголосовать за него.

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

2 голосов
/ 29 декабря 2010

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

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

0 голосов
/ 29 декабря 2010

IIRC невозможно полностью сохранить исключение, однако трассировка стека может быть сохранена с некоторым отражением.Вот сообщение в блоге, описывающее, как это сделать: http://iridescence.no/post/Preserving-Stack-Traces-When-Re-Throwing-Inner-Exceptions.aspx

...