Search code examples
.net-corerazorblazor-server-side

Display message in Blazor server


I'm starting to learn Web dev with Blazor. I'm on Blazor server. The idea is to display a message to the user if the name already exists in db.

Let's consider this simple method (in a service class and in another assembly)

public async Task<Category> Add(string categoryName)
    {
        var cat = new Category { Name = categoryName };

        if (_context.Category.Any(c => c.Name == categoryName))
        {
            //Display "Already exists"
        }
        else
        {
            await _context.AddAsync(cat);
            await _context.SaveChangesAsync();
            return cat;
        }
    }

In the Razor page, I'm calling the method like that:

protected async Task<Category> IncrementCount()
{
    return category= await _categoryService.Add("test");
}

How to display the message created in the method here for example

<div>@errorMessage</div>

I would like to keep the logic in the service class, it means that I would avoid something like that in the Razor page:

protected async Task<Category> IncrementCount()  
{
    category= await _categoryService.Add("test"); 
    errorMessage=category is null?"Already exists":string.Empty;
    return category;
}

Thanks


Solution

  • 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");
        }
    }
    

    enter image description here