Blazor - CRUD com MudBlazor


Neste artigo vamos iniciar a criação de uma aplicação Blazor Server para gerenciar informações de alunos em um banco de dados SQL Server usando a biblioteca Mudblazor.

O destaque deste artigo será a utilização do Mudblazor que é uma biblioteca de componentes para Blazor que facilita o desenvolvimento.

A maior vantagem desta biblioteca é que ela foi totalmente escrita em C#, com dependência quase zero de Javascript. A documentação da biblioteca pode ser verificada neste link:  Mudblazor

Criando o projeto Blazor Server no VS 2019

Abra o VS 2019 Community e selecione a opção Create a New Project;

A seguir selecione a opção Blazor Server App e clique em next;

Informe o nome do projeto BlazorAlunos igual ao da Solução e clique em Next;

Defina as configurações conforme mostrado na figura abaixo e clique em Create;

Teremos a solução e o projeto criados em uma arquitetura monolítica de projeto único.

Limpando o projeto

Vamos limpar o projeto criado deixando apenas os componentes Index.razor na pasta Pages e removendo todos os arquivos da pasta Data. Após isso teremos que  remover as referências nos arquivos do projeto aos arquivos excluídos.

Vamos ajustar o arquivo NavMenu.razor da pasta Shared deixando apenas dois links definidos: Home e Alunos

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>

        <li class="nav-item px-3">
            <NavLink class="nav-link" href="alunos">
                <span class="oi oi-document" aria-hidden="true"></span> Alunos
            </NavLink>
        </li>

    </ul>
</div>

Instalando as dependências : Entity Framework Core e Mudblazor

Vamos agora incluir alguns pacotes NuGet básicos que usaremos em nosso aplicativo CRUD. Isso inclui alguns dos pacotes do Entity Framework Core, que será nossa ferramenta de acesso a dados.

Abra o Console do gerenciador de pacotes e execute os seguintes comandos.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

A seguir vamos incluir o pacote do Mudblazor em nosso projeto com o comando:

Install-Package Mudblazor

Com o pacote instalado, abra o arquivo Startup.cs e inclua o código a seguir no método ConfigureServices(). Isso adiciona os serviços comuns exigidos pelos componentes do Mudblazor ao DI Container do aplicativo ASP.NET Core:

services.AddMudServices();

A seguir vamos configurar os arquivos estáticos do Mudblazor, os arquivos CSS e JS.

Abra o arquivo _Host.cshtml e inclua a referência aos arquivos CSS e JS  na tag <head>:

...
<link href="MudblazorDemo.CRUD.styles.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
..

A seguir abaixo da referência a blazor.server.js inclua a referência a MudBlazor.min.js :

...
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
...

A seguir abra o arquivo _Imports.razor e inclua a linha de código:

...
@using MudBlazor
...

Isso habilita a biblioteca Mudblazor em todo o projeto.

Agora abra o arquivo MainLayout.razor e inclua as duas linhas de código no final do arquivo:

...
...
 
<MudThemeProvider/>
<MudSnackbarProvider/>

Estamos incluindo um SnackBar que é uma notificação semelhante a um toast inspirada na IU do material (geralmente encontrada em dispositivos do Google). Iremos usar este recurso no projeto.

Criando o modelo de domínio e o contexto

Na pasta Data vamos definir o nosso modelo de domínio criando a classe Aluno :

public class Aluno
{
   public int Id { get; set; }
   public string Nome { get; set; }
   public string Email { get; set; }
   public int Idade { get; set; }
}

A seguir vamos criar a nossa classe de contexto que vai representar uma sessão com o banco de dados SQL Server. Crie a classe AppDbContext na pasta Data :

using Microsoft.EntityFrameworkCore;
namespace BlazorAlunos.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }
        public DbSet<Aluno> Alunos { get; set; }
    }
}

Abra o arquivo appsettings.json e inclua a string de conexão com o SQL Server:

