facebook

Blog

Resta aggiornato

Alcuni trucchi per non impazzire durante la migrazione
Migrare un progetto Blazor Wasm Hosted da .NET Core 3.1 a .NET 5
martedì 13 Aprile 2021
Blazor su .NET5

Uno degli obbiettivi della milestone in corso per un nostro cliente è la migrazione dalla versione .NET Core 3.1 alla versione .NET 5. Ripercorriamo insieme i passi che abbiamo intrapreso ed analizziamo i correttivi adottati per mitigare alcune breaking changes.

Il progetto in questione è un applicativo Blazor Wasm con un backend realizzato in .NET. Il Data Access Layer è realizzato con Entity Framework Core.

1. Partiamo dalle dipendenze

Al giorno d’oggi non esistono progetti che non abbiano qualche dipendenza da una o più librerie esterne. Per cominciare, ci accertiamo quindi che tutte le dipendenze del nostro progetto siano compatibili con la nuova versione del framework. A tal proposito, ricordo che .NET 5 supporta tutte le librerie che supportano .NET Standard fino alla versione 2.1. Nel nostro caso, avevamo poche ma fondamentali dipendenze tutte sviluppate con .NET Standard 2.0 e partivamo quindi avvantaggiati. 

Il primo consiglio che vi condivido è di prediligere, tra le librerie esterne, quelle che implementano .NET Standard con una buona reputazione e supporto.

2. Aggiorniamo Blazor

Una volta verificato che tutte le nostre dipendenze saranno utilizzabili anche dopo l’upgrade del framework possiamo iniziare l’aggiornamento dei vari progetti. Partiamo dal progetto di backend e apportiamo le seguenti modifiche: 

  • Aggiornare il file global.json con la nuova versione dell’SDK
{ 
  "sdk": { 
	"version": "5.0.102" 
   } 
} 
  • Aggiornare il TargetFramework del file di progetto 
<PropertyGroup> 
<TargetFramework>net5.0</TargetFramework> 
</PropertyGroup> 
  • Aggiornare pacchetti Blazor Server
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="5.0.2" /> 
  • Aggiornare tutte le altre dipendenze alla versione che supporti il Framework .NET 5 ad esclusione di EF Core.

La procedura non è particolarmente complessa e non presenta nessuna criticità. 

Passiamo adesso al progetto di frontend (Blazor Web Assembly) dove qualche piccolo cambiamento in più è necessario ma niente di difficile. 

I passi necessari per aggiornare il front end Blazor sono:

  • Aggiornare il TargetFramework del file di progetto
<PropertyGroup> 
        <TargetFramework>net5.0</TargetFramework> 
</PropertyGroup>
  • Aggiornare pacchetti Blazor Client 
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.2" /> 
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.2" PrivateAssets="all" /> 
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /><PackageReference Include="System.Net.Http.Json" Version="5.0.0" /> 
  • Modificare il file Index.html

Rimuovere

<app>Loading...</app> 

Aggiungere

<div id="app">Loading...</div> 
  • Modificare il file App.razor
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> 
  • Modificare il file Program.cs
var builder = WebAssemblyHostBuilder.CreateDefault(args); 
builder.RootComponents.Add<App>("#app"); 
  • Aggiornare tutte le altre dipendenze alla versione che supporti il Framework .NET 5

Terminata la procedura indicata la nostra applicazione è aggiornata alla nuova versione del framework e possiamo iniziare a utilizzare tutte le nuove funzionalità che il framework ci mette a disposizione. Per maggiori informazioni su casi particolari potete dare uno sguardo alla guida ufficiale.

3. Aggiorniamo Entity Framework

Per completare la migrazione del nostro progetto ci troviamo adesso davanti ad una scelta, continuare ad utilizzare Entity Framework nella versione 3.1 oppure completare il percorso di migrazione con l’upgrade alla versione 5.0 di EF Core?  

Potrebbe sembrare una scelta banale: visto che stiamo aggiornando all’ultima versione del framework perché non aggiornare anche la “Dipendenza” all’ultima versione?  

Premesso che Entity Framework Core è una libreria .NET Standard 2.0 già dalla versione 3.1, entrambe le versioni sono quindi supportate in .NET 5. Purtroppo però Entity Framework Core 5.0 presenta una serie di modifiche al provider LINQ per SQL Server (che utilizziamo nel nostro progetto ) e che potrebbero avere un impatto non prevedibile sulla nostra applicazione sia in termini funzionali che in termini prestazionali. 

Fortunatamente il nostro progetto è fornito di una buona suite di test e test di carico così decidiamo di provare l’upgrade anche di Entity Framework Core alla versione 5.0. 

