Skip to main content

Nesse artigo mostro um tutorial com exemplo de como utilizar o C# com WebAssembly por meio do Blazor. Mas, antes…

O que é Blazor?

É um framework para aplicações “client-side” escrito em .NET e rodando em cima do WebAssembly.
É baseado em tecnologias já existentes como HTLM e CSS, mas você pode usar C# e Razor ao invés de JavaScript para buildar os componentes. Oferece todos os benefícios de uma plataforma de aplicativo de página única (SPA) rica e moderna ao usar o .NET de ponta a ponta.
A ideia sobre o Blazor é ser capaz de combinar Razor e C # em um projeto da web do lado do cliente que roda completamente no navegador.

O que é WebAssembly?

WebAssembly é um formato binário para o código no navegador e é executado muito mais rápido do que o JavaScript tradicional. Oferece ao navegador várias vantagens, como:

  1. Executa em desempenho quase nativo;
  2. É executado em uma caixa de areia segura para a memória;
  3. Compila a partir de uma variedade de linguagens, ou seja, .NET, C, C++, Java, Rust etc.

A principal vantagem do WebAssembly é que ele lida muito bem com trabalhos com muita memória e multithreading, em comparação com o JavaScript.

Features

O Blazor contém todos os recursos de uma estrutura web moderna, como:

  • Um modelo de componente para a construção de IU combinável;
  • Roteamento;
  • Layouts;
  • Formulários e validação;
  • Injeção de dependência;
  • Recarregamento ao vivo no navegador durante o desenvolvimento;
  • Renderização do lado do servidor;
  • Depuração completa do .NET em navegadores e no IDE;
  • Rich IntelliSense e ferramentas;
  • Publicação e corte de tamanho de aplicativo.

Setup

  1. Baixe e instale o .NET Core 2.1 SDK (2.1.300 ou posterior).
  2. Baixe e instale o Visual Studio 2017 (15.7 ou posterior) com o ASP.NET e a carga de trabalho de desenvolvimento da web selecionados.

Fonte: RIP Tutorial

Um CRUD simples utilizando Blazor, EF Core e in-memory database

Neste exemplo vou lhes mostrar como fazer um simples CRUD utilizando as seguintes ferramentas:

Após a instalação do Blazor, vamos criar um projeto através do seguinte comando:

dotnet new blazorserver -o <VOCE_ESCOLHE_O_NOME> --no-https
cd NOME_QUE_VOCE_ESCOLHEU

Observação: você pode utilizar o Visual Studio e criar o projeto através do caminho “File > New Solution” e pesquisar por “Blazor”

Para saber se está tudo OK, basta digitar o comando:

dotnet run

Por padrão, já vem criada uma aplicação para você se ambientar com o Blazor. Para visualizar a aplicação, acesse http://localhost:5000.

Agora, vamos criar as nossas classes, páginas e instalar alguns pacotes necessários para fazermos o CRUD.

Setup

Primeiro, vamos instalar o pacote Microsoft.EntityFrameworkCore.InMemory. Para isso, digite o seguinte comando (dentro da pasta do seu projeto):

dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 3.1.8

Codificando

Agora, dentro da pasta “Data”, vamos criar as seguintes classes: “Context.cs” e “ClienteService.cs”.

A classe “ClienteService.cs” terá o seguinte código:

