Futurum.Core
1.0.16
dotnet add package Futurum.Core --version 1.0.16
NuGet\Install-Package Futurum.Core -Version 1.0.16
<PackageReference Include="Futurum.Core" Version="1.0.16" />
paket add Futurum.Core --version 1.0.16
#r "nuget: Futurum.Core, 1.0.16"
// Install Futurum.Core as a Cake Addin #addin nuget:?package=Futurum.Core&version=1.0.16 // Install Futurum.Core as a Cake Tool #tool nuget:?package=Futurum.Core&version=1.0.16
Futurum.Core
A dotnet library providing Option and Result data types, based on the concepts behind 'Railway Oriented Programming'
- Result data type encapsulating computational result that represents the result that is either a success or a failure
- Option data type that represents a value that is either present or not present
- Based on the concepts behind Railway Oriented Programming
- Extension methods that allows interacting with and going into and out of the Result and Option data types
- Fluent discoverable and commented API
- Tested solution > 99% test coverage
- Tested with Stryker mutation testing > 95%
- Integration with Polly via Futurum.Core.Polly
Result
The Result data type is a type that represents the result of a computation that can either be a success or a failure. It is a type that is either a success or a failure.
It is a struct, preventing you from returning null instead.
This come in two forms:
Without a payload - Result
var result = Result.Ok();
var result = Result.Fail("Error message");
With a payload - Result<T>
var result = Result.Ok(2);
var result = Result.Fail<int>("Error message");
Any method that can fail should return a Result or Result<T>.
There are extension methods that allow you to easily convert dotnet or third-party dotnet libraries to return Result.
NOTE: There are async version of all the extension methods.
Extension Methods
There are extension methods that allow you to easily convert dotnet or third-party dotnet libraries to return Result.
NOTE: There are async version of all the extension methods.
Combine
Allows you to combine multiple Result's into one, if they have the same payload types.
If there is no payload on the Results passed in:
- If all of the Results passed are success, then return a Result success
- If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok();
var result2 = Result.Ok();
var result3 = Result.Ok();
Result result = Result.Combine(result1, result2, result3);
If there are payloads on the Results passed in and they are all the same type:
- If all of the Results passed are success, then return a Result success with an IEnumerable of all the Result payloads
- If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok(Guid.NewGuid());
var result2 = Result.Ok(Guid.NewGuid());
var result3 = Result.Ok(Guid.NewGuid());
Result<IEnumerable<Guid>> result = Result.Combine(result1, result2, result3);
If there are IEnumerable payloads on the Results passed in and they are all the same type:
- If all of the Results passed are success, then return a Result success with an IEnumerable of all the Result payloads concatenated together
- If any of the Results passed are failed, then return a Result failure with a ResultError of all of the failure ResultError's
var result1 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());
var result2 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());
var result3 = Result.Ok(new[] {Guid.NewGuid()}.AsEnumerable());
Result<IEnumerable<Guid>> result = Result.Combine(result1, result2, result3);
CombineAll
Allows you to combine Result's that have different payload types.
You additionally pass in a selector func.
var result1 = Result.Ok(Guid.NewGuid());
var result2 = Result.Ok(Guid.NewGuid().ToString());
Result<(Guid, string)> result = Result.CombineAll(result1, result2);
Compensate
Allows you to provide a compensating action (that also returns a Result), if the Result is:
- a failure
var result = Result.Fail<int>("Error message").Compensate(() => Result.Ok(2));
CompensateWhen
Allows you to provide a compensating action (that also returns a Result), if the Result is:
- a failure of a specific type
- matches a predicate optional
var result = Result.Fail<int>("Error message").CompensateWhen<InvalidOperationException>(() => Result.Ok(2));
CompensateWhenAny
Allows you to provide a compensating action (that also returns a Result), if the Result is:
- a failure of a specific type
- matches a predicate optional
NOTE: this will flatten a ResultErrorComposite.
var result = Result.Fail<int>("Error message").CompensateWhenAny<InvalidOperationException>(() => Result.Ok(2));
Do - Perform side-effect only on success
Executes a side-effect if the Result is success.
var result = Result.Ok(2)
.Do(x =>
{
// do something
});
DoWhenFailure - Perform side-effect only on failure
Executes a side-effect if the Result is failure.
var result = Result.Fail<int>("Error message")
.DoWhenFailure(x =>
{
// do something
});
DoSwitch - Perform side-effect
Allows you to perform a side-effect on an Result<T>.
You have to say what to do when the value is success and failure.
var result = Result.Fail<int>("Error message")
.DoSwitch(x =>
{
// do something on success
},
error =>
{
// do something on failure
});
EnhanceWithError
Allows you to provide additional context to a failure.
NOTE: This will create a ResultErrorComposite, with the parent IResultError being the error passed in and the existing IResultError its children.
var result = Result.Fail<int>("Error message").EnhanceWithError("Additional context");
Factory
Result.Ok and Result.Ok<T>
- Creates a success Result
- Creates a success Result<T> with the payload provided
var result = Result.Ok();
var result = Result.Ok(2);
Result.Fail
- Creates a failure Result with the error provided
- Creates a failure Result<T> with the error provided
var result = Result.Fail("Error message");
var result = Result.Fail<int>("Error message");
ToResultOk - Result → Result<T>
Creates a success Result<T> with the payload provided
var result = 2.ToResultOk();
IgnoreFailure
Ignores the failure on Result and always returns Result.Ok().
var resultInput = Result.Fail(ErrorMessage);
var resultOutput = resultInput.IgnoreFailure();
Map - Result<T> → Result<TR>
Transforms an Result<T> to an Result<TR>.
The transformation will only occur if there is the Result is success.
var result = Result.Ok(1).Map(x => x.ToString());
NOTE It is assumed that the transformation func that you pass in cannot fail, i.e. throw an exception.
NOTE If it can throw an exception, use Then, returning a Result<TR> from the transformation func.
MapSwitch
Transforms an Result<T> to a Result<TR>, allows you to pass in seperate transformations for success and failure.
var result = Result.Ok(1).MapSwitch(x => x + 1, () => 0);
Switch
Allows you to transform an Result<T> to a TR.
You have to say what to return when the value is success and failure.
int result = Result.Ok(1).Switch(x => x + 1, () => 0);
Then - Result<T> → Result<TR>
Allows you to bind two Result or Result<T> methods together.
Result resultInput = Result.Ok();
Result resultOutput = resultInput.Then(Result.Ok);
Result<int> resultInput = Result.Ok(1);
Result<string> resultOutput = resultInput.Then(value => Result.Ok(value.ToString()));
ThenSwitch
Transforms an Result<T> to a Result<TR>
Allows you to specify a predicate to change the way the Result is transformed.
ThenTry
Allows you to execute a func wrapping it in a try / catch
- If an exception is thrown, it will return a Result.Fail
- If no exception is thrown it will return a Result.Ok
The result of this is then passed to the then func
var result = await Result.ThenTryAsync(func, () => ERROR_MESSAGE);
A nice pattern to follow using local functions is as follows:
public Task<Result<string>> ExecuteAsync()
{
return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
Task<string> Execute()
{
// Do something
}
}
public Task<Result<string>> ExecuteAsync()
{
return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
Task<Result<string>> Execute()
{
// Do something
}
}
public Task<Result> ExecuteAsync()
{
return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
Task Execute()
{
// Do something
}
}
public Task<Result> ExecuteAsync()
{
return Result.Ok().ThenTryAsync(Execute, () => ERROR_MESSAGE);
Task<Result> Execute()
{
// Do something
}
}
NOTE
- This is really Result.Try and Result.Then combined.
- This can be a good way to integrate third-party libraries.
- There can be no return value
- The return value can be a T or a Result<T>
ToNonGeneric - Result<T> → Result
Discards the payload.
var result = Result.Ok(2).ToNonGeneric();
Try
Will run the func wrapping it in a try / catch.
- If an exception is thrown, it will return a Result.Fail, setting the ResultError to the exception
- If no exception is thrown it will return a Result.Ok, with the return value if there is one
var result = await Result.TryAsync(func, () => ERROR_MESSAGE);
A nice pattern to follow using local functions is as follows:
With a return value
public Task<Result<string>> ExecuteAsync()
{
return Result.TryAsync(Execute, () => ERROR_MESSAGE);
Task<string> Execute()
{
// Do something
}
}
With a Result<T> return value
public Task<Result<string>> ExecuteAsync()
{
return Result.TryAsync(Execute, () => ERROR_MESSAGE);
Task<Result<string>> Execute()
{
// Do something
}
}
With no return value
public Task<Result> ExecuteAsync()
{
return Result.TryAsync(Execute, () => ERROR_MESSAGE);
Task Execute()
{
// Do something
}
}
With a Result return value
public Task<Result> ExecuteAsync()
{
return Result.TryAsync(Execute, () => ERROR_MESSAGE);
Task<Result> Execute()
{
// Do something
}
}
NOTE
- This can be a good way to integrate third-party libraries.
- There can be no return value
- The return value can be a T or a Result<T>
TryGetValue - Get a value from a Dictionary
The TryGetValue extension methods work with dictionary.
They get the value from a dictionary as an Result<T>.
var values = Enumerable.Range(0, 10)
.ToDictionary(x => x, x => x);
var result = values.TryGetValue(2, ERROR_MESSAGE);
Unwrap - Result<T> → T or Exception
Comes out of the Result monad.
- If the Result is Ok, then return the value if there is one
- If the Result is Fail, then throw an exception, passing through the ResultError data
Success - With no return value
Result.Ok().Unwrap();
Success - With a return value
var value = Result.Ok(1).Unwrap();
Failure - With no return value (Exception will be thrown)
Result.Fail(ErrorMessage).Unwrap();
Failure - With a return value (Exception will be thrown)
var value = Result.Fail<int>(ErrorMessage).Unwrap();
Enumerable
Choose
Return only those elements of a sequence of Result<T> that are success.
If none of the elements in the sequence are success, then a Result<T> failure is returned.
var result1 = Result.Ok(1);
var result2 = Result.Ok(2);
var result3 = Result.Ok(3);
IEnumerable<int> values = new[] {result1, result2, result3}.Choose()
Filter
Filters an IEnumerable>Result<T>> based on a predicate
var values = Enumerable.Range(0, 10);
var resultInput = Result.Ok(values);
Result<IEnumerable<int>> resultOutput = resultInput.Filter(x => x % 2 == 0);
FlatMap
Transforms each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.
(IEnumerable<T>, Func<T, Result>) -> Result
var input = new[] {1, 2};
Result outputResult = input.FlatMap(x => Result.Ok());
(IEnumerable<T>, Func<T, Result<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2};
Result outputResult = input.FlatMap(x => Result.Ok());
(IEnumerable<T>, Func<T, IEnumerable<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2};
Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
if (x == 1) return Result.Fail<IEnumerable<int>>(ErrorMessage1);
return Result.Fail<IEnumerable<int>>(ErrorMessage2);
});
(Result<IEnumerable<T>>, Func<T, Result<IEnumerable<TR>>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2}.AsEnumerable().ToResultOk();
Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
if (x == 1) return Result.Fail<IEnumerable<int>>(ErrorMessage1);
return Result.Fail<IEnumerable<int>>(ErrorMessage2);
});
(Result<IEnumerable<T>>, Func<T, Result<TR>>) -> Result<IEnumerable<TR>>
var input = new[] {1, 2}.AsEnumerable().ToResultOk();
Result<IEnumerable<int>> outputResult = input.FlatMap(x =>
{
if (x == 1) return Result.Fail<int>(ErrorMessage1);
return Result.Fail<int>(ErrorMessage2);
});
Result<IEnumerable<IEnumerable<T>>> -> Result<IEnumerable<T>>
var input = new[] {1, 2};
Result<IEnumerable<int>> outputResult = input.FlatMap(x => new[] { x * 2 }.AsEnumerable().ToResultOk());
NOTE
- If ParallelOptions is not specified, then it will use default ParallelOptions.
- There are overloads that take a ParallelOptions, so you can control the level of concurrency.
FlatMapAs
Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>
public record Container(IEnumerable<int> Values);
var values = Enumerable.Range(0, 1)
.Select(x => new Container(new []{x}));
Result<IEnumerable<Container>> resultInput = Result.Ok(values);
Result<IEnumerable<int>> resultOutput = resultInput.FlatMapAs(x => x.Values, x -> x * 2);
FlatMapSequential
Transforms each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.
NOTE This runs sequentially, i.e. runs with a ParallelOptions MaxDegreeOfParallelism = 1
var input = new[] { 1, 2 };
Result outputResult = await input.FlatMapSequentialAsync(x =>
{
if (x == 1) return Result.FailAsync(ErrorMessage1);
return Result.FailAsync(ErrorMessage2);
});
FlatMapSequentialUntilFailure
Transforms each element of a sequence to an IEnumerable<T>, returning the first error it finds.
var input = new [] {1,2, 3};
Result outputResult = input.FlatMapSequentialUntilFailure(x => x == 2 ? Result.Fail(ErrorMessage1) : Result.Ok());
MapAs
Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>
var values = Enumerable.Range(0, 1);
var resultInput = Result.Ok(values);
Result<IEnumerable<int>> resultOutput = resultInput.MapAs(x => x + 1);
Pick
Return the first element of a sequence of Result<T> that are success.
If none of the elements in the sequence match are success, then a Result<T> failure is returned.
var result1 = Result.Ok(1);
var result2 = Result.Ok(2);
var result3 = Result.Ok(3);
Result<int> result = new[] {result1, result2, result3}.Pick(ErrorMessage);
PickFailureOrSuccess
Return the first element of a sequence of Result that are failure.
If none of the elements in the sequence match are failure, then a Result failure is returned.
var result1 = Result.Ok();
var result2 = Result.Ok();
var result3 = Result.Ok();
Result result = new[] {result1, result2, result3}.PickFailureOrSuccess();
ThenAs
Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>
var values = Enumerable.Range(0, 1);
var resultInput = Result.Ok(values);
Result<int> resultOutput = resultInput.ThenAs(x => (x + 1).ToResultOk());
TryFirst
Sequentially runs through a sequence of elements applying a function that returns a Result<TR>. The first one that is success is returned.
var input = new[] { 1, 2, 3 };
Result<string> result = input.TryFirst(x =>
{
if (x == 1)
{
return Result.Fail<string>(ErrorMessage);
}
return x.ToString().ToResultOk();
},
ErrorMessage);
TryToDictionary
Try to create a Dictionary<TKey, TElement;> from an IEnumerable<T> according to specified key selector and element selector functions.
NOTE: This will explicitly checks for duplicate keys and return a Result<Dictionary<TKey, TElement>> failure if there are any.
var numbers = Enumerable.Range(0, 10)
.ToList();
Result<Dictionary<int, int>> result = numbers.TryToDictionary(x => x, x => x);
AsyncEnumerable
Choose
Return only those elements of a sequence of Result<T> that are success.
If none of the elements in the sequence are success, then a Result<T> failure is returned.
IAsyncEnumerable<Result<int>> inputResults = AsyncEnumerable.Range(1, 3)
.Select(x => Core.Result.Result.Ok(x));
List<int> values = await inputResults.Choose().ToListAsync();
Filter
Filters an IEnumerable>Result<T>> based on a predicate
var values = AsyncEnumerable.Range(0, 1);
var resultInput = Result.Ok(values);
Result<IAsyncEnumerable<int>> returnedResult = resultInput.Filter(x => x % 2 == 0);
MapAs
Transforms an Result<IEnumerable<T>> to a Result<IEnumerable>TR>>
var values = AsyncEnumerable.Range(0, 1);
var resultInput = Result.Ok(values);
Result<IAsyncEnumerable<int>> returnedResult = resultInput.MapAs(x => x * 2);
Interop with Option
Option → Result
You can move between an Option and a Result using the 'ToResult' extension method. You just need to provide the error if the Option is not present.
var option = "hello".ToOption();
var result = option.ToResult(ErrorMessage);
Result → Option
This is not supported as it would result in the loss of the error context.
ResultError
Failures are represented as by the IResultError interface. These represent the context of the error.
This interface has two implementations:
- IResultErrorNonComposite
- IResultErrorComposite
IResultErrorNonComposite
This is the base interface for all IResultError implementations that are not composite, i.e. have no children.
IResultErrorComposite
This is the base interface for all IResultError implementations that are composite, i.e. have children.
What ResultError's come built-in
There are a number of built-in IResultError implementations.
You are free to create your own, by implementing either the IResultErrorNonComposite or IResultErrorComposite interfaces.
ResultErrorMessage
Represents a string error message, without any other context.
You can transform a string to a ResultErrorMessage using the extension method ToResultErrorMessage.
var resultError = "error message".ToResultError();
NOTE Most extension methods have an overload that allows you to pass in a error string.
ResultErrorException
Represents a exception error.
You can transform an Exception to a ResultErrorMessage using the extension method ToResultErrorMessage.
var resultError = new Exception("error message").ToResultError();
ResultErrorEmpty
Represents an empty error message, this is mainly used internally in the library.
var resultError = ResultErrorEmpty.Value;
ResultErrorComposite
This is a special IResultError, it allows you to have a tree structure of IResultError's.
var resultError = resultErrors.ToResultError();
var resultError = ResultErrorCompositeExtensions.ToResultError(parentResultError, resultErrors);
What to do with them
There are four supported ways to get the data from an IResultError:
- GetErrorString
- GetErrorStringSafe
- GetErrorStructure
- GetErrorStructureSafe
GetErrorString
This gets a string representing the error(s). Different implementation of IResultError deal with this differently.
ResultErrorComposite will call GetErrorString on all of their children, separating them with the specified separator.
GetErrorStringSafe
This gets a string representing the error(s). Different implementation of IResultError deal with this differently.
ResultErrorComposite will call GetErrorStringSafe on all of their children, separating them with the specified separator.
NOTE: The error message will be safe to return to the client, that is, it will not contain any sensitive information e.g. StackTrace.
GetErrorStructure
This gets a tree structure representing the error(s). This tree structure is serializable to Json.
ResultErrorComposite will call GetErrorStructure on all of their children, and add them as children to the ResultErrorComposite.
GetErrorStructureSafe
This gets a tree structure representing the error(s). This tree structure is serializable to Json.
ResultErrorComposite will call GetErrorStructureSafe on all of their children, and add them as children to the ResultErrorComposite.
NOTE: The error message will be safe to return to the client, that is, it will not contain any sensitive information e.g. StackTrace.
Option
The Option data type is a type that represents a value that is either present or not present. It is a type that is either HasValue
or HasNoValue
.
It is a struct, preventing you from returning null instead.
It can be used for value and reference types.
Extension Methods
Map - Option<T> → Option<TR>
Allows you to transform an Option<T> to an Option<TR>.
The transformation will only occur if there is a value present.
var option = Option.From(1).Map(x => x + 1);
Switch - Option<T> → TR
Allows you to transform an Option<T> to a TR.
You have to say what to return when the value is present and not present.
var option = Option.From(1).Switch(x => x + 1, () => 0);
DoSwitch - Perform side-effect
Allows you to perform a side-effect on an Option<T>.
You have to say what to do when the value is present and not present.
var option = Option.From(1).Switch(x =>
{
// do something when the value is present
},
() =>
{
// do something when the value is not present
});
TryGetValue - Get a value from a Dictionary
Gets the value from a dictionary as an Option<T>.
var values = Enumerable.Range(0, 10)
.ToDictionary(x => x, x => x);
var option = values.TryGetValue(2);
Factory - T → Option<T>
There are a number of factory extension methods.
From
Converts a T to an Option<T>
If the value is not null, then it will be a Option<T> with the value on it.
var option = Option.From(1);
var option = Option.From("hello");
If the value is null, then it will be a Option<T> with the value not set.
var option = Option.From((int?)null);
var option = Option.From((object)null));
None
Returns a Option<T> with the value not set.
var option = Option<int>.None;
var option = Option<string>.None;
ToOption
Converts a T to an Option<T>
If the value is not null, then it will be a Option<T> with the value on it.
var option = 1.ToOption();
var option = "hello".ToOption();
If the value is null, then it will be a Option<T> with the value not set.
var option = ((int?)null).ToOption();
var option = ((object)null).ToOption();
Implicit conversion
There is an implicit conversion in place. So if your method returns an T, then it will be automatically converted to an Option<T>.
OrElse and GetValueOrDefault
The OrElse extension methods are good when you have a precedence order.
The GetValueOrDefault extension methods are used to as a terminating method and will take you out if an Option, returning the default value provided if the Option has no value.
var value = Option.From(1)
.OrElse(Option.From(2))
.OrElse(Option.From(3))
.GetValueOrDefault(4);
TryParse
There are a lot of TryParse extension methods provided, for a lot of the built-in framework types:
Bool
var option = true.ToString().TryParseBool();
Int
var option = 1.ToString().TryParseInt();
Long
var option = 1.ToString().TryParseLong();
Decimal
var option = 1m.ToString().TryParseDecimal();
Double
var option = 1d.ToString().TryParseDouble();
Float
var option = 1f.ToString().TryParseFloat();
Guid
var option = Guid.NewGuid().ToString().TryParseGuid();
DateTime
var option = DateTime.Now.ToString().TryParseDateTime();
DateOnly
var option = DateOnly.FromDateTime(DateTime.Now).ToString().TryParseDateOnly();
TimeOnly
var option = TimeOnly.FromDateTime(DateTime.Now).ToString().TryParseTimeOnly();
Enum
var option = EnumValues.Value2.ToString().TryParseEnum<EnumValues>();
Enumerable<Option<T>>
Choose
Return only those elements of a sequence of Option<T> that have a value.
var options = Enumerable.Range(0, 10).Select(i => i.ToOption());
var returnValues = options.MapSwitch(value => trueValue, () => falseValue);
Map
Transforms each element of a sequence into a new form.
The transformation will only occur if there is a value present.
var options = Enumerable.Range(0, 5).Select(i => i.ToOption());
var values = options.Map(x => x + 1);
MapSwitch
Transforms each element of a sequence based, allows you to pass in seperate transformations for if the value is there or not.
var options = Enumerable.Range(0, 10).Select(i => i.ToOption());
var returnValues = options.MapSwitch(value => trueValue, () => falseValue);
Pick
Return the first element of a sequence based on if the value is there.
If none of the elements in the sequence match the predicate, then Option<T>.None is returned.
var option1 = Core.Option.Option.From(1);
var option2 = Core.Option.Option.From(2);
var option3 = Core.Option.Option.From(3);
var option = new[] {option1, option2, option3}.Pick();
TryElementAt
Returns the element at a specified index in a sequence.
- If the index is out of range, then an Option<T>.None is returned.
- If the index is in range, the element at the specified position in the source sequence is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryElementAt(2);
TryFirst
Returns the first element of a sequence.
- If the sequence is empty, then an Option<T>.None is returned.
- If the sequence is not empty, the first element is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryFirst();
TryLast
Returns the last element of a sequence.
- If the sequence is empty, then an Option<T>.None is returned.
- If the sequence is not empty, the last element is returned as a Option<T>.
var option = Enumerable.Range(0, 10).TryLast();
TrySingle
Returns the only element of a sequence.
- If the sequence is empty, then an Option<T>.None is returned, wrapped in a Result<T>.IsSuccess.
- If the sequence has 1 value, then the value is returned as a Option<T>, wrapped in a Result<T>.IsSuccess.
var option = Enumerable.Range(0, 10).TrySingle();
Interop with Nullable
For structs there is interop with Nullable
Nullable → Option
Use the ToOption extension method.
var option = ((int?)null).ToOption();
var option = ((int?)1).ToOption();
Option → Nullable
Use the ToNullable extension method.
int? value = null;
var option = value.ToOption();
var nullable = option.ToNullable();
int? value = 1;
var option = value.ToOption();
var nullable = option.ToNullable();
Interop with Result
Option → Result
You can move between an Option and a Result using the 'ToResult' extension method. You just need to provide the error if the Option is not present.
var option = "hello".ToOption();
var result = option.ToResult(ErrorMessage);
Result → Option
This is not supported as it would result in the loss of the error context.
Stryker mutation testing
Make sure you have Stryker installed
dotnet tool install -g dotnet-stryker
Make sure the Test project is built
dotnet build ./test/Futurum.Core.Tests
Run Stryker
dotnet stryker
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- System.Linq.Async (>= 6.0.1)
NuGet packages (7)
Showing the top 5 NuGet packages that depend on Futurum.Core:
Package | Downloads |
---|---|
Futurum.WebApiEndpoint
A dotnet library that allows you to build WebApiEndpoints using a vertical slice architecture approach. Provides a structured way to create REST apis in dotnet without controllers. |
|
Futurum.Test
Small dotnet testing library, allowing you to test code that uses Futurum.Core |
|
Futurum.FluentValidation
A dotnet library, that allows FluentValidation to work with Futurum.Core. |
|
Futurum.ApiEndpoint
A dotnet library, that is the base for ApiEndpoints in Futurum. |
|
Futurum.Core.Polly
Small dotnet library, allowing you to use [Polly](https://github.com/App-vNext/Polly) with Futurum.Core, based on the concepts behind 'Railway Oriented Programming'. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.16 | 526 | 4/10/2023 |
1.0.15 | 392 | 4/7/2023 |
1.0.14 | 354 | 3/26/2023 |
1.0.13 | 809 | 11/18/2022 |
1.0.12 | 431 | 11/3/2022 |
1.0.11 | 1,279 | 6/3/2022 |
1.0.10 | 566 | 4/4/2022 |
1.0.9 | 421 | 4/3/2022 |
1.0.8 | 577 | 3/21/2022 |
1.0.7 | 401 | 3/19/2022 |
1.0.6 | 1,701 | 2/24/2022 |
1.0.5 | 545 | 2/23/2022 |
1.0.4 | 421 | 2/20/2022 |
1.0.3 | 1,106 | 2/2/2022 |
1.0.2 | 428 | 1/28/2022 |
1.0.1 | 2,128 | 1/28/2022 |