diff --git a/Operation.sln b/Operation.sln index 0f1f1ee..7ccaa05 100644 --- a/Operation.sln +++ b/Operation.sln @@ -1,21 +1,22 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26114.2 +VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{28601411-6B83-4FE5-8DD0-F6A9C60559C1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{139E940F-CEEE-42C5-A627-C5E2CF9DCBBB}" ProjectSection(SolutionItems) = preProject + appveyor.yml = appveyor.yml + build.ps1 = build.ps1 Changelog.md = Changelog.md ReadMe.md = ReadMe.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Operation", "src\Operation\Operation.csproj", "{19B61102-2672-4429-96C8-D60FAEA34A88}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Operation", "src\Operation\Operation.csproj", "{19B61102-2672-4429-96C8-D60FAEA34A88}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{497C936C-DAFA-4A50-9B9F-0CB02D6AD26F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "test\Tests\Tests.csproj", "{7E284F74-CED3-41C3-B109-839D41ADFA81}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "test\Tests\Tests.csproj", "{7E284F74-CED3-41C3-B109-839D41ADFA81}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,4 +40,7 @@ Global {19B61102-2672-4429-96C8-D60FAEA34A88} = {28601411-6B83-4FE5-8DD0-F6A9C60559C1} {7E284F74-CED3-41C3-B109-839D41ADFA81} = {497C936C-DAFA-4A50-9B9F-0CB02D6AD26F} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F43CD339-09EC-4B43-9B16-F187BA45327E} + EndGlobalSection EndGlobal diff --git a/ReadMe.md b/ReadMe.md index e9e0928..84f196c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -12,7 +12,7 @@ that tells the calling method that it will not throw an exception rather, any ex Using `Operation` helps ensure that your applications can fail gracefully even in unforeseen circumstances. It's typically used at the boundries between domains/layers in your application.eg. Between Calls from WebApi to Business Layer or between calls from your Business Layer to your DataAccess Layer -##Operation and Operation<T> +## Operation and Operation<T> At the Heart of the library are two types. They are `Operation` and `Operation`. An Operation represents the output of a piece of computation. It has two states: Succeeded or Failed. @@ -21,13 +21,13 @@ It also contains a `GetException()` Method that returns the original Exception i It also contains a helpful message that states why the piece of computation failed. `Operation` includes a `Result` property that contains the Result of that Computation. -##Installation +## Installation You can install Operation library via Nuget: `install-package Operation` -##Features -###1. Fault Tolerance +## Features +### 1. Fault Tolerance ```csharp public Operation ErrorProneFunction() @@ -42,8 +42,13 @@ var operation = ErrorProneFunction() var suceeded = operation.Succeeded //False var message = operation.Message //Halt and Catch Fire ``` +There are also helper functions for creating Operation Objects +```csharp +Operation succeededOperation = Operation.Success(20); //Returns a Successful Operation +Operation failedOperation = Operation.Fail("Error Here"); //Returns a Failed Operation with the Corresponding Page +``` -###2. Operation Chaining +### 2. Operation Chaining You can chain multiple Operations together to produce a compound Operation ```csharp @@ -75,7 +80,14 @@ var result = compoundOp.Result; // "10 CUPS" ``` `r1` and `r2` are the return values of `ErrorFunction1` and `ErrorFunction2` respectively. -###3. Linq Syntax +If the Operation did not succeed, you can chain an error + +```csharp +var failed = Operation.Fail("Error Occured"); +failed.Catch(o => Console.WriteLine(o.Message)); +``` + +### 3. Linq Syntax You can also chain operations using Linq query syntax. This makes the flow of execution clearer. It also allows you to easily combine the interim results of operations as shown below @@ -102,7 +114,7 @@ This should simplify chaining combining the results across operations. *NOTE* - Using the LINQ Syntax with `Operation` returns `null`. Trying to access its properties will produce an null reference exception that won't be caught -###4. Batch Operations +### 4. Batch Operations The linq syntax is also extended to allow you to mix Linq Queries with Operation Queries. This is useful for doing batch operations in a safe way @@ -145,7 +157,7 @@ var all = new[] { op1, op2 }.Fold((a, s) => a + " " + s); var succeeded = all.Succeeded; //true var result = all.Result; //"Hello World" ``` -###5. Operation Dependency +### 5. Operation Dependency If an Operation depends on another operation, you simply call the `Unwrap()` method on the dependent Operation and it halts exectuion and raises the error for the main operation to catch. @@ -177,7 +189,7 @@ var operation = Operation.Create(() => { }); ``` -###6. Async Support +### 6. Async Support ```csharp Task> asyncOp = Operation.Run(async () => { var result = await SomeLongRunningProcess(); @@ -191,7 +203,7 @@ var message = asyncOp.Result.Message //Returns the message of the var result = asyncOp.Result.Result //Result of SomeLongRunningProcess() ``` -###7. Conversion +### 7. Conversion Its also easy to Convert Operations to Tasks for APIs that Require Tasks ```csharp diff --git a/src/Operation/Extensions.cs b/src/Operation/Extensions.cs index 6d76eb4..0f26148 100644 --- a/src/Operation/Extensions.cs +++ b/src/Operation/Extensions.cs @@ -244,49 +244,13 @@ public static Operation Fold(this IEnumerable> operations, Fu } } - - //public static Operation Fold(this IEnumerable operations) - //{ - // using (var e = operations.GetEnumerator()) - // { - // if (!e.MoveNext()) - // { - // var ex = new InvalidOperationException("Sequence contains no Elements"); - // return new Operation(ex) - // { - // Message = ex.Message, - // }; - // } - - // List badOperations = new List(); - // do - // { - // //If the Current Operation did not succeed, add it - // if (!e.Current.Succeeded) - // { - // badOperations.Add(e.Current); - // }; - // } - // while (e.MoveNext()); - - - // //If there were any bad operations - // if (badOperations.Any()) - // { - // return new Operation - // { - // Message = badOperations.Select(o => o.Message).Aggregate((ag, m) => ag + ", " + m), - // Succeeded = false - // }; - // } - // else - // { - // return new Operation() - // { - // Succeeded = true, - // }; - // } - // } - //} + public static Operation Catch(this Operation operation, Action> func) + { + if (!operation.Succeeded) + { + func(operation); + } + return operation; + } } } diff --git a/src/Operation/Unit.cs b/src/Operation/Unit.cs index d968d2b..9697876 100644 --- a/src/Operation/Unit.cs +++ b/src/Operation/Unit.cs @@ -74,7 +74,7 @@ public static async Task Run(Func process) } /// - /// Creates an new Operation by Invoking an Async Delegate + /// Creates a new Operation by Invoking an Async Delegate /// /// /// @@ -124,7 +124,7 @@ public static Operation Fail(string message) public partial class Operation { [DebuggerHidden] - public void Catch(Exception ex) + internal void Catch(Exception ex) { _exception = ex; while (ex.InnerException != null) ex = ex.InnerException; diff --git a/test/Tests/AsyncTests.cs b/test/Tests/AsyncTests.cs new file mode 100644 index 0000000..52775d8 --- /dev/null +++ b/test/Tests/AsyncTests.cs @@ -0,0 +1,63 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Tests +{ + [TestClass] + public class AsyncTests + { + [TestMethod] + public void AsyncOperationCreationSuccess() + { + var task = Operation.Run(async () => + { + await Task.Run(() => Console.WriteLine("Hello Operation")); + }); + + task.Wait(); + + Assert.IsTrue(task.Result.Succeeded); + } + + [TestMethod] + public async Task AsyncOperationCreationFailure() + { + var task = Operation.Run(() => + { + return Task.Run(() => + { + Console.WriteLine("Hello Operation"); + throw new Exception("The Error"); + }); + }); + + var result = await task; + + Assert.IsFalse(result.Succeeded); + Assert.AreEqual(result.Message, "The Error"); + } + + [TestMethod] + public async Task AsyncOperationTCreationFailure() + { + var task = Operation.Run(() => + { + return Task.Run(() => + { + Console.WriteLine("Hello Operation"); + var x = true; + if(x) throw new Exception("The Error"); + return 1; + }); + }); + + var result = await task; + + Assert.IsFalse(result.Succeeded); + Assert.AreEqual(result.Message, "The Error"); + } + } +} diff --git a/test/Tests/ExtensionsTest.cs b/test/Tests/ExtensionsTest.cs index 58214ce..2c12bc0 100644 --- a/test/Tests/ExtensionsTest.cs +++ b/test/Tests/ExtensionsTest.cs @@ -102,5 +102,13 @@ public void GenericAllWithFailure() Assert.IsFalse(all.Succeeded); Assert.AreEqual("Something Bad Happened", all.Message); } + + [TestMethod] + public void CatchT() + { + var message = ""; + var op1 = Operation.Fail("An Error").Catch(o => message = o.Message); + Assert.AreEqual("An Error", message); + } } } diff --git a/test/Tests/UnitTest.cs b/test/Tests/UnitTest.cs index a387a0c..cfb214c 100644 --- a/test/Tests/UnitTest.cs +++ b/test/Tests/UnitTest.cs @@ -60,33 +60,7 @@ public void OperationResultFailure() Assert.AreEqual(operation.Message, "The Error"); Assert.AreEqual(operation.Result, default(int)); } - [TestMethod] - public void AsyncOperationCreationSuccess() - { - var task = Operation.Run(async () => - { - await Task.Run(() => Console.WriteLine("Hello Operation")); - }); - task.Wait(); - - Assert.IsTrue(task.Result.Succeeded); - } - - [TestMethod] - public void AsyncOperationCreationFailure() - { - var task = Operation.Run(async () => - { - await Task.Run(() => Console.WriteLine("Hello Operation")); - throw new Exception("The Error"); - }); - - task.Wait(); - - Assert.IsFalse(task.Result.Succeeded); - Assert.AreEqual(task.Result.Message, "The Error"); - } [TestMethod] public void OperationFail()