using Blazor.Client.Data;
using Blazor.Client.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blazor.Data
{
    public class ClienteService
    {
        readonly Context _context = new Context();

        /// <summary>
        /// Add cliente to the memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public async Task AddAsync(Cliente cliente)
        {            
            await Task.Run(() => _context.Clientes.Add(cliente));

            SaveChanges();

        }

        /// <summary>
        /// Add cliente to the memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public void Add(Cliente cliente)
        {
            _context.Clientes.Add(cliente);            
            SaveChanges();

        }

        /// <summary>
        /// Get all Clientes from memory database
        /// </summary>
        /// <returns>A list of all Clientes</returns>
        public async Task<IEnumerable<Cliente>> GetAllAsync()
        {
            List<Cliente> retorno;

            return await Task.Run(() => retorno =  new List<Cliente>(_context.Clientes));
            
        }

        /// <summary>
        /// Get all Clientes from memory database
        /// </summary>
        /// <returns>A list of all Clientes</returns>
        public IEnumerable<Cliente> GetAll()
        {
            List<Cliente> retorno = new List<Cliente>(_context.Clientes);

            return retorno;

        }

        /// <summary>
        /// Get the Cliente by its ID
        /// </summary>
        /// <param name="id">An integer ID</param>
        /// <returns></returns>
        public Cliente GetById(int id)
        {
            var query = _context.Clientes.Find(id);

            return query;
        }

        /// <summary>
        /// Get the Cliente by its ID
        /// </summary>
        /// <param name="id">An integer ID</param>
        /// <returns></returns>
        public async Task<Cliente> GetByIdAsync(int id)
        {
            Cliente cliente;

            return await Task.Run(() => cliente = _context.Clientes.Find(id));
        }

        /// <summary>
        /// Update the Cliente on memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public void Update(Cliente cliente)
        {
            _context.Clientes.Update(cliente);
            SaveChanges();
        }

        /// <summary>
        /// Update the Cliente on memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public async Task UpdateAsync(Cliente cliente)
        {
            await Task.Run(() => _context.Clientes.Update(cliente));

            SaveChanges();
        }

        /// <summary>
        /// Delete the Client on memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public void Delete(Cliente cliente)
        {
            _context.Clientes.Remove(cliente);
            SaveChanges();
        }

        /// <summary>
        /// Delete the Client on memory database
        /// </summary>
        /// <param name="cliente">The Cliente object</param>
        public async Task DeleteAsync(Cliente cliente)
        {
            await Task.Run(() => _context.Clientes.Remove(cliente));

            SaveChanges();
        }

        private int SaveChanges()
        {
            return _context.SaveChanges();
        }
    }
}

A classe “Context.cs” terá o seguinte código:

using Blazor.Client.Models;
using Microsoft.EntityFrameworkCore;

namespace Blazor.Client.Data
{
    public class Context : DbContext
    {        
        public DbSet<Cliente> Clientes { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseInMemoryDatabase("BlazorDB");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Cliente>()
                .HasKey(c => c.Id);

        }
    }
}

Agora, dentro da pasta “Models”, vamos criar a seguinte classe:

