Blazor - Pagina de busca simples (PostgreSQL)


  Hoje vamos recordar alguns conceitos básicos do Blazor e criar um projeto para acessar dados em um banco de dados PostgreSQL e criar uma página de busca.

Hoje vamos recordar alguns conceitos do Blazor começando pelo data binding.

Continuando a primeira parte do artigo vamos agora definir os demais projetos.

O Blazor possui duas formas principais de vincular dados no lado do cliente.

  1. One-Way data binding - Ligação de dados unidirecional
  2. Two-Way data binding - Ligação de dados bidirecional

No Blazor, a ligação de dados bidirecional é obtida com o atributo bind.

Você especifica a variável que deseja vincular dentro do atributo bind : <input @bind="@Variavel"/>

Nesse caso, @bind funciona com um manipulador onchange depois que a entrada perde o foco (como quando um usuário sai da tabulação).

Se quisermos que as atualizações ocorram enquanto o usuário digita e a capacidade de controlar qual evento aciona uma atualização podemos usar @bind-value.

Quando usamos @bind-value:event="event", podemos especificar um evento válido como oninput, keydown, keypress e assim por diante.

Nossa aplicação vai usar um banco de dados PostgreSQL para acessar os dados e vamos usar o EF Core na abordagem Code-First para criar o banco de dados e a tabela de exemplo.

Você pode fazer o download do PostgreSQL neste link: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads

Criando o projeto

Abra o VS 2022 e acione o menu Create New Project;

Na janela Add New Project selecione Blazor e a seguir o template Blazor Server App:

Informe o nome Blazor_SearchDemo e a seguir selecione as configurações conformea abaixo:

Após criar o projeto vamos limpar o contéudo da pasta Data e remover os componentes Counter , FetchData e SurveryPrompt do projeto.

Vamos criar neste projeto as pastas Context e Services e vamos incluir também os seguintes pacotes Nuget:

Você pode usar o comando install-package <nome_pacote> e as versões usadas são as mais recentes. Pode também usar o menu Tools selecione Nuget Package Manager -> Manage Nuget Packages for Solution.

Na pasta Data vamos criar a classe Cliente :

public class Cliente
{
    public int ClienteId { get; set; }
    [MaxLength(100)]
    public string? Nome { get; set; }
    [MaxLength(150)]
    public string? Email { get; set; }
    public int Idade { get; set; }
}

Na pasta Context vamos criar o arquivo AppDbContext que herda de DbContext e que contém o mapeamento ORM e onde vamos usar os recursos do Bogus para incluir 100 registros fake na base de dados:

using Bogus;
using Microsoft.EntityFrameworkCore;
namespace Blazor_SearchDemo.Data;
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<Cliente> Clientes { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var ids = 1;
        var clienteFaker = new Faker<Cliente>("pt_BR")
            .RuleFor(c => c.ClienteId, f => ids++)
            .RuleFor(c => c.Nome, f => f.Name.FullName(Bogus.DataSets.Name.Gender.Female))
            .RuleFor(c => c.Email, f => f.Internet.Email(f.Person.FirstName).ToLower())
            .RuleFor(c => c.Idade, f => f.Random.Number(8, 90));
        // gera 100 Clientes
        modelBuilder
            .Entity<Cliente>()
            .HasData(clienteFaker.GenerateBetween(100, 100));
    }
}

Para saber como usar o Bogus veja o meu artigo :  C# - Gerando Dados Fake com Bogus

No arquivo appsettings.json inclua a string de conexão :

