In questo tutorial andremo a modificare l’applicazione di esempio inclusa in Visual Studio per creare una Single Page Application di accesso ai dati con Blazor Server.

Prima di iniziare a sviluppare la nostra applicazione Blazor, dobbiamo procedere con l’installazione del Net Core 3.1 SDK dal seguente URL https://dotnet.microsoft.com/download.

Apriamo Visual Studio 2019 e creiamo un nuovo progetto Blazor.

Questa immagine ha l'attributo alt vuoto; il nome del file è blazorapp-tutorial-1-sc3.jpg

Assegniamo un nome al nostro progetto (ad esempio potremmo chiamarlo BlazorAppDB) e successivamente selezioniamo l’opzione “App server Blazor“.

Le App server Blazor sono modelli di progetto per la creazione di applicazioni che vengono eseguite lato server.

In questa stessa finestra, è anche possibile abilitare le funzionalità di autenticazione. Per approfondire l’argomento sulle nuove funzionalità di ASP.NET Core Identity ti suggerisco di leggere la seguente documentazione Nuove opzioni di autenticazione WebApp per Visual Studio

Dopo che Visual Studio avrà generato i file, il nostro progetto sarà così costituito:

Dato che andremo ad utilizzare Entity Framework per mappare le nostre tabelle, utilizzando NuGet andiamo ad installare le seguenti librerie:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

Nella cartella “Data” Creiamo ora la nostra classe con i campi che saranno presenti nella tabella “Cliente”:

namespace BlazorAppDB.Data
{
   public class Cliente
  {
       public string Id { get; set; }
       public string Cognome { get; set; }
       public string Nome { get; set; }
       public string Indirizzo { get; set; }
       public string Comune { get; set; }
       public string Telefono { get; set; }
  }
}

Sempre nella cartella “Data”, creiamo la classe SqlDbContext per connetterci al database tramite Entitiy Framework.

using Microsoft.EntityFrameworkCore;

namespace BlazorAppDB.Data
{
   public class SqlDbContext : DbContext
  {
       public SqlDbContext(DbContextOptions<SqlDbContext> options)
          : base(options)
      {
      }
       public DbSet<Cliente> Clienti { get; set; }
  }
}

Alcuni comandi degli strumenti di EF Core (ad esempio, i comandi delle migrazioni ) richiedono la creazione di un’istanza di DbContext per raccogliere informazioni dettagliate sui tipi di entità e come eseguono il mapping al database. Per approfondire l’argomento: Creazione di DbContext in fase di progettazione

Possiamo creare il nostro database. Apriamo “Esplora oggetti di SQL Server” ed aggiungiamo un nuovo database che chiameremo “BlazorAppDB”.

Al termine della creazione selezioniamolo con il tasto destro del mouse e successivamente la voce aggiorna.

Selezioniamo con il tasto destro del mouse il nostro database e la voce “proprietà” e andiamo a recuperare la stringa di connessione al database.

Che andremo ad inserire nel file “appsettings.json” della nostra applicazione.

Occorre, ora, registrare la nostra stringa di connessione nel file “Startup.cs” nel metodo di configurazione dei servizi “ConfigureServices“.

Considerando che il nostro database non ha alcuna tabella, procediamo con la creazione della nostra tabella “Cliente”. Dal menù “Strumenti” selezioniamo la voce “Gestione pacchetti NuGet” ed avviamo la “Console di Gestione pacchetti“.

Digitiamo il seguente comando che andrà a creare gli script necessari per l’aggiornamento incrementale dello schema del database:

add-migration Initial

e successivamente utilizzeremo il seguente comando per creare la nostra tabella:

update-database

Per approfondire l’utilizzo delle Migrazioni in Entity Framework ti suggerisco di leggere: Migrazioni

Ora che la nostra tabella è stata generata, possiamo procedere con la creazione dei metodi per le operazioni di Create, Update e Delete (CRUD).

Per prima cosa andremo a scrivere, nella cartella “Data”, la nostra interfaccia “IClienteService” con la dichiarazione dei metodi sopra descritti:

using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorAppDB.Data
{
   public interface IClienteService
  {
       Task<List<Cliente>> ElencoClienti();
       Task<bool> AggiungiCliente(Cliente cliente);
       Task<bool> ModificaCliente(string id, Cliente cliente);
       Task<Cliente> DatiCliente(string id);
       Task<bool> CancellaCliente(string id);
  }
}

