Skip to content

Commit d624950

Browse files
committed
Add tutorial41 about dealing with exceptions
1 parent 9992323 commit d624950

17 files changed

+701
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Tutorial41
7+
{
8+
public class AggregatePipelineFailure : IAmFailure
9+
{
10+
public AggregatePipelineFailure(IEnumerable<IAmFailure> failures)
11+
{
12+
frequencies = new Dictionary<IAmFailure, int>();
13+
var failureNames = failures.GroupBy(o => o.GetType().Name + o.Reason);
14+
var sb = new StringBuilder();
15+
foreach (var name in failureNames)
16+
{
17+
var lookupFailure = failures.First(o=>(o.GetType().Name + o.Reason).Equals(name.Key));
18+
if(!frequencies.ContainsKey(lookupFailure))
19+
frequencies.Add(lookupFailure, name.Count());
20+
21+
sb.Append($"Failure name: {name.Key} Count: {name.Count()}\n");
22+
}
23+
24+
Reason = sb.ToString();
25+
}
26+
public string Reason { get; set; }
27+
public Dictionary<IAmFailure, int> frequencies {get;set;}
28+
public static IAmFailure Create(IEnumerable<IAmFailure> failures) => new AggregatePipelineFailure(failures);
29+
}
30+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
namespace Tutorial41
3+
{
4+
public class ConditionNotSatisfiedFailure : IAmFailure
5+
{
6+
public ConditionNotSatisfiedFailure(string caller = "Caller not captured")
7+
{
8+
Reason = $"Condition not satisfied. Caller: {caller}";
9+
}
10+
public string Reason { get; set; }
11+
public static IAmFailure Create(string caller) => new ConditionNotSatisfiedFailure(caller);
12+
}
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
using System;
4+
5+
namespace Tutorial41
6+
{
7+
public class ExceptionFailure : Exception, IAmFailure
8+
{
9+
public ExceptionFailure(Exception e) => Reason = e.Message;
10+
public string Reason { get; set; }
11+
}
12+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
using System;
3+
4+
namespace Tutorial41
5+
{
6+
public class ExternalLibraryFailure : IAmFailure
7+
{
8+
public ExternalLibraryFailure(Exception exception)
9+
{
10+
Reason = exception.Message;
11+
Exception = exception;
12+
}
13+
14+
public ExternalLibraryFailure(string message)
15+
{
16+
Reason = message;
17+
}
18+
19+
public string Reason { get; set; }
20+
public Exception Exception { get; }
21+
22+
public static IAmFailure Create(string message) => new ExternalLibraryFailure(message);
23+
public static IAmFailure Create(Exception e) => new ExternalLibraryFailure(e);
24+
}
25+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
namespace Tutorial41
3+
{
4+
/// <summary>
5+
/// Represents a failure for some reason
6+
/// </summary>
7+
public interface IAmFailure
8+
{
9+
/// <summary>
10+
/// Nature of the failure
11+
/// </summary>
12+
string Reason { get; set; }
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
namespace Tutorial41
3+
{
4+
public class InvalidDataFailure : IAmFailure
5+
{
6+
public InvalidDataFailure(string empty)
7+
{
8+
Reason = empty;
9+
}
10+
11+
public string Reason { get; set; }
12+
public static IAmFailure Create(string message) => new InvalidDataFailure(message);
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
namespace Tutorial41
3+
{
4+
public class NotFound : IAmFailure
5+
{
6+
public NotFound(string message)
7+
{
8+
Reason = message;
9+
}
10+
public string Reason { get; set; }
11+
12+
public static IAmFailure Create(string message) => new NotFound(message);
13+
}
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
using System;
3+
4+
namespace Tutorial41
5+
{
6+
public class NotTypeExceptionFailure : IAmFailure
7+
{
8+
public NotTypeExceptionFailure(Type type)
9+
{
10+
Reason = $"Function did not return expected type of '{type}'";
11+
}
12+
13+
public string Reason { get; set; }
14+
15+
public static IAmFailure Create(Type type) => new NotTypeExceptionFailure(type);
16+
}
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
namespace Tutorial41
3+
{
4+
public class ShortCircuitFailure : IAmFailure
5+
{
6+
public ShortCircuitFailure(string message)
7+
{
8+
Reason = message;
9+
}
10+
11+
public string Reason { get; set; }
12+
public static IAmFailure Create(string msg) => new ShortCircuitFailure(msg);
13+
}
14+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//-----------------------------------------------------------------------
2+
3+
// <copyright file="TransformExceptionFailure.cs" company="Stuart Mathews">
4+
5+
// Copyright (c) Stuart Mathews. All rights reserved.
6+
7+
// <author>Stuart Mathews</author>
8+
9+
// <date>03/10/2021 13:16:11</date>
10+
11+
// </copyright>
12+
13+
//-----------------------------------------------------------------------
14+
15+
using System;
16+
17+
namespace Tutorial41
18+
{
19+
public class TransformExceptionFailure : IAmFailure
20+
{
21+
public string Reason { get; set; }
22+
23+
public Exception Exception { get; set; }
24+
25+
public TransformExceptionFailure(string message)
26+
{
27+
Reason = message;
28+
}
29+
30+
public override string ToString()
31+
{
32+
return Reason;
33+
}
34+
35+
public static IAmFailure Create(string msg) => new TransformExceptionFailure(msg);
36+
37+
}
38+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
namespace Tutorial41
3+
{
4+
public class UnexpectedFailure : IAmFailure
5+
{
6+
public UnexpectedFailure(string reason)
7+
{
8+
Reason = reason;
9+
}
10+
11+
public override string ToString()
12+
{
13+
return Reason;
14+
}
15+
16+
public string Reason { get; set; }
17+
public static IAmFailure Create(string reason) => new UnexpectedFailure(reason);
18+
}
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
using System;
3+
4+
namespace Tutorial41
5+
{
6+
public class UnexpectedFailureException : Exception, IAmFailure
7+
{
8+
public UnexpectedFailureException(IAmFailure failure): base(failure.Reason)
9+
{
10+
Reason = failure.Reason;
11+
}
12+
13+
public string Reason { get; set; }
14+
public static IAmFailure Create(IAmFailure failure) => new UnexpectedFailureException(failure);
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
using LanguageExt;
3+
4+
namespace Tutorial41
5+
{
6+
internal class UninitializedFailure : IAmFailure
7+
{
8+
public UninitializedFailure(string what)
9+
{
10+
Reason = what;
11+
}
12+
public string Reason { get; set; }
13+
public static IAmFailure Create(string what) => new UninitializedFailure(what);
14+
public static Either<IAmFailure, T> Create<T>(string what) => new UninitializedFailure(what);
15+
}
16+
}

Tutorial41_Statics/Program.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using LanguageExt;
2+
using System;
3+
4+
namespace Tutorial41
5+
{
6+
// This tutorial shows you how to protect binding transformations from exceptions,
7+
// and also override how the problem is surfaced to the user.
8+
class Program
9+
{
10+
static void Main(string[] args)
11+
{
12+
// Create some products based on an assembly line (pipeline)
13+
var product1 = GetAnalysisResult()
14+
.Bind(analysisResult => GetDesignResult())
15+
.Bind(designResult => GetImplementationResult());
16+
17+
// Design fails internally by throwing an exception:
18+
var product2 = GetAnalysisResult()
19+
.EnsuringBind(analysisResult => GetDesignResult(throwException: true)) // We can Ensure the transformation function against exceptions
20+
.Bind(designResult => GetImplementationResult()); // We assume there aren't any exceptions here
21+
22+
// Implementation fails internally by throwing an exception:
23+
var product3 = GetAnalysisResult()
24+
.Bind(analysisResult => GetDesignResult())
25+
.EnsuringBind(designResult => GetImplementationResult(throwException: true));
26+
27+
28+
// However sometimes the internal error caused somewhere in the guts of the function isn't as useful,
29+
// and re-interpreting it to add more context at a higher level is more useful...
30+
31+
// Same process but you can override how internal error is surfaced/presented using MapLeft:
32+
33+
var product4 = GetAnalysisResult()
34+
.EnsuringBind(analysisResult => GetDesignResult(throwException: true))
35+
// Whatever the left value is, override/customize/surface it diffirently as a new Failure and add better context, ie product4
36+
.MapLeft( failure => UnexpectedFailure.Create($"Design Error Occured while creating product 4. Details were '{failure.Reason}'"))
37+
.Bind(designResult => GetImplementationResult());
38+
var product5 = GetAnalysisResult()
39+
.Bind(analysisResult => GetDesignResult())
40+
.EnsuringBind(designResult => GetImplementationResult(throwException: true))
41+
// Whatever the left value is, override/customize/surface it diffirently as a new Failure and add better context, ie product5
42+
.MapLeft( failure => UnexpectedFailure.Create($"Implementation Error Occured while creating product 5. Details were '{failure.Reason}'"));
43+
44+
Console.WriteLine($"Product1 result: {product1}");
45+
Console.WriteLine($"Product2 result: {product2}");
46+
Console.WriteLine($"Product3 result: {product3}");
47+
Console.WriteLine($"Product4 result: {product4}");
48+
Console.WriteLine($"Product5 result: {product5}");
49+
50+
51+
}
52+
53+
/// <summary>
54+
/// Do some Analysis Work
55+
/// </summary>
56+
/// <returns></returns>
57+
public static Either<IAmFailure, string> GetAnalysisResult()
58+
{
59+
// Do some analysis work...
60+
return "Analysis Success";
61+
}
62+
63+
/// <summary>
64+
/// Do some design work
65+
/// </summary>
66+
/// <param name="throwException"></param>
67+
/// <returns></returns>
68+
public static Either<IAmFailure, string> GetDesignResult(bool throwException = false)
69+
{
70+
// Do some design work...
71+
return throwException
72+
? throw new UnexpectedFailureException(UnexpectedFailure.Create("Design Width is too large. Error DES623 Ref=KJE871"))
73+
: "Design Success";
74+
}
75+
76+
/// <summary>
77+
/// Do some implementation
78+
/// </summary>
79+
/// <param name="throwException"></param>
80+
/// <returns></returns>
81+
public static Either<IAmFailure, string> GetImplementationResult(bool throwException = false)
82+
{
83+
// Do some implemantion work...
84+
return throwException
85+
? throw new UnexpectedFailureException(UnexpectedFailure.Create("Implementation Volume exceeds mainframe size. Error IMP371 Ref=K763P"))
86+
: "Implementation Success";
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)