From a3b81fa1324bfc7baffafc3f2979874d848a6478 Mon Sep 17 00:00:00 2001 From: albert-du <52804499+albert-du@users.noreply.github.com> Date: Thu, 18 Nov 2021 21:12:55 -0800 Subject: [PATCH 1/3] Add F# snippets for System.AggregateException --- .../fs/exception1.fs | 56 +++++++++++++++++++ .../fs/exception1.fsproj | 12 ++++ .../tpl_exceptions/fs/flatten2.fs | 38 +++++++++++++ .../tpl_exceptions/fs/handlemethod2.fs | 26 +++++++++ .../tpl_exceptions/fs/taskexceptions.fs | 53 ++++++++++++++++++ .../tpl_exceptions/fs/taskexceptions2.fs | 43 ++++++++++++++ .../tpl_exceptions/fs/tpl_exceptions.fsproj | 15 +++++ xml/System/AggregateException.xml | 3 + 8 files changed, 246 insertions(+) create mode 100644 samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs create mode 100644 samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fsproj create mode 100644 samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/flatten2.fs create mode 100644 samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/handlemethod2.fs create mode 100644 samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions.fs create mode 100644 samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions2.fs create mode 100644 samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/tpl_exceptions.fsproj diff --git a/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs new file mode 100644 index 00000000000..4a3b4ed4e4f --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs @@ -0,0 +1,56 @@ +// +open System +open System.IO +open System.Threading.Tasks + +let getAllFiles str = + // Should throw an UnauthorizedAccessException exception. + System.IO.Directory.GetFiles(str, "*.txt", System.IO.SearchOption.AllDirectories) + +// Get a folder path whose directories should throw an UnauthorizedAccessException. +let path = + Environment.SpecialFolder.UserProfile + |> Environment.GetFolderPath + |> Directory.GetParent + |> fun x -> x.FullName + +// Use this line to throw an exception that is not handled. +// let task1 = Task.Factory.StartNew(fun () -> raise (IndexOutOfRangeException()) ) +let task1 = Task.Factory.StartNew(fun () -> getAllFiles (path)) + +try + Async.AwaitTask task1 + |> Async.Ignore + |> Async.RunSynchronously +with +| :? UnauthorizedAccessException -> printfn "Caught unauthorized access exception-await behavior" +| :? AggregateException as ae -> + printfn "Caught aggregate exception-Task.Wait behavior" + + ae.Handle (fun x -> + match x with + | :? UnauthorizedAccessException -> + printfn "You do not have permission to access all folders in this path." + printfn "See your network administrator or try another path." + true + | _ -> false) +printfn $"""task1 Status: {if task1.IsCompleted then "Completed," else ""}{task1.Status}""" + +// The example displays the following output if the file access task is run: +// You do not have permission to access all folders in this path. +// See your network administrator or try another path. +// task1 Status: Completed,Faulted +// It displays the following output if the second task is run: +// Unhandled exception. System.AggregateException: One or more errors occurred. (Index was outside the bounds of the array.) (Index was outside the bounds of the array.) +// ---> System.IndexOutOfRangeException: Index was outside the bounds of the array. +// at Exception1.task1@19.Invoke() +// at System.Threading.Tasks.Task`1.InnerInvoke() +// at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj) +// at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) +// --- End of stack trace from previous location --- +// at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) +// at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) +// --- End of inner exception stack trace --- +// at System.AggregateException.Handle(Func`2 predicate) +// at .$Exception1.main@() +// diff --git a/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fsproj b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fsproj new file mode 100644 index 00000000000..489dceab6c8 --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fsproj @@ -0,0 +1,12 @@ + + + + Exe + net5.0 + + + + + + + diff --git a/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/flatten2.fs b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/flatten2.fs new file mode 100644 index 00000000000..394e25abd32 --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/flatten2.fs @@ -0,0 +1,38 @@ +module flatten2 +// +open System +open System.Threading.Tasks + +type CustomException(message) = + inherit Exception(message) + +let task1 = + Task.Factory.StartNew (fun () -> + let child1 = + Task.Factory.StartNew( + (fun () -> + let child2 = + Task.Factory.StartNew( + (fun () -> raise (CustomException "Attached child2 faulted,")), + TaskCreationOptions.AttachedToParent + ) + raise (CustomException "Attached child1 faulted.")), + TaskCreationOptions.AttachedToParent + ) + () + ) + +try + task1.Wait() +with +| :? AggregateException as ae -> + for e in ae.Flatten().InnerExceptions do + if e :? CustomException then + printfn "%s" e.Message + else + reraise() + +// The example displays the following output: +// Attached child1 faulted. +// Attached child2 faulted. +// diff --git a/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/handlemethod2.fs b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/handlemethod2.fs new file mode 100644 index 00000000000..c879e554195 --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/handlemethod2.fs @@ -0,0 +1,26 @@ +module handlemethod2 +// +open System +open System.Threading.Tasks + +type CustomException(message) = + inherit Exception(message) + +let task1 = + Task.Run(fun () -> raise (CustomException "This exception is expected!")) + +try + task1.Wait() +with +| :? AggregateException as ae -> + // Call the Handle method to handle the custom exception, + // otherwise rethrow the exception. + ae.Handle (fun ex -> + if ex :? CustomException then + printfn $"{ex.Message}" + + ex :? CustomException) + +// The example displays the following output: +// This exception is expected! +// diff --git a/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions.fs b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions.fs new file mode 100644 index 00000000000..b69a636c397 --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions.fs @@ -0,0 +1,53 @@ +module taskexceptions +// +open System +open System.IO +open System.Threading.Tasks + +let getAllFiles path = + let task1 = + Task.Run(fun () -> Directory.GetFiles(path, "*.txt", SearchOption.AllDirectories)) + + try + task1.Result + with + | :? AggregateException as ae -> + ae.Handle (fun x -> + // Handle an UnauthorizedAccessException + if x :? UnauthorizedAccessException then + printfn "You do not have permission to access all folders in this path." + printfn "See your network administrator or try another path." + + x :? UnauthorizedAccessException) + + Array.empty + +// This should throw an UnauthorizedAccessException. +try + let files = getAllFiles @"C:\" + + if not (isNull files) then + for file in files do + printfn $"{file}" +with +| :? AggregateException as ae -> + for ex in ae.InnerExceptions do + printfn $"{ex.GetType().Name}: {ex.Message}" + +printfn "" + +// This should throw an ArgumentException. +try + for s in getAllFiles "" do + printfn $"{s}" +with +| :? AggregateException as ae -> + for ex in ae.InnerExceptions do + printfn $"{ex.GetType().Name}: {ex.Message}" + +// The example displays the following output: +// You do not have permission to access all folders in this path. +// See your network administrator or try another path. +// +// ArgumentException: The path is empty. (Parameter 'path') +// diff --git a/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions2.fs b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions2.fs new file mode 100644 index 00000000000..92d8034a6d7 --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions2.fs @@ -0,0 +1,43 @@ +module taskexceptions2 +// +open System +open System.IO +open System.Threading.Tasks + +let executeTasks () = + // Assume this is a user-entered String. + let path = @"C:\" + + let tasks = + [| Task.Run (fun () -> + // This should throw an UnauthorizedAccessException. + Directory.GetFiles(path, "*.txt", SearchOption.AllDirectories)) + :> Task + Task.Run (fun () -> + if path = @"C:\" then + raise (ArgumentException "The system root is not a valid path.") + + [| ".txt"; ".dll"; ".exe"; ".bin"; ".dat" |]) + :> Task + Task.Run(fun () -> raise (NotImplementedException "This operation has not been implemented")) |] + + try + Task.WaitAll(tasks) + with + | :? AggregateException as ae -> raise (ae.Flatten()) + +try + executeTasks () +with +| :? AggregateException as ae -> + for e in ae.InnerExceptions do + printfn $"{e.GetType().Name}:\n {e.Message}" + +// The example displays the following output: +// UnauthorizedAccessException: +// Access to the path 'C:\Documents and Settings' is denied. +// ArgumentException: +// The system root is not a valid path. +// NotImplementedException: +// This operation has not been implemented. +// diff --git a/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/tpl_exceptions.fsproj b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/tpl_exceptions.fsproj new file mode 100644 index 00000000000..01c77cf1e9a --- /dev/null +++ b/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/tpl_exceptions.fsproj @@ -0,0 +1,15 @@ + + + + Exe + net5.0 + + + + + + + + + + diff --git a/xml/System/AggregateException.xml b/xml/System/AggregateException.xml index f0de8893cbe..bb9f1fc4fb4 100644 --- a/xml/System/AggregateException.xml +++ b/xml/System/AggregateException.xml @@ -71,6 +71,7 @@ The following example catches the exception and calls the method to handle each exception it contains. Compiling and running the example with the first `task1` variable should result in an object that contains an exception. Commenting out that line, uncommenting the second `task1` variable, and compiling and running the example produces an object that contains an exception. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR_System/system.aggregateexception.class/cs/exception1.cs" id="Snippet1"::: + :::code language="fsharp" source="~/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs" id="Snippet1"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_CLR_System/system.aggregateexception.class/vb/exception1.vb" id="Snippet1"::: ]]> @@ -712,11 +713,13 @@ Ordinarily, an exception handler that catches an exception uses a `foreach` loop (in C#) or `For Each` loop (in Visual Basic) to handle each exception in its collection. Instead, the following example uses the method to handle each exception, and only re-throws exceptions that are not `CustomException` instances. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_Misc/tpl_exceptions/cs/handlemethod2.cs" id="Snippet16"::: + :::code language="fsharp" source="~/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/handlemethod2.fs" id="Snippet16"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_Misc/tpl_exceptions/vb/handlemethod2.vb" id="Snippet16"::: The following is a more complete example that uses the method to provide special handling for an when enumerating files. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_Misc/tpl_exceptions/cs/taskexceptions.cs" id="Snippet12"::: + :::code language="fsharp" source="~/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions.fs" id="Snippet12"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_Misc/tpl_exceptions/vb/taskexceptions.vb" id="Snippet12"::: ]]> From 930a8b1dbd0eb11496acfe7beff99e96adc6207f Mon Sep 17 00:00:00 2001 From: albert-du <52804499+albert-du@users.noreply.github.com> Date: Fri, 19 Nov 2021 06:45:02 -0800 Subject: [PATCH 2/3] Update AggregateException.xml --- xml/System/AggregateException.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xml/System/AggregateException.xml b/xml/System/AggregateException.xml index bb9f1fc4fb4..21895053bef 100644 --- a/xml/System/AggregateException.xml +++ b/xml/System/AggregateException.xml @@ -549,11 +549,13 @@ In the following example, nested instances are flattened and handled in just one loop. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_Misc/tpl_exceptions/cs/flatten2.cs" id="Snippet22"::: + :::code language="fsharp" source="~/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/flatten2.fs" id="Snippet22"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_Misc/tpl_exceptions/vb/flatten2.vb" id="Snippet22"::: You can also use the method to rethrow the inner exceptions from multiple instances thrown by multiple tasks in a single instance, as the following example shows. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_Misc/tpl_exceptions/cs/taskexceptions2.cs" id="Snippet13"::: + :::code language="fsharp" source="~/samples/snippets/fsharp/VS_Snippets_Misc/tpl_exceptions/fs/taskexceptions2.fs" id="Snippet13"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_Misc/tpl_exceptions/vb/taskexceptions2.vb" id="Snippet13"::: ]]> From d0d0ac16b7b2bac12e862a4834b967bbe48ba5e4 Mon Sep 17 00:00:00 2001 From: albert-du <52804499+albert-du@users.noreply.github.com> Date: Fri, 19 Nov 2021 06:45:45 -0800 Subject: [PATCH 3/3] Update exception1.fs --- .../fs/exception1.fs | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs index 4a3b4ed4e4f..aed5876bf0d 100644 --- a/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs +++ b/samples/snippets/fsharp/VS_Snippets_CLR_System/system.aggregateexception.class/fs/exception1.fs @@ -9,33 +9,35 @@ let getAllFiles str = // Get a folder path whose directories should throw an UnauthorizedAccessException. let path = - Environment.SpecialFolder.UserProfile - |> Environment.GetFolderPath - |> Directory.GetParent - |> fun x -> x.FullName + let directory = + Environment.SpecialFolder.UserProfile + |> Environment.GetFolderPath + |> Directory.GetParent + directory.FullName // Use this line to throw an exception that is not handled. // let task1 = Task.Factory.StartNew(fun () -> raise (IndexOutOfRangeException()) ) let task1 = Task.Factory.StartNew(fun () -> getAllFiles (path)) -try - Async.AwaitTask task1 - |> Async.Ignore - |> Async.RunSynchronously -with -| :? UnauthorizedAccessException -> printfn "Caught unauthorized access exception-await behavior" -| :? AggregateException as ae -> - printfn "Caught aggregate exception-Task.Wait behavior" +let execute () = + try + task1.Wait() + with + | :? UnauthorizedAccessException -> printfn "Caught unauthorized access exception-await behavior" + | :? AggregateException as ae -> + printfn "Caught aggregate exception-Task.Wait behavior" + + ae.Handle (fun x -> + match x with + | :? UnauthorizedAccessException -> + printfn "You do not have permission to access all folders in this path." + printfn "See your network administrator or try another path." + true + | _ -> false) + printfn $"""task1 Status: {if task1.IsCompleted then "Completed," else ""}{task1.Status}""" + +execute () - ae.Handle (fun x -> - match x with - | :? UnauthorizedAccessException -> - printfn "You do not have permission to access all folders in this path." - printfn "See your network administrator or try another path." - true - | _ -> false) -printfn $"""task1 Status: {if task1.IsCompleted then "Completed," else ""}{task1.Status}""" - // The example displays the following output if the file access task is run: // You do not have permission to access all folders in this path. // See your network administrator or try another path.