In good design you should separate out Commands and Queries. Pass into the data pipeline request objects and get out result objects, not raw data objects. You need status information as well as a result. You should never return null
. What does it mean?
So a command looks like this:
public Task<CommandResult> AddItem(CommandRequest<Category> command)
{
//...
}
Where CommandRequest
looks like this:
public record struct CommandRequest<TRecord>(TRecord Item, CancellationToken Cancellation = new());
And a CommandResult
looks like this:
public sealed record CommandResult : IDataResult
{
public bool Successful { get; init; }
public string? Message { get; init; }
public int InsertedId { get; init; }
private CommandResult() { }
public static CommandResult Success(int insertedId, string? message = null)
=> new CommandResult { Successful = true, Message= message };
public static CommandResult Success(string? message = null)
=> new CommandResult { Successful = true, Message= message };
public static CommandResult Failure(string message)
=> new CommandResult { Message = message};
}
where:
public interface IDataResult
{
public bool Successful { get; }
public string? Message { get; }
}
If you want the inserted record get it by a separate query. If you want to access specific fields or create data object instances to submit into the pipeline do so in the Presentation layer.
You can then interact with the returned IDataResult
in the UI. Here's a demo using Bootstrap alerts.
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<div class="m-2 border border-2 border-dark rounded-3 m-2 p-3">
<div class="mb-5">
Data Edit form
</div>
<div class="m-2">
<button class="btn btn-sm btn-success" @onclick=this.SuccessCommand>Successful Submit</button>
<button class="btn btn-sm btn-primary" @onclick=this.SuccessCommandWithMessage>Successful Submit with A Message</button>
<button class="btn btn-sm btn-danger" @onclick=this.FailureCommand>Fail Submit</button>
</div>
</div>
<div>
@if (!_dataResult.Successful)
{
<div class="alert alert-danger">@_dataResult.Message</div>
}
@if (_dataResult.Successful && _dataResult.Message is not null)
{
<div class="alert alert-success">@_dataResult.Message</div>
}
</div>
@code {
private IDataResult _dataResult = CommandResult.Success();
private async Task SuccessCommand()
{
//fake a call into the data pipeline
await Task.Delay(100);
_dataResult = CommandResult.Success(56);
}
private async Task SuccessCommandWithMessage()
{
//fake a call into the data pipeline
await Task.Delay(100);
_dataResult = CommandResult.Success(56, $"Inserted Record with Id: {56}");
}
private async Task FailureCommand()
{
//fake a call into the data pipeline
await Task.Delay(100);
_dataResult = CommandResult.Failure("Something went wrong");
}
}
