Blazor - CRUD básico com SQLite


  Neste tutorial vamos criar uma aplicação Blazor Sserver e realizar o CRUD básico usando o SQLite.

Vamos criar uma aplicação simples que vai ler todos os registros da tabela Produtos dentro de um banco de dados SQLite ProdutosDB.db. Os registros serão exibidos como uma tabela HTML na interface com o usuário.

Na interface teremos um botão de exclusão individual é exibido em cada linha, e, clicando no mesmo exclui o registro da tabela Produtos. Ao lado da tabela, é exibido um formulário para adicionar um novo produto à tabela do banco de dados e, da mesma forma, também é exibido um formulário para atualizar o produto, através do qual o usuário pode atualizar um produto existente. Agora vamos implementar a operação CRUD no Blazor usando o SQLite como back-end.

A seguir temos uma visão da aplicação em execução :

recursos usados:

Criando o projeto

No VS 2022 Community vamos criar um projeto usando o template Blazor Server App com o nome BlazorSqlite.

Após criar o projeto vamos remover as referências e arquivos que não vamos usar e a seguir incluir os seguintes pacotes nugets no projeto:

1- Microsoft.EntityFrameworkCore.Sqlite
2- Microsoft.EntityFrameworkCore.Tools

Na pasta Data do projeto vamos criar a classe Produto:

public class Produto
{
 
public int Id { get; set; }
 
public string? Nome { get; set; }
 
public string? Descricao { get; set; }
 
public double Preco { get; set; }
 
public int Quantidade { get; set; }
}

Na mesma pasta vamos criar a classe de contexto ProdutoContext:

public class ProdutoContext : DbContext
{
 
public ProdutoContext(DbContextOptions<ProdutoContext> options): base(options)
  {}

 
public DbSet<Produto> Produto { get; set; }

 
protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
     modelBuilder.Entity<Produto>().HasData(GetProdutos());
    
base.OnModelCreating(modelBuilder);
  }

 
private List<Produto> GetProdutos()
  {
   
return new List<Produto>
    {
     
new Produto { Id = 1001, Nome = "Laptop", Preco = 20.02, Quantidade = 10,
 Descricao =
"O melhor laptop para jogos"},
     
new Produto { Id = 1002, Nome = "Microsoft Office", Preco = 20.99, Quantidade = 50,
 Descricao =
"Aplicação Office"},
     
new Produto { Id = 1003, Nome = "Lazer Mouse", Preco = 12.02, Quantidade = 20,
Descricao =
"Um mouse decente"},
     
new Produto { Id = 1004, Nome = "USB Storage", Preco = 5.00, Quantidade = 20,
Descricao =
"Armazena ate 256GB de dados"}
    };
  }
}

Nesta classe estamos usando o método HasData para incluir 3 registros na tabela quando da aplicação do Migrations.

Agora vamos criar a pasta Services no projeto e a seguir criar a interface IProdutoService e a classe concreta ProdutoService que implementa esta interface :

1- IProdutoService

public interface IProdutoService
{
   Task<List<Produto>> GetProdutosAsync();
   Task<Produto> AddProdutoAsync(Produto produto);
   Task<Produto> UpdateProdutoAsync(Produto produto);
   Task DeleteProdutoAsync(Produto produto);
}

2- ProdutoService

using BlazorSqlite.Data;
using
Microsoft.EntityFrameworkCore;

namespace BlazorSqlite.Services;

public class ProdutoService : IProdutoService
{
  
private readonly ProdutoContext dbContext;
  
public ProdutoService(ProdutoContext dbContext)
   {
    
this.dbContext = dbContext;
   }
  
public async Task<List<Produto>> GetProdutosAsync()
   {
    
return await dbContext.Produto.ToListAsync();
   }
  
public async Task<Produto> AddProdutoAsync(Produto produto)
   {
     
try
      {
       dbContext.Produto.Add(produto);
     
 await dbContext.SaveChangesAsync();
      }
    
 catch (Exception)
      {
        
throw;
      }
    
 return produto;
   }
  
public async Task<Produto> UpdateProdutoAsync(Produto produto)
   {
     
 try
       {
        
var productExist = dbContext.Produto.FirstOrDefault(p => p.Id == produto.Id);
        
if (productExist != null)
         {
            dbContext.Update(produto);
           
await dbContext.SaveChangesAsync();
          }
        }
       
catch (Exception)
        {
         
throw;
        }
       
return produto;
    }
   
public async Task DeleteProdutoAsync(Produto produto)
    {
     
 try
       {
         dbContext.Produto.Remove(produto);
        
await dbContext.SaveChangesAsync();
       }
     
 catch (Exception)
       {
        
throw;
       }
    }
}

No arquivo _Imports.razor inclua as referências:

...
@
using BlazorSqlite.Data
@
using BlazorSqlite.Services

Na classe Program vamos registrar o serviço e contexto da aplicação :

using BlazorSqlite.Data;
using
BlazorSqlite.Services;
using
Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

builder.Services.AddDbContext<ProdutoContext>(options =>
{
   options.UseSqlite(
"Data Source = ProdutosDB.db");
});

builder.Services.AddScoped<IProdutoService, ProdutoService>();
...
 

Criando o componente Razor

Vamos o componente Razor ProdutosPage na pasta Pages mas antes vamos ajustar o código do arquivo NavMenu.razor da pasta /Shared criando os seguintes links no menu : Home e Produtos  :

...
<
div class="nav-item px-3">
  <
NavLink class="nav-link" href="" Match="NavLinkMatch.All">
    <
span class="oi oi-home" aria-hidden="true"></span> Home
  