Successivamente andremo ad implementare i metodi nella nostra classe “ClienteService“:

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorAppDB.Data
{
   public class ClienteService : IClienteService
  {
       private readonly SqlDbContext _dbContext;
       public ClienteService(SqlDbContext dbContext)
      {
           _dbContext = dbContext;
      }
       public async Task<List<Cliente>> ElencoClienti()
      {
           return await _dbContext.Clienti.ToListAsync();
      }
       public async Task<bool> AggiungiCliente(Cliente cliente)
      {
           cliente.Id = Guid.NewGuid().ToString();
           _dbContext.Add(cliente);
           try
          {
               await _dbContext.SaveChangesAsync();
               return true;
          }
           catch (DbUpdateException)
          {
               return false;
          }
      }
       public async Task<Cliente> DatiCliente(string id)
      {
           return await _dbContext.Clienti.FindAsync(id);
      }
       public async Task<bool> ModificaCliente(string id, Cliente cliente)
      {
           if (id != cliente.Id)
          {
               return false;
          }
           _dbContext.Entry(cliente).State = EntityState.Modified;
           await _dbContext.SaveChangesAsync();
           return true;
      }
       public async Task<bool> CancellaCliente(string id)
      {
           var idcliente = await _dbContext.Clienti.FindAsync(id);
           if (idcliente == null)
          {
               return false;
          }
           _dbContext.Clienti.Remove(idcliente);
           await _dbContext.SaveChangesAsync();
           return true;
      }
  }
}

Ora che abbiamo inserito tutta la logica per le operazioni CRUD nella classe “ClienteService” possiamo procedere con la sua registrazione nel file “Startup.cs“.

Completata la creazione del BackEnd andremo ora a creare le i nostri componenti Razor per la visualizzazione e gestione dei nostri clienti.

Per prima cosa andremo a creare il file “ListaClienti.razor” nella cartella “Pages“:

@page "/listaclienti"

@using BlazorAppDB.Data
@inject IClienteService ClienteService

<h2>LISTA CLIENTI</h2>
<p>
   <a href="/aggiungicliente">Aggiungi cliente</a>
</p>

@if (clienti == null)
{
   <img src="./basicloader.gif" />

}
else
{
   <table class='table'>
       <thead>
           <tr>
               <th>Cognome</th>
               <th>Nome</th>
               <th>Indirizzo</th>
               <th>Comune</th>
               <th>Telefono</th>
           </tr>
       </thead>
       <tbody>
           @foreach (var cliente in clienti)
          {
               <tr>
                   <td>@cliente.Cognome</td>
                   <td>@cliente.Nome</td>
                   <td>@cliente.Indirizzo</td>
                   <td>@cliente.Comune</td>
                   <td>@cliente.Telefono</td>
                   <td>
                       <a href='/modificacliente/@cliente.Id'>Modifica</a>
                       <a href='/cancellacliente/@cliente.Id'>Cancella</a>
                   </td>
               </tr>
          }
       </tbody>
   </table>
}

@code {
   List<Cliente> clienti;

   protected override async Task OnInitializedAsync()
  {
       clienti = await ClienteService.ElencoClienti();
  }

}

Il suddetto file creerà l’elenco dei clienti con la possibilità, accanto a ciascun nome, di cancellarlo o aprire la pagina per modificarlo.

Ma prima di tutto abbiamo bisogno di una pagina per aggiungere un nuovo cliente.

Sempre nella cartella “Pages” andremo ad aggiungere il file “AggiungiCliente.razor”:

@page "/aggiungicliente"

@using BlazorAppDB.Data
@inject IClienteService ClienteService
@inject NavigationManager NavigationManager

<h2>AGGIUNGI CLIENTE</h2>
<p>
   <hr />
</p>
<form>
   <div class="row">
       <div class="col-md-8">
           <div class="form-group">
               <label for="Name" class="control-label">Cognome</label>
               <input for="Name" class="form-control" @bind="@cliente.Cognome" />
           </div>
           <div class="form-group">
               <label for="Department" class="control-label">Nome</label>
               <input for="Department" class="form-control" @bind="@cliente.Nome" />
           </div>
           <div class="form-group">
               <label for="Designation" class="control-label">Indirizzo</label>
               <input for="Designation" class="form-control" @bind="@cliente.Indirizzo" />
           </div>
           <div class="form-group">
               <label for="Company" class="control-label">Comune</label>
               <input for="Company" class="form-control" @bind="@cliente.Comune" />
           </div>
           <div class="form-group">
               <label for="Company" class="control-label">Telefono</label>
               <input for="Company" class="form-control" @bind="@cliente.Telefono"/>
           </div>
       </div>
   </div>
   <div class="row">
       <div class="col-md-4">
           <div class="form-group">
               <input type="button" class="btn btn-primary" @onclick="@AggiungiCliente" value="Save" />
               <input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
           </div>
       </div>
   </div>
</form>

@code {

   Cliente cliente = new Cliente();
   
   protected async Task AggiungiCliente()
  {
       await ClienteService.CreaCliente(cliente);
       NavigationManager.NavigateTo("listaclienti");
  }
   
   void Cancel()
  {
       NavigationManager.NavigateTo("listaclienti");
  }

}    

