Как перестроить внутреннее исключение TargetInvocationException без потери трассировки стека

у меня есть много методов, которые вызывают с помощью 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 ответов


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

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), а также используется асинхронным CTP его системой автоматического разворачивания (без публично открытого API).

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

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


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

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


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