web-dev-qa-db-ja.com

処理のためのAggregateExceptionsのフラット化

flattenAggregateExceptionを呼び出すいくつかの問題が発生していますが、内部には別のAggregateExceptionがあります。これは明らかに、それらが連鎖的に伝播され、別のAggregateExceptionにロールインされていることを意味します。すべての内部AggregateExceptionを再帰的にフラット化する方法はありますか?通常、ハンドルデリゲートを使用してこれらを処理しますが、別の内部AggregateExceeptionがある場合はfalseを返します。これらを適切に扱っていませんか?

編集:私はすでにフラット化を呼び出しているので、問題はそれがコールスタックのかなり後までキャッチされないことです。これがFlatten()を呼び出すコードです。スタックトレースで使用するために、このメソッドはWriteExceptionRecord(string、FileInfo)と呼ばれます。

do
{
    try
    {
        using (var stream = file.Open(FileMode.Append, FileAccess.Write, FileShare.None))
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                await writer.WriteLineAsync(data);
            }
        }
    }
    catch (AggregateException ex)
    {
        ex.Flatten().Handle((x) =>
        {
            if (x is IOException)
            {
                retryNeeded = true;
                retryLeft--;
                Thread.Sleep(500);
                return true;
            }

            logger.ErrorException("Could not write to exception file: " + data, ex);
            return false;
        });
    }
}
while (retryNeeded && retryLeft > 0);

ただし、スタックトレースは、ここでは捕捉されていないことを示しています。代わりに、コールスタックのかなり後の方でキャッチされます。以下は、セキュリティ上の理由から一部の識別情報が削除されたトレースです。

System.AggregateException: One or more errors occurred. --->      
System.AggregateException: One or more errors occurred. --->
System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.    
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)    
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)    
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)    
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)    
   at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---  
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61

   --- End of inner exception stack trace ---

   --- End of inner exception stack trace ---

---> (Inner Exception #0) System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)
   at PeopleDocImporter.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61

   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)
   at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<---

<---

System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.    
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)    
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)    
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)    
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)    
   at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61

   --- End of inner exception stack trace ---

---> (Inner Exception #0) System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\J\PD\Exception.csv' because it is being used by another process.    
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)    
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)    
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)    
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)    
   at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61

   --- End of inner exception stack trace ---    
---> (Inner Exception #0) System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.    
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)    
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)    
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)    
   at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)    
   at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316

--- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)    
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()    
   at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<---

ちなみに、これはTPL-Dataflowブロックによって呼び出されています。

22
JNYRanger

'flatten'メソッドは例外のリストを提供しますが、各例外の内部にフラット化されたInnerExceptionが残る可能性があることに注意してください。

だから私はこれは本当に十分ではないことがわかりました:

try
{
    // something dangerous
}
catch (AggregateException ae)
{ 
    foreach(Exception innerException in ae.Flatten().InnerExceptions)
    {
        Console.WriteLine(innerException.Message());
    }
}

この例外のため:

System.Net.Http.HttpRequestException:リクエストの送信中にエラーが発生しました。 ---> System.Net.WebException:リモートサーバーに接続できません---> System.Net.Sockets.SocketException:一定時間または接続が確立された後、接続先が適切に応答しなかったため、接続試行が失敗しました接続されたホストがSystem.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure、Socket s4、Socket s6、Socket&socket、IPAddress&addressのSystem.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)で192.168.42.55:443の応答に失敗したため失敗、ConnectSocketState状態、IAsyncResult asyncResult、Exception&例外)---内部例外スタックトレースの終わり--- System.Net.Http.WebRequest.EndGetRequestStream(IAsyncResult asyncResult、TransportContext&context)at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar )---内部例外スタックトレースの終了---

このようになるでしょう:

リクエストの送信中にエラーが発生しました。

修正は次のようなものでした:

foreach(Exception exInnerException in aggEx.Flatten().InnerExceptions)
{
    Exception exNestedInnerException = exInnerException;
    do
    {
        if (!string.IsNullOrEmpty(exNestedInnerException.Message))
        {
            Console.WriteLine(exNestedInnerException.Message);
        }
        exNestedInnerException = exNestedInnerException.InnerException;
    }
    while (exNestedInnerException != null);
}

その結果:

リクエストの送信中にエラーが発生しました。

リモートサーバーに接続できません

一定時間後に接続された側が適切に応答しなかったために接続試行が失敗したか、接続されたホストが応答しなかったために確立された接続が失敗した192.168.42.54:443

それが誰かを助けることを願っています。

26

はい、あなたが求めているものがまさにあります:

AggreggateException.Flatten()

すべてを通過し、すべてを単一のAggregateExceptionに圧縮します。したがって、これを使用して、次のようなすべての内部例外をループできます。

try
{
    // something dangerous
}
catch (AggregateException ae)
{ 
    foreach(var innerException in ae.Flatten().InnerExceptions)
    {
        // handle error
    }
}

MSDNリンク: http://msdn.Microsoft.com/en-us/library/system.aggregateexception.flatten.aspx

25
Sean U

これは古い質問ですが、OPで発生する問題は、awaitが待機中のタスクからAggregateExceptionを公開せず、代わりに最初の例外のみを公開することです。 AggregateException。したがって、catch(AggregateException ex)ブロックはバイパスされ、例外はスタックのさらに上位でキャッチされます。したがって、コードは「単純に」されているはずです:

retryNeeded = false;
do
{
    try
    {
        if (retryNeeded)
            await Task.Delay(500); // substituted for Thread.Sleep

        using (var stream = file.Open(FileMode.Append, FileAccess.Write, FileShare.None))
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                await writer.WriteLineAsync(data);
                retryNeeded = false;
            }
        }
    }
    catch (IOException)
    {
        retryNeeded = true;
        retryLeft--;
    }
    catch (Exception ex)
    {
        logger.ErrorException("Could not write to exception file: " + data, ex);
        throw;
    }
} while (retryNeeded && retryLeft > 0);

return (retryLeft > 0);

または、 Jon SkeetのWithAllExceptions拡張メソッド は、AggregateExceptionを含むAggregateExceptionを取得し、元の/内部のAggregateExceptionが「返される」のを待つように、別のタスクでタスクをラップすることにより、AggregateExceptionを待機から保護することができます。 。

注:AggregateException.Flattenは、 MSDNページ の例に示されているように、実際に再帰的に「フラット化」します。

EDIT:不正な非同期の例の設定を回避するために、retryNeededの遅延を改善しました。

7
SensorSmith

通常、AggregateExceptionは、複数の失敗を単一のスロー可能な例外オブジェクトに統合するために使用されます。

try {
          Task.WaitAll(tasks)
      }
      catch (AggregateException ae) {
          ae.Handle((x) =>
          {
              if (x is UnauthorizedAccessException) // This we know how to handle.
              {
                 //do your code here  
              }
               return true; //if you do something like this all exceptions are marked as handled  
           });
      }
4
BRAHIM Kamel

以下のサンプルコードを試してください。これにより、awaitAggregateExceptionをアンラップすることを回避し、task.resultが呼び出された時点で元のAggregateExceptionをスローします。

var task = writer.WriteLineAsync(data);
            await task.ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously));
            return task.Result;
0
Dogu Arslan