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:
- Executa em desempenho quase nativo;
- É executado em uma caixa de areia segura para a memória;
- 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
- Baixe e instale o .NET Core 2.1 SDK (2.1.300 ou posterior).
- 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.
Um grande abraço!