</NavLink>
</
div>
<
div class="nav-item px-3">
  <
NavLink class="nav-link" href="produtos">
    <
span class="oi oi-list" aria-hidden="true"></span> Produtos
 
</NavLink>
</
div>
...

Na página Index.razor vamos exibir uma imagem  produtos1.jpg que esta contida na pasta wwwroot/images:

@page "/"

<h3>Produtos</h3>
<
center>
  <
img src="images/produtos1.jpg" height="424" width="256"/>
</
center>
 

Vamos criar agora componente ProdutosPage.razor que vai exibir a relação de produtos existentes com opção para Deletar os dados de um produto selecionado :

@page "/produtos"
@inject IProdutoService service
<div class="container">
    <div class="row bg-light">
        <table class="table table-bordered">
            <thead class="thead-dark">
                <tr>
                    <th>ID</th>
                    <th>Nome</th>
                    <th>Descrição</th>
                    <th>Preço</th>
                    <th>Quantidade</th>
                    <th>Deletar</th>
                </tr>
            </thead>
            <tbody>
                @if (Produtos.Any())
                {
                    @foreach (var produto in Produtos)
                    {
                        <tr @onclick="(() => SetProdutoForUpdate(produto))">
                            <td>@produto.Id</td>
                            <td>@produto.Nome</td>
                            <td>@produto.Descricao</td>
                            <td>@produto.Preco</td>
                            <td>@produto.Quantidade</td>
                            <td><button class="btn btn-danger" @onclick="(() => 
                                              DeleteProduto(produto))">Deletar</button></td>
                        </tr>
                    }
                }
                else
                {
                    <tr><td colspan="6"><strong>Nenhum produto disponível</strong></td></tr>
                }
            </tbody>
        </table>
    </div>
    <div class="row m-5">
        <div class="col-5 bg-light m-2 justify-content-start">
            <div class="p-3 mb-3 bg-primary text-white text-center">Novo Produto</div>
            <EditForm Model="@NovoProduto">
                <div class="form-group">
                    <label for="nome">Nome</label>
                    <input type="text" id="nome" class="form-control" @bind-value="@NovoProduto.Nome" />
                </div>
                <div class="form-group">
                    <label for="preco">Preço</label>
                    <input type="text" id="preco" class="form-control" @bind="@NovoProduto.Preco" />
                </div>
                <div class="form-group">
                    <label for="quantidade">Quantidade</label>
                    <input type="text" id="quantidade" class="form-control" @bind="@NovoProduto.Quantidade" />
                </div>
                <div class="form-group">
                    <label for="descricao">Descrição</label>
                    <input type="text" id="descricao" class="form-control" @bind="@NovoProduto.Descricao" />
                </div>
                <div class="text-center p-3 mb-3">
                    <button class="btn btn-info" @onclick="AddNovoProduto"> Adicionar</button>
                </div>
            </EditForm>
        </div>
        <div class="col-5 bg-light m-2 justify-content-end">
            <div class="p-3 mb-3 bg-primary text-white text-center">Atualiza Produto</div>
            <EditForm Model="@UpdateProduto">
                <div class="form-group">
                    <label for="nome">Nome</label>
                    <input type="text" id="nome" class="form-control" @bind-value="@UpdateProduto.Nome" />
                </div>
                <div class="form-group">
                    <label for="preco">Preco</label>
                    <input type="text" id="preco" class="form-control" @bind="@UpdateProduto.Preco" />
                </div>
                <div class="form-group">
                    <label for="quantidade">Quantidade</label>
                    <input type="text" id="quantidade" class="form-control" @bind="@UpdateProduto.Quantidade" />
                </div>
                <div class="form-group">
                    <label for="descricao">Descricao</label>
                    <input type="text" id="descricao" class="form-control" @bind="@UpdateProduto.Descricao" />
                </div>
                <div class="text-center p-3 mb-3">
                    <button class="btn btn-info" @onclick="UpdateProdutoData">Atualizar</button>
                </div>
            </EditForm>
        </div>
    </div>
</div>
@code {
    List<Produto> Produtos = new List<Produto>();
    protected override async Task OnInitializedAsync()
    {
        await RefrescaProdutos();
    }
    private async Task RefrescaProdutos()
    {
        Produtos = await service.GetProdutosAsync();
    }

    public Produto NovoProduto { get; set; } = new Produto();
    private async Task AddNovoProduto()
    {
        await service.AddProdutoAsync(NovoProduto);
        NovoProduto = new Produto();
        await RefrescaProdutos();
    }
    Produto UpdateProduto = new Produto();

    private void SetProdutoForUpdate(Produto produto)
    {
        UpdateProduto = produto;
    }
    private async Task UpdateProdutoData()
    {
        await service.UpdateProdutoAsync(UpdateProduto);
        await RefrescaProdutos();
    }
    private async Task DeleteProduto(Produto produto)
    {
        await service.DeleteProdutoAsync(produto);
        await RefrescaProdutos();
    }
}

Aplicando a migração

Vamos agora aplicar o Migrations para gerar o banco de dados ProdutosDB.db a tabela Produtos e incluir os registros.

No VS 2022 abra a janela Package Manager Console e a seguir execute os seguintes comandos nesta ordem:

1- Add-Migration Inicial

2- Update-Database

O primeiro comando vai criar o arquivo de scripts contendo os comandos para criar o banco, a tabela e os registros e o segundo comando vai aplicar este script.

Pegue o código do projeto aqui: BlazorSqlite.zip

"Não vos esqueçais da hospitalidade, porque por ela alguns, não o sabendo, hospedaram anjos."
Hebreus 13:2

Referências:


José Carlos Macoratti