Creating Clean Commands
Problem
Sometimes a Command
property's code can get lengthy and messy, especially when there are a lot of conditions involved. Some commands even implement callbacks. It can sometimes be hard to understand what a Command
does just by looking at the code.
Solution
The ICommand.Create
factory method provides an ICommandBuilder
parameter that serves as a way to tidily create a command on a single line. It makes commands feel more instinctive by needing at most three methods to build them (Given, When and Then). You can find more information on these methods here. Let's take a look at how ICommandBuilder
is used in Chefs:
public IState<Credentials> UserCredentials => State<Credentials>.Value(this, () => new Credentials());
public ICommand Login => Command.Create(b => b.Given(UserCredentials).When(CanLogin).Then(DoLogin));
private bool CanLogin(Credentials userCredentials)
{
return userCredentials is not null &&
!string.IsNullOrWhiteSpace(userCredentials.Username) &&
!string.IsNullOrWhiteSpace(userCredentials.Password);
}
private async ValueTask DoLogin(Credentials userCredentials, CancellationToken ct)
{
await Authentication.LoginAsync(Dispatcher, new Dictionary<string, string> { { "Username", userCredentials.Username! }, { "Password", userCredentials.Password! } });
await NavigateToMain(ct);
}
When the Login
command is triggered, the Given method will get the current UserCredentials and pass it to the When method, which will check if the command can be executed. The Then method represents the callback to be invoked when the command is executed.
We can see in this example that firstly the Login command is created, then it picks up the credentials of the user UserCredentials to check if they CanLogin. If successful, then DoLogin the user.