Skip to content

Commit

Permalink
Added Catch Operator for Error Cases #8
Browse files Browse the repository at this point in the history
  • Loading branch information
odytrice committed Nov 6, 2017
1 parent 35108a4 commit 3966659
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 86 deletions.
12 changes: 8 additions & 4 deletions Operation.sln
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
32 changes: 22 additions & 10 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>`.
An Operation represents the output of a piece of computation. It has two states: Succeeded or Failed.
Expand All @@ -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<T>`
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()
Expand All @@ -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<int> succeededOperation = Operation.Success(20); //Returns a Successful Operation<int>
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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -177,7 +189,7 @@ var operation = Operation.Create(() => {
});
```

###6. Async Support
### 6. Async Support
```csharp
Task<Operation<T>> asyncOp = Operation.Run(async () => {
var result = await SomeLongRunningProcess();
Expand All @@ -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
Expand Down
52 changes: 8 additions & 44 deletions src/Operation/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,49 +244,13 @@ public static Operation<T> Fold<T>(this IEnumerable<Operation<T>> operations, Fu
}
}


//public static Operation Fold(this IEnumerable<Operation> 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<Operation> badOperations = new List<Operation>();
// 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<T> Catch<T>(this Operation<T> operation, Action<Operation<T>> func)
{
if (!operation.Succeeded)
{
func(operation);
}
return operation;
}
}
}
4 changes: 2 additions & 2 deletions src/Operation/Unit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public static async Task<Operation> Run(Func<Task> process)
}

/// <summary>
/// Creates an new Operation by Invoking an Async Delegate
/// Creates a new Operation by Invoking an Async Delegate
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="process"></param>
Expand Down Expand Up @@ -124,7 +124,7 @@ public static Operation<T> Fail<T>(string message)
public partial class Operation<T>
{
[DebuggerHidden]
public void Catch(Exception ex)
internal void Catch(Exception ex)
{
_exception = ex;
while (ex.InnerException != null) ex = ex.InnerException;
Expand Down
63 changes: 63 additions & 0 deletions test/Tests/AsyncTests.cs
Original file line number Diff line number Diff line change
@@ -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");
}
}
}
8 changes: 8 additions & 0 deletions test/Tests/ExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
26 changes: 0 additions & 26 deletions test/Tests/UnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 3966659

Please sign in to comment.