Tecnicamente essendo una libreria esterna quello che abbiamo fatto è stato aggiornare la versione dei pacchetti nel file di progetto.

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" /> 
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.2" /> 
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2"> 

Abbiamo lanciato i test e come era prevedibile molti test non sono stati superati. Non ci siamo persi d’animo e siamo andati ad analizzare i vari test falliti ed abbiamo individuato alcune criticità dovute ai breaking changes introdotti nella nuova versione a cui eravamo preparati, ed alcuni non documentati che abbiamo scoperto ed affrontato strada facendo da buoni sviluppatori. Una delle novità introdotte in Entity Framework Core 5 è lo Split Queries.

Il provider LINQ to SQL converte le query LINQ in codice T-SQL da eseguire sul DBMS e mappa il risultato di queste query sulle entità definite nel context.  Utilizzando l’opzione Split Queries, il provider LINQ genererà N queries piccole per ogni Tabella coinvolta utilizzando le Join come filtro sulle tabelle collegate, in questo modo sia la fase di recupero dati dal DBMS che la successiva fase di mapping delle entità di dominio posso essere parallelizzate ed ottimizzate, Un piccolo esempio preso dalla documentazione per capire meglio il meccanismo.

var artists = context.Artists         
     .Include(e => e.Albums)  
     .ToList(); 

Genererà il codice T- SQL

SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title" 
FROM "Artists" AS a 
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId" 
ORDER BY a."Id", a0."Id" 

Mentre

var artists = context.Artists 
     .Include(e => e.Albums) 
     .AsSplitQuery() 
     .ToList(); 

Genererà il codice T-SQL

SELECT a."Id", a."Name" 
FROM "Artists" AS a 
ORDER BY a."Id" 
  
SELECT a0."Id", a0."ArtistId", a0."Title", a."Id" 
FROM "Artists" AS a 
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId" 
ORDER BY a."Id" 

Di default lo split queries è disabilitato ed è possibile utilizzarlo singolarmente su ogni query con il metodo AsSplitQuery() oppure globalmente nell’ OnConfiguring (documentazione). 

È inoltre possibile abilitare un warning che a runtime segnali le query che potenzialmente potrebbero beneficiare di un incremento di performance con l’abilitazione dello Split Queries.

services.AddDbContext<ItinerisDbContext>(opt => 
        	{ 
                opt.UseSqlServer(Configuration.GetConnectionString("SqlServerConnectionString")); 
                opt.ConfigureWarnings(s => s.Log(RelationalEventId.MultipleCollectionIncludeWarning)); 
            }); 

Un’altra novità introdotta in EF Core è la possibilità di filtrare gli Include es:

var blogs = context.Blogs 
    .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese"))) 
	.ToList(); 

Le due novità appena viste introducono un breaking change non documentato, che i nostri test hanno portato alla luce.  Le query che producono proiezioni su diverse collezioni con l’istruzione Select devono contenere anche tutti gli Include necessari alla proiezione, altrimenti le proprietà delle collezioni collegate saranno sempre “null”.

Poco male, introdotti gli Include necessari i test falliti sono finalmente passati. 

Una volta risolti i problemi evidenziati dagli unit test lanciamo i test di carico ed i risultati non sono stati proprio all’altezza delle aspettative, in alcuni casi abbiamo riscontrato un degrado delle performance rispetto alla versione 3.1 anche del 50%.

Per effettuare il tuning delle query utilizziamo un’altra novità di Entity Framework Core 5: il log nelle query eseguite. Tramite questo meccanismo possiamo analizzarle e ottimizzarle. 

Il log si abilitata nel seguente modo:

services.AddDbContext<ItinerisDbContext>(opt => 
{ 
      … 
      opt.LogTo(l => Console.WriteLine(l)); 
}); 

Analizzando i warning ed i log forniti ci siamo lanciati in una sessione di tuning del codice. Un uso oculato dello split query e l’utilizzo di metodi specifici che il provider SQL Server mette a disposizione (ad esempio Like()) ci permette in parte di mitigare il calo di performance senza però raggiungere i livelli della versione precedente.

Conclusioni

Concludendo, se da una parte l’upgrade alla nuova versione nel framework ha generato reali benefici sia in termini di performance che nuove funzionalità, l’upgrade di EF Core non è stato né indolore e  veloce né tanto meno esente da imprecazioni . 

L’upgrade di EF Core lo consiglio in quei progetti dove si ha una buona copertura di test unitari per individuare automaticamente tutti i punti da modificare. Se il progetto non è corredato di test armatevi di tanta pazienza e ponete particolare attenzione ai metodi che recuperate dal DB.

Buona Strada

Scritto da

Antonio Venditti