Josupeit.Practices.Commands
1.0.8
Prefix Reserved
dotnet add package Josupeit.Practices.Commands --version 1.0.8
NuGet\Install-Package Josupeit.Practices.Commands -Version 1.0.8
<PackageReference Include="Josupeit.Practices.Commands" Version="1.0.8" />
<PackageVersion Include="Josupeit.Practices.Commands" Version="1.0.8" />
<PackageReference Include="Josupeit.Practices.Commands" />
paket add Josupeit.Practices.Commands --version 1.0.8
#r "nuget: Josupeit.Practices.Commands, 1.0.8"
#:package Josupeit.Practices.Commands@1.0.8
#addin nuget:?package=Josupeit.Practices.Commands&version=1.0.8
#tool nuget:?package=Josupeit.Practices.Commands&version=1.0.8
Josupeit.Practices.Commands
Implementing both Execute and ExecuteAsync on every command is tedious and error-prone. If the two implementations diverge, callers that happen to use the synchronous path silently get different behavior. This package solves that by providing abstract base classes where you only implement one variant — sync or async — and the other is derived automatically.
The bridging works in both directions. If you override ExecuteAsync, then Execute will block on it. If you override Execute, then ExecuteAsync will wrap it. If you accidentally call the base implementation from an override without actually implementing the other direction, the base class detects the recursion and throws an InvalidOperationException with a clear message rather than causing a stack overflow.
dotnet add package Josupeit.Practices.Commands
How to implement a command
Override either Execute (sync) or ExecuteAsync(CancellationToken) (async), but not both unless your implementation genuinely differs between the two. Never call base.Execute or base.ExecuteAsync from an override.
Parameterless command (no return value)
public sealed class SendWelcomeEmailCommand : Command
{
private readonly IEmailService _email;
public SendWelcomeEmailCommand(IEmailService email) => _email = email;
// Override the async path. Execute() is bridged automatically.
public override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await _email.SendAsync("welcome@example.com", cancellationToken);
}
}
// Both call sites work without any extra effort.
new SendWelcomeEmailCommand(email).Execute();
await new SendWelcomeEmailCommand(email).ExecuteAsync(cancellationToken);
Command with input
public sealed class DeleteUserCommand : Command<Guid>
{
private readonly IUserRepository _repository;
public DeleteUserCommand(IUserRepository repository) => _repository = repository;
public override async Task ExecuteAsync(Guid userId, CancellationToken cancellationToken)
{
await _repository.DeleteAsync(userId, cancellationToken);
}
}
Command with output
public sealed class CreateOrderCommand : CommandWithOutput<Order>
{
private readonly IOrderRepository _repository;
public CreateOrderCommand(IOrderRepository repository) => _repository = repository;
public override async Task<Order> ExecuteAsync(CancellationToken cancellationToken)
{
return await _repository.CreateAsync(cancellationToken);
}
}
Query (pure, no side effects)
Queries are expected to be pure. Implement the synchronous path when the implementation does not involve I/O, and the base class will handle the async path.
public sealed class GetUserQuery : Query<Guid, User>
{
private readonly IUserRepository _repository;
public GetUserQuery(IUserRepository repository) => _repository = repository;
public override User Execute(Guid id) => _repository.Find(id);
}
Available base classes
| Base class | Input | Output |
|---|---|---|
Command |
— | — |
Command<TInput> |
TInput |
— |
CommandWithOutput<TOutput> |
— | TOutput |
CommandWithOutput<TInput, TOutput> |
TInput |
TOutput |
Query<TOutput> |
— | TOutput |
Query<TInput, TOutput> |
TInput |
TOutput |
The interfaces that these classes implement live in Josupeit.Practices.Commands.Abstractions, which is pulled in automatically as a transitive dependency.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Josupeit.Practices.Commands.Abstractions (>= 1.0.0 && < 2.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.8 | 120 | 3/10/2026 |