{
  "ConnectionStrings": {
    "DefaultConnection": "User ID=postgres;Password=***;Host=localhost;Port=5432;Database=ClientesDB;Pooling=true;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Estou usando o usuário padrão e sua senha na porta 5432 onde vamos criar o banco ClientesDB.

Neste momento podemos aplicar o Migrations e gerar o banco e a tabela ClientesDB usando os comandos:

1 - Add-Migration Inicial -> gera o script
2 - Update-database -> aplica o script

Ao final teremos o banco de dados criado no PostgreSQL e podemos usar o pgAdmin4 para verificar isso. Acessando o pgAdmin iremos ver o resultado abaixo:

Na pasta Services vamos criar a interface IClienteService e a classe ClienteService para implementar os métodos para localizar dados.

1- IClienteService

public interface IClienteService
{
    Task<List<Cliente>> GetClientesAsync();
    Task<Cliente> GetClienteAsync(int id);
    Task<List<Cliente>> GetClientePorNomeAsync(string nome);
}

2- ClienteService

using Blazor_SearchDemo.Data;
using Microsoft.EntityFrameworkCore;
namespace Blazor_SearchDemo.Services;
public class ClienteService : IClienteService
{
    private readonly AppDbContext _context;
    public ClienteService(AppDbContext context)
    {
        _context = context;
    }
    public async Task<Cliente> GetClienteAsync(int id)
    {
        return await _context.Clientes.FirstOrDefaultAsync(c => c.ClienteId == id);
    }
    public async Task<List<Cliente>> GetClientePorNomeAsync(string nome)
    {
        return await _context.Clientes.Where(c => c.Nome.Contains(nome)).ToListAsync();
    }
    public async Task<List<Cliente>> GetClientesAsync()
    {
        return await _context.Clientes.ToListAsync();
    }
}

Agora vamos registrar o serviço do contexto e o serviço do cliente na classe Program :

...

var connection = builder.Configuration.GetConnectionString("DefaultConnection");

builder.Services.AddDbContext<AppDbContext>(options =>
   options.UseNpgsql(connection));

builder.Services.AddTransient<IClienteService, ClienteService>();

var app = builder.Build();

...
 

Agora, neste primeiro exemplo, vamos usar o componente Index.razor substituindo o código gerado pelo código abaixo:

@page "/"

<PageTitle>Index</PageTitle>

<h2>Clientes</h2>

No arquivo _Imports.razor vamos incluir o seguinte código:

@using System.Net.Http
@
using Microsoft.AspNetCore.Authorization
@
using Microsoft.AspNetCore.Components.Authorization
@
using Microsoft.AspNetCore.Components.Forms
@
using Microsoft.AspNetCore.Components.Routing
@
using Microsoft.AspNetCore.Components.Web
@
using Microsoft.AspNetCore.Components.Web.Virtualization
@
using Microsoft.JSInterop
@
using Blazor_SearchDemo
@
using Blazor_SearchDemo.Shared
@
using Blazor_SearchDemo.Pages
@
using Blazor_SearchDemo.Data
@
using Blazor_SearchDemo.Services

A seguir vamos incluir na pasta Pages um novo componente Razor chamado Clientes.razor :

@page "/clientes"
@inject IClienteService _service;
<h3>Lista de Clientes  </h3>
<hr />
@if (_render)
{
      <td>
        <input type="text" placeholder="Nome do cliente" @bind-value="ocliente.Nome" />
        <button class="btn-primary" @onclick="@ProcuraCliente">
            Procurar Cliente
        </button>
    </td>
    <table class="table">   
        <tr>
            <th>Id</th>
            <th>Nome</th>
            <th>Email</th>
            <th>Idade</th>
        </tr>
        @foreach (var item in lista)
        {
            <tr>
                <td>@item.ClienteId</td>
                <td>@item.Nome</td>
                <td>@item.Email</td>
                <td>@item.Idade</td>
            </tr>
        }
    </table>
    <h4>total : @lista.Count</h4>
}
else
{
    <h4>Carregando...</h4>
}
@code {
    List<Cliente>? lista;
    private bool _render = false;
    Cliente ocliente = new Cliente();
    public string? nome { get; set; }
    async Task ProcuraCliente()
    {
        nome = ocliente.Nome;
        lista = await _service.GetClientePorNomeAsync(nome);
        _render = true;
        StateHasChanged();
    }
   protected override async Task OnInitializedAsync()
    {
        lista = await _service.GetClientesAsync();
        _render = true;
    }
}

Vamos entender o código:

Injetamos o serviço IClienteService usando a diretiva @inject

Definimos o input type onde usamos o atributo @bind-value vinculando a oCliente.Nome

Criamos um button e no evento onclick vamos chamar o método ProcuraCliente.

Observe que o código de marcação possui um @if avaliando o valor da variável boolean _render. Isso  é feito para evitar o código que usa a lista de clientes seja acessando visto que view pode ser renderizada antes do evento OnInitializedAsync().

No método ProcuraCliente estamos usando o serviço e acessando os dados e informando definindo o valor de _render como true para informar que a view pode ser renderizada com os dados.

Precisamos usar StateHasChanged para atualizar a interface com o usuário.

Executando o projeto teremos:

Pegue o projeto aqui:  Blazor_SearchDemo.zip ...

"O meu mandamento é este: Que vos ameis uns aos outros, assim como eu vos amei."
João 15:12

Referências:


José Carlos Macoratti