A classe “Model.cs” terá o seguinte código:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Blazor.Client.Models
{
    public class Cliente
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o nome!")]
        [StringLength(60, MinimumLength = 4,
            ErrorMessage = "O nome deve ter no minimo 4 e no maximo 60 caracteres!")]
        [DisplayName("Nome")]
        public string Nome { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o RG!")]
        [StringLength(9)]
        [DisplayName("RG")]
        public string Rg { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o CPF!")]
        [StringLength(11, ErrorMessage = "O CPF deve conter 11 digitos")]
        [DisplayName("CPF")]
        public string Cpf { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o Endereco!")]
        [StringLength(60)]
        [DisplayName("Endereco")]
        public string Endereco { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o Telefone!")]
        [StringLength(11)]
        [DisplayName("Telefone")]
        public string Telefone { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o Celular!")]
        [StringLength(12)]
        [DisplayName("Celular")]
        public string Celular { get; set; }

        [Required(ErrorMessage = "Por favor, preencha o email!")]
        [EmailAddress(ErrorMessage = "Email invalido!")]
        [StringLength(60)]
        [DisplayName("Email")]
        public string Email { get; set; }

    }
}

Agora, dentro da pasta “Pages”, vamos criar a pasta “Clientes” e dentro dela as seguintes páginas: “Cadastrar.razor” e “Editar.razor”.
Ainda na pasta “Pages”, vamos criar a página “Cliente.razor”:

A página “Cadastrar.razor” terá o seguinte código:

@page "/Clientes/cadastrar"
@using Blazor.Data
@using Models = Blazor.Client.Models

@inject ClienteService Service
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager

<h3>Cadastrar Clientes</h3>

<div class="container">
    <h1 class="text-center">Cadastro de Clientes</h1>
    <div class="row" style="padding-top: 10%">
        <div class="col-lg-12">
            <div class="form-group-lg">
                
                <EditForm class="text-center" Model="@_cliente">
                    <DataAnnotationsValidator />
                    <ValidationSummary />
                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Nome"> Nome:</label>
                            <InputText id="Nome" @bind-Value="@_cliente.Nome" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                        <div class="col-lg-6">
                            <label id="Rg">RG:</label>
                            <InputText id="Rg" @bind-Value="@_cliente.Rg" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Cpf">CPF:</label>
                            <InputText id="Cpf" @bind-Value="@_cliente.Cpf" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <label id="Endereco">Endereço:</label>
                            <InputText id="Endereco" @bind-Value="@_cliente.Endereco" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">

                        <div class="col-lg-6">
                            <label id="Telefone">Telefone:</label>
                            <InputText id="Telefone" @bind-Value="@_cliente.Telefone" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <label id="Celular">Celular:</label>
                            <InputText id="Celular" @bind-Value="@_cliente.Celular" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Email">E-mail:</label>
                            <InputText id="Email" @bind-Value="@_cliente.Email" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <br />
                            <br />
                            <input type="submit" value="Cadastrar" class="btn btn-primary" @onclick="OpenModal" />
                        </div>
                    </div>

                </EditForm>

            </div>

        </div>
    </div>

</div>

@code {

    private Models.Cliente _cliente = new Models.Cliente();
    private int idCount = 0;


    /// <summary>
    /// It opens the JS alert with a message
    /// </summary>
    /// <returns>void</returns>
    public async Task OpenModal()
    {
        await JSRuntime.InvokeAsync<string>(

           "jsFunctions.openModalCadastro", null);

        AddCliente();

    }

    private void AddCliente()
    {
        _cliente.Id = idCount++;
        Service.Add(_cliente);

        NavigationManager.NavigateTo("cliente");
    }


}

E a página “Editar.razor” terá o seguinte código:

@page "/Clientes/editar/{Id:int}"
@using Blazor.Data
@using Models = Blazor.Client.Models

@inject ClienteService Service
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager

<h3>Cadastrar Clientes</h3>

<div class="container">
    <h1 class="text-center">Cadastro de Clientes</h1>
    <div class="row" style="padding-top: 10%">
        <div class="col-lg-12">
            <div class="form-group-lg">
                <EditForm class="text-center" Model="@_cliente">

                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Nome"> Nome:</label>
                            <InputText id="Nome" @bind-Value="@_cliente.Nome" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                        <div class="col-lg-6">
                            <label id="Rg">RG:</label>
                            <InputText id="Rg" @bind-Value="@_cliente.Rg" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Cpf">CPF:</label>
                            <InputText id="Cpf" @bind-Value="@_cliente.Cpf" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <label id="Endereco">Endereço:</label>
                            <InputText id="Endereco" @bind-Value="@_cliente.Endereco" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">

                        <div class="col-lg-6">
                            <label id="Telefone">Telefone:</label>
                            <InputText id="Telefone" @bind-Value="@_cliente.Telefone" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <label id="Celular">Celular:</label>
                            <InputText id="Celular" @bind-Value="@_cliente.Celular" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-lg-6">
                            <label id="Email">E-mail:</label>
                            <InputText id="Email" @bind-Value="@_cliente.Email" class="form-control" />
                            <span class="text-danger" style="font-size:small;"></span>
                        </div>

                        <div class="col-lg-6">
                            <br />
                            <br />
                            <input type="submit" value="Editar" class="btn btn-primary" @onclick="OpenModal" />
                        </div>
                    </div>

                </EditForm>

            </div>

        </div>
    </div>

</div>

@code {

    private Models.Cliente _cliente;


    [Parameter]
    public int Id { get; set; }

    protected override async Task OnInitializedAsync()
    {
        _cliente = Service.GetById(Id);
    }

    public async Task OpenModal()
    {
        await JSRuntime.InvokeAsync<string>(

           "jsFunctions.openModalEdit", null);

        EditarCliente();

    }

    private void EditarCliente()
    {
        
        Service.Update(_cliente);

        NavigationManager.NavigateTo("cliente");

    }

}

Repare que em ambas as páginas temos códigos C# escritos dentro do bloco “@code”. Dentro desse bloco é possível criar qualquer código em C#.

Testando

Após realizar a codificação, rode o comando “dotnet build” para buildar a aplicação. Caso apareça algum erro, leia-o atentamente e verifique os passos anteriores deste artigo.

Deu tudo certo? Então, vamos rodar nossa aplicação:
dotnet run

Essa é a nossa página inicial:

Você pode navegar pelas páginas através do menu no lado esquerdo.

Nossa página “Clientes” está vazia:

Vamos cadastrar alguns clientes:

Agora, nossa página de “Clientes” foi preenchida:

Repare que podemos editar e excluir o dado cadastrado.
Vamos, primeiro, editar:

Agora, vamos excluí-lo:

We made it!

Todas as funções desse CRUD estão na classe “ClienteService.cs”. Lembra do bloco “@code” dentro das páginas “.razor”? Pois bem, é nesse bloco que chamamos as funções da nossa classe.

Este foi um pequeno exemplo de como utilizar o C# com WebAssembly através do Blazor. Espero que tenham gostado.

Qualquer dúvida, crítica ou sugestão, deixe nos comentários.

Acesse o código no Github.

Um grande abraço!

João Paulo de Castro Lima – Analista de Sistemas na BRQ

BRQ fale conosco