Per la modifica di un cliente andremo ad aggiungere (sempre nella cartella “Pages“) il file “ModificaCliente.razor”:

@page "/modificacliente/{ID}"

@using BlazorAppDB.Data
@inject IClienteService ClienteService
@inject NavigationManager NavigationManager

<h2>MODIFICA CLIENTE</h2>
<p>
   <hr />
</p>
<form>
   <div class="row">
       <div class="col-md-8">
           <div class="form-group">
               <label for="Name" class="control-label">Cognome</label>
               <input for="Name" class="form-control" @bind="@cliente.Cognome" />
           </div>
           <div class="form-group">
               <label for="Department" class="control-label">Nome</label>
               <input for="Department" class="form-control" @bind="@cliente.Nome" />
           </div>
           <div class="form-group">
               <label for="Designation" class="control-label">Indirizzo</label>
               <input for="Designation" class="form-control" @bind="@cliente.Indirizzo" />
           </div>
           <div class="form-group">
               <label for="Company" class="control-label">Comune</label>
               <input for="Company" class="form-control" @bind="@cliente.Comune" />
           </div>
           <div class="form-group">
               <label for="Company" class="control-label">Telefono</label>
               <input for="Company" class="form-control" @bind="@cliente.Telefono" />
           </div>
       </div>
   </div>
   <div class="row">
       <div class="form-group">
           <input type="button" class="btn btn-primary" @onclick="@AggiornaCliente" value="Update" />
           <input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
       </div>
   </div>
</form>


@code {

  [Parameter]
   public string id { get; set; }

   Cliente cliente = new Cliente();

   protected override async Task OnInitializedAsync()
  {
       cliente = await ClienteService.DatiCliente(id);
  }

   protected async Task AggiornaCliente()
  {
       await ClienteService.ModificaCliente(id, cliente);
       NavigationManager.NavigateTo("listaclienti");
  }

   void Cancel()
  {
       NavigationManager.NavigateTo("listaclienti");
  }

}

Per finire, andremo a creare la pagina “CancellaCliente.razor”:

@page "/cancellacliente/{id}"

@using BlazorAppDB.Data
@inject IClienteService ClienteService
@inject NavigationManager NavigationManager

<h2>CANCELLA CLIENTE</h2>
<p>
   <hr />
</p>
<p>Sei sicuro di voler cancellare il cliente @id?</p>

<div class="col-md-4">
   <table class="table">
       <tr>
           <td>Cognome</td>
           <td>@cliente.Cognome</td>
       </tr>
       <tr>
           <td>Nome</td>
           <td>@cliente.Nome</td>
       </tr>
       <tr>
           <td>Indirizzo</td>
           <td>@cliente.Indirizzo</td>
       </tr>
       <tr>
           <td>Comune</td>
           <td>@cliente.Comune</td>
       </tr>
       <tr>
           <td>Telefono</td>
           <td>@cliente.Telefono</td>
       </tr>
   </table>

   <div class="form-group">
       <input type="button" value="Delete" @onclick="@Delete" class="btn btn-primary" />
       <input type="button" value="Cancel" @onclick="@Cancel" class="btn" />
   </div>
</div>

@code {

  [Parameter]
   public string id { get; set; }
   Cliente cliente = new Cliente();

   protected override async Task OnInitializedAsync()
  {
       cliente = await ClienteService.DatiCliente(id);
  }

   protected async Task Delete()
  {
       await ClienteService.CancellaCliente(id);
       NavigationManager.NavigateTo("listaclienti");
  }

   void Cancel()
  {
       NavigationManager.NavigateTo("listaclienti");
  }
}    

Per modificare il menu laterale, occorre modificare la pagina “NavMenu.razor” che si trova nella cartella “Shared” come segue:

<div class="top-row pl-4 navbar navbar-dark">
   <a class="navbar-brand" href="">BlazorAppDB</a>
   <button class="navbar-toggler" @onclick="ToggleNavMenu">
       <span class="navbar-toggler-icon"></span>
   </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
   <ul class="nav flex-column">
       <li class="nav-item px-3">
           <NavLink class="nav-link" href="listaclienti">
               <span class="oi oi-plus" aria-hidden="true"></span>Elenco Clienti
           </NavLink>
       </li>
   </ul>
</div>

@code {
   private bool collapseNavMenu = true;

   private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

   private void ToggleNavMenu()
  {
       collapseNavMenu = !collapseNavMenu;
  }
}

Per concludere possiamo cancellare i files di template che sono stati creati automaticamente all’avvio del progetto:

  • Data\WeatherForecast.cs
  • Data\WeatherForecastService.cs
  • Pages\Counter.razor
  • Pages\FetchData.razor

Ora premiamo “F5” ed eseguiamo la nostra prima applicazione Blazor con accesso ai dati.

Il codice open source è disponibile nel repository GitHub/EtabetaWeb