ThomasW.Domain.SharedKernel.Results
0.2.0
dotnet add package ThomasW.Domain.SharedKernel.Results --version 0.2.0
NuGet\Install-Package ThomasW.Domain.SharedKernel.Results -Version 0.2.0
<PackageReference Include="ThomasW.Domain.SharedKernel.Results" Version="0.2.0" />
paket add ThomasW.Domain.SharedKernel.Results --version 0.2.0
#r "nuget: ThomasW.Domain.SharedKernel.Results, 0.2.0"
// Install ThomasW.Domain.SharedKernel.Results as a Cake Addin #addin nuget:?package=ThomasW.Domain.SharedKernel.Results&version=0.2.0 // Install ThomasW.Domain.SharedKernel.Results as a Cake Tool #tool nuget:?package=ThomasW.Domain.SharedKernel.Results&version=0.2.0
ThomasW.Domain.SharedKernel.Results
Indicate the success of domain or application operations without throwing slow and expensive exceptions.
Installation
This package can be installed via NuGet.
Usage
Creation of results is achieved by accessing the static methods on the Result
class.
For operations that do not return a value, such as application commands, you
can create basic result objects using the Fail()
and Success()
methods:
public async Task<Result> CreateUser(Guid userId, User user)
{
if (userId == default)
{
return Result.Fail(new InvalidEntityIdFailure());
}
await this._repository.AddUser(userId, user);
return Result.Success();
}
In the above example, an argument is passed into the Fail()
method that
indicates the failure reason. This type must inherit from the
FailureReason
abstract class.
For operations that do return a value, such as application queries, you can
create typed result objects using the Fail<T>()
and Success<T>()
methods:
public async Task<Result<User>> GetUser(Guid userId)
{
User? user = await this._repository.GetUser(userId);
if (user == null)
{
return Result.Fail<User>(new EntityNotFoundFailure());
}
return Result.Success(user);
}
In the preceding example, a type is passed into the Fail<T>()
method to
indicate the type of value that would have been returned had the operation been
successful.
As the value is passed directly into the Success<T>()
method, its type can be
inferred and thus does not have to be explicitly specified.
For instances in which there may be multiple possible failure reasons,
specifying the value type in each call to Fail<T>()
will soon become
laborious. To make this easier, you can create pending typed results with the
Pending<T>()
method and specify the failure reason when necessary:
public async Task<Result<User>> GetUser(Guid userId)
{
PendingResult<User> result = Result.Pending<User>();
if (userId == default)
{
return result.Fail(new InvalidEntityIdFailure());
}
User? user = await this._repository.GetUser(userId);
if (user == null)
{
return result.Fail(new EntityNotFoundFailure());
}
return result.Success(user);
}
When consuming results, you can determine their success with the IsSuccessful
and IsFailed
properties, which will indicate whether the Value
and
FailureReason
properties are null
:
public async IActionResult Get(Guid userId)
{
Result<User> result = await this._userService.Get(userId);
if (result.IsSuccessful)
{
return this.Ok(result.Value);
}
if (result.FailureReason is EntityNotFoundFailure)
{
return this.NotFound();
}
return this.BadRequest();
}
Here, we check that the result is successful, which will tell the compiler that
the Value
property is not null
, and the FailureReason
property is
null
.
Conversely, if we checked that the operation failed, this will tell the
compiler that the FailureReason
property is not null
, and the Value
property is null
:
public async IActionResult Get(Guid userId)
{
Result<User> result = await this._userService.Get(userId);
if (result.IsFailed)
{
return result.FailureReason switch
{
EntityNotFoundFailure => this.NotFound(),
_ => this.BadRequest()
};
}
return this.Ok(result.Value);
}
ThomasW.Domain.SharedKernel.Results.FluentAssertions
To make testing result objects easier, a set of FluentAssertions extensions are available to install via NuGet.
The package currently provides two assertion methods, BeSuccessful
and
BeFailed
, for both typed and non-typed results.
Simple checks on the success of a result can be made by using the methods with no arguments:
public async Task GetUser_ValidId_ReturnsUser()
{
// Arrange Act
Result<User> result = await this._sut.GetUser(this._userId);
// Assert
result.Should().BeSuccessful();
}
public async Task GetUser_InvalidId_ReturnsUser()
{
// Arrange Act
Result<User> result = await this._sut.GetUser(Guid.NewGuid());
// Assert
result.Should().BeFailed();
}
If you want to assert on the value of a successful result, you can pass a
predicate into the method. The assertion will only pass if the result is
successful and the predicate evaluates to true
:
public async Task GetUser_ValidId_ReturnsUser()
{
// Arrange Act
Result<User> result = await this._sut.GetUser(this._userId);
// Assert
result.Should().BeSuccessful(user => user.Id == this._userId);
}
You can also assert on the failure reason by passing a type argument into the
IsFailed
method:
public async Task GetUser_InvalidId_ReturnsUser()
{
// Arrange Act
Result<User> result = await this._sut.GetUser(Guid.NewGuid());
// Assert
result.Should().BeFailed<EntityNotFoundFailure>();
}
This assertion will only pass if the result is failed and the failure reason matches the passed-in type.
Furthermore, you can pass a predicate into the IsFailed
method, as well as
the type parameter, to assert against the FailureReason
object itself:
public async Task GetUser_InvalidId_ReturnsUser()
{
// Arrange Act
Result<User> result = await this._sut.GetUser(Guid.NewGuid());
// Assert
result.Should().BeFailed<EntityNotFoundFailure>(
reason => reason.Message == "User not found");
}
The above assertion will only pass if the result is failed, the failure reason
matches the passed-in type and the predicate evaluates to true
.
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
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on ThomasW.Domain.SharedKernel.Results:
Package | Downloads |
---|---|
ThomasW.Domain.SharedKernel.Results.FluentAssertions
FluentAssertions extensions for ThomasW.Domain.SharedKernel.Results |
|
ThomasW.Application.Commands.Abstractions
A set of abstractions for building an application command pipeline. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.2.0 | 381 | 12/2/2021 |
0.2.0-beta.0.1 | 135 | 12/2/2021 |
0.2.0-alpha.0.2 | 127 | 12/2/2021 |
0.2.0-alpha.0.1 | 129 | 12/2/2021 |
0.1.1 | 274 | 12/1/2021 |
0.1.1-alpha.0.1 | 137 | 12/1/2021 |
0.1.0 | 555 | 12/1/2021 |
0.0.0-alpha.0.14 | 133 | 12/1/2021 |
0.0.0-alpha.0.13 | 134 | 12/1/2021 |