{
 "ConnectionStrings": {
"MudblazorConn": "Data Source=*****************d Security=True"

},
...
 

Agora, vamos configurar o contexto e adicioná-lo ao contêiner de serviço no método ConfigureServices da classe Startup :

public void ConfigureServices(IServiceCollection services)
{
     services.AddDbContext<AppDbContext>(options => 
         options.UseSqlServer(Configuration.GetConnectionString("MudblazorConn")));
    services.AddMudServices();
    services.AddRazorPages();
    services.AddServerSideBlazor();
}

Agora podemos aplicar o Migrations para gerar o banco de dados CadastroDB e a tabela Alunos conforme configuramos. Para isso emita os comandos abaixo na janela do Package Manager Console:

add-migration initial
update-database

Com isso podemos iniciar a implementação das operações CRUD e criar os componentes usando os recursos do Mudblazor.

Criando a camada de serviço

Vamos preparar uma camada de serviço que pode se comunicar com a classe de contexto do banco de dados e buscar e gravar dados no banco de dados.

Vamos criar uma pasta Services no projeto e nesta pasta vamos criar uma interface IAlunoService :

using BlazorAlunos.Data;
using System.Collections.Generic;
namespace BlazorAlunos.Services
{
    public interface IAlunoService
    {
        Task<IEnumerable<Aluno>> GetAlunos();
        Task<Aluno> GetAlunoById(int id);
        Task SaveAluno(Aluno aluno);
        Task DeleteAluno(int id);
    }
}

Definimos aqui um contrato para implementar as operações CRUD : obter , incluir, alterar e excluir alunos.

Agora vamos criar a classe AlunoService que implementa esta interface e os serviços definidos:

using BlazorAlunos.Data;
using System.Collections.Generic;
using System.Linq;
namespace BlazorAlunos.Services
{
    public class AlunoService : IAlunoService
    {
        private readonly AppDbContext _context;
        public AlunoService(AppDbContext context)
        {
            _context = context;
        }
       public async Task DeleteAluno(int id)
       {
            var customer = await _context.Alunos.FindAsync(id);
            if (customer != null)
            {
                _context.Alunos.Remove(customer);
                await _context.SaveChangesAsync();
            }
        }
        public async Task<Aluno> GetAlunoById(int id)
        {
            var aluno = await _context.Alunos.FindAsync(id);
            return aluno;
        }
        public async Task<IEnumerable<Aluno>> GetAlunos()
        {
            return await _context.Alunos.ToListAsync();
        }
        public async Task SaveAluno(Aluno aluno)
        {
            if (aluno.Id == 0)
            {
                await _context.Alunos.AddAsync(aluno);
            }
            else
            {
                _context.Alunos.Update(aluno);
                await _context.SaveChangesAsync();
            }
        }
    }
}

A implementação do serviço foi feita usando async/await de forma que os métodos são assíncronos.

Aqui injetamos a instância do contexto e realizamos as operações CRUD. Assim não estamos usando um repositório e acessando os dados via contexto causando um forte acoplamento com a ferramenta ORM. Isso foi feito assim para simplificar o código pois o objetivo é mostrar o Mudblazor.

Temos que lembrar de registrar este serviço na classe Startup:

public void ConfigureServices(IServiceCollection services)
{
     services.AddDbContext<AppDbContext>(options => 
         options.UseSqlServer(Configuration.GetConnectionString("MudblazorConn")));
    services.AddScoped<IAlunoService, AlunoService>();
    services.AddMudServices();

    services.AddRazorPages();
    services.AddServerSideBlazor();
}

Temos também que atualizar o arquivo _Imports.razor e incluindo as referências às pastas Data e Services:

...
@using MudBlazor
@using BlazorAlunos.Data
@using BlazorAlunos.Services

Criando os componentes da UI com o Mudblazor

Já temos a configuração do acesso aos dados pronta e agora vamos nos concentrar na implementação da IU e do MudBlazor.

O componente Index.razor será a página de apresentação e aqui vamos exibir uma imagem que deve estar na pasta images dentro da pasta wwwroot do projeto:

@page "/"
<h3>Alunos</h3>
<br />
<img src="/images/alunos1.jpg" width="600" height="400" />

 

O resultado ao executar o projeto é o seguinte:

Para exibir os dados dos alunos vamos criar na pasta Pages o componente Alunos.razor.

O layout usado para exibir os dados dos alunos vai usar os seguintes componentes do Mudblazor:

A seguir vou apresentar o código referente a UI do componente usando os componentes Mudblazor:

. @page "/alunos"

@inject IAlunoService alunoService
@inject MudBlazor.ISnackbar snackBar

1.<MudCard Elevation="25">
2.    <MudCardHeader>
3.        <CardHeaderContent>
4.            <MudText Typo="Typo.h6">Incluir - Editar Alunos</MudText>
5.        </CardHeaderContent>
6.    </MudCardHeader>
7.    <MudCardContent>
8.        <MudTextField @bind-Value="aluno.Nome" Label="Nome" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
9.        <MudTextField @bind-Value="aluno.Email" Label="Email" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
10.       <MudNumericField @bind-Value="aluno.Idade" Label="Idade" Variant="Variant.Text" Margin="Margin.Normal"></MudNumericField>
11.       <br />
12.       <MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="Save">Salvar Aluno</MudButton>
13.    </MudCardContent>
14. </MudCard>

15. <br />
16. <MudTable Elevation="25" Items="alunos" Filter="new Func<Aluno, bool>(Search)" @bind-aluno="aluno">
17.     <ToolBarContent>
18.        <MudText Typo="Typo.h6">Alunos</MudText>
19.        <MudToolBarSpacer />
20.        <MudTextField @bind-Value="searchString" Placeholder="Procurar alunos..." Adornment="Adornment.Start"
21.               AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
22.    </ToolBarContent>
23.   
<HeaderContent>
24.        <MudTh>Nome</MudTh>
25.        <MudTh>Email</MudTh>
26.        <MudTh>Idade</MudTh>
27.        <MudTh>Operação</MudTh>
28.    </HeaderContent>

29.    <RowTemplate>
30.        <MudTd DataLabel="Id">@context.Id</MudTd>
31.        <MudTd DataLabel="Nome">@context.Nome</MudTd>
32.        <MudTd DataLabel="Email">@context.Email</MudTd>
33.        <MudTd DataLabel="Idade">@context.Idade</MudTd>
34.        <MudTd DataLabel="">
35.            <MudFab @onclick="@(()=>Edit(@context.Id))" Color="Color.Primary" Icon="@Icons.Material.Filled.Edit" Size="Size.Small" IconSize="Size.Small" />
36.            <MudFab @onclick="@(()=>Delete(@context.Id))" Color="Color.Secondary" Icon="@Icons.Material.Filled.Delete" Size="Size.Small" IconSize="Size.Small" />
37.        </MudTd>
38.    </RowTemplate>
39. </MudTable>

Explicando de forma resumida o código temos que:

Linha 1-14 : Definimos um componente <MudCard>;
Linha 8-10 : Estes são os componentes de campo de texto do mudblazor que são vinculados às propriedades  nome, email e idade do aluno;
Linha 12: O botão salvar que invoca a função Save() no evento OnClick;
Linha 16-39:  Usamos o componente MudTable definindo a fonte dos dados (Items="alunos") e o recurso para Filtrar os dados;
Linha 20:  Criamos a barra de pesquisa que altera o valor da variável searchString, que por sua vez aciona o método de pesquisa;
Linha 23-28: Definimos os cabeçalhos usando <HeaderContent>;
Linha 29-34:  Vinculamos as propriedades de context para exibir os dados na tabela;
Linha 35-36:  Definimos os botões para Editar e Deletar invocando os respectivos métodos : Edit() e Delete();

A seguir temos o código C# usando no bloco @code do arquivo:

@code{
    private bool hover = false;
    private string searchString = "";

    private Aluno aluno = new Aluno();
    private IEnumerable<Aluno> alunos = new List<Aluno>();

    protected override async Task OnInitializedAsync()
    {
        await GetAlunos();
    }

    private async Task<IEnumerable<Aluno>> GetAlunos()
    {
        alunos = await alunoService.GetAlunos();
        return alunos;
    }

    private bool Search(Aluno aluno)
    {
        if (string.IsNullOrWhiteSpace(searchString)) return true;
        if (aluno.Nome.Contains(searchString, StringComparison.OrdinalIgnoreCase)
            || aluno.Email.Contains(searchString, StringComparison.OrdinalIgnoreCase))
        {
            return true;
        }
        return false;
    }

    private async Task Save()
    {
        await alunoService.SaveAluno(aluno);
        aluno = new Aluno();
        snackBar.Add("Aluno Salvo.", Severity.Success);
        await GetAlunos();
    }

    private void Edit(int id)
    {
        aluno = alunos.FirstOrDefault(c => c.Id == id);
    }

    private async Task Delete(int id)
    {
        await alunoService.DeleteAluno(id);
        snackBar.Add("Aluno Exccluído.", Severity.Success);
        await GetAlunos();
    }
}

Neste código temos que :

- Declaramos um novo objeto de aluno e uma lista de alunos;

- Assim que a página é carregada, o método OnInitializedAsync é chamado por padrão. Substituímos esse método e carregamos a lista de alunos de nossa instância de serviço.

- O método Search é um método específico do Mudblazor que é chamado quando o usuário digita algo na barra de pesquisa e pressiona Enter. Basicamente, ele verifica se alguma das palavras-chave pesquisadas está contida no nome ou email de qualquer aluno.

-  O método Save() é usado quando o usuário insere dados no formulário e clica no botão Salvar, esse método deve ser acionado. Aqui também você pode notar que estamos usando a instância de serviço injetada.

- O método Edit() recupera o registro do aluno quando o usuário clicar no botão de edição correspondente a um aluno na grade de dados.

- O método Delete() é acionado quando o botão de exclusão for clicado, e vai usar a instância de serviço para excluir o aluno selecionado. Depois de excluído, temos que recarregar o aluno disponível novamente.

Abaixo temos a apresentação dos dados com os botões para realizar as operações CRUD:

Pegue o código do projeto aqui:  BlazorAlunos.zip (sem as referências)

"E eu, quando o vi, caí a seus pés como morto; e ele pôs sobre mim a sua destra, dizendo-me: Não temas; Eu sou o primeiro e o último; E o que vivo e fui morto, mas eis aqui estou vivo para todo o sempre. Amém. E tenho as chaves da morte e do inferno."
Apocalipse 1:17,18

Referências:


José Carlos Macoratti