Da ASP.NET MVC a ASP.NET CORE MVC

Il framework .NET si sta evolvendo molto rapidamente e ben presto la necessità di migrare un’applicazione ASP.NET MVC classica sul nuovo framework .NET CORE sarà sempre più sentita.
Come vedremo in dettaglio qui di seguito, la strada più semplice è creare una nuova web app direttamente nel “mondo” ASP.NET CORE MVC che, usando il codice già esistente (leggermente riadattato al nuovo contesto), ricalcherà esattamente le funzionalità della web app preesistente.

Per affrontare questa operazione sono necessarie conoscenze base delle varie componenti del framework e dei più diffusi design pattern: ASP MVC, Razor, Entity Framework, IoC, dependency injection, ecc…

In questo articolo svilupperemo tre punti:

  1. Analisi della struttura della web app sviluppata in ASP.NET MVC
  2. Creazione della nuova web app sviluppata in ASP.NET CORE MVC funzionalmente equivalente alla precedente
  3. Conclusioni

Il nostro scopo è evidenziare le differenze a livello di configurazione tra un’applicazione web ASP.NET MVC “classica” ed una ASP.NET CORE e studiare come lo sviluppo e la configurazione dei componenti dell’applicazione ASP.NET MVC possano essere efficacemente riutilizzati in una nuova app ASP.NET CORE.

Nonostante il framework sia stato pesantemente riscritto, snellito ed ottimizzato. il team di sviluppo è riuscito infatti a mantenere un buon livello di compatibilità con il mondo preesistente.

Analisi della struttura della web app sviluppata in ASP.NET MVC

Analizziamo velocemente la web app sviluppata in ASP.NET MVC: si tratta di una semplice applicazione web che utilizza Razor ed Entity Framework versione 6.2.

Le entità utilizzate nell’esempio sono due: “Scuole di musica” e “Maestri”, collegate tra loro.
È stato creato un DbContext per permettere, oltre alla gestione delle entità, la comunicazione con SQL Server.

Sono state utilizzate le “migrations” in quanto l’approccio allo sviluppo è stato Code First.
È stato inserito uno strato di servizi (un servizio per “Scuole di musica” e uno per “Maestri”) ed è stato utilizzato Unity.MVC5 come IoC container.
Le viste sono state create utilizzando Razor per la renderizzazione HTML dei modelli di dati.

Creazione della nuova web app sviluppata in ASP.NET CORE MVC funzionalmente equivalente alla precedente

In fase di creazione del progetto specifichiamo che si intende utilizzare MVC, in modo che VisualStudio crei automaticamente le classiche cartelle Model, View, Controller del pattern MVC e, più importante, perché inserisca nel file StartUp del progetto il seguente codice:

Il codice evidenziato configura l’applicazione per utilizzare il pattern MVC: notiamo come, nel metodo Configure, venga anche specificata la struttura delle rotte da seguire. Nei progetti ASP.NET MVC, invece, le rotte vengono definite all’interno del file RouteConfig.cs, che viene poi inizializzato in fase di StartUp del progetto.

La classe StartUp di un progetto ASP.NET CORE viene richiamata in fase di start up dell’applicazione ed è necessaria per definire i servizi utilizzati nell’applicazione e per comporre la pipeline dei suoi componenti.

Creata la struttura “vergine” del progetto ASP.NET CORE MVC, iniziamo a riportare le componenti del progetto ASP.NET MVC evidenziando step-by-step le modifiche da effettuare:

1. I modelli dei dati (Models) non necessitano di nessuna modifica, si riutilizzano così come sono.

2. I controller necessitano invece di un cambio di reference: per far sì che i controller ereditino dalla classe Controller è necessario aggiungere la referenza al pacchetto Microsoft.AspNetCore.Mvc, che prende il posto di System.Web.Mvc (che eravamo abituati ad utilizzare in ASP.NET MVC). Aggiunto il pacchetto, gli unici errori che rimangono all’interno delle classi dei controller sono quelli relativi ai servizi, non ancora aggiunti!

3. All’interno delle View è necessario cambiare le reference ai modelli di dati utilizzati nelle singole viste mentre, all’interno del file _Layout.cshtml, è necessario modificare l’accesso ai file CSS e JS. Quindi è necessario cambiare per i CSS
da:

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)

a:

<enviroment include="Development">
     <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
     <link rel="stylesheet" href="~/css/site.css" />
</enviroment>

E per i JS, da:

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)

a:

<enviroment include="Development">
     <script src="~/lib/jquery/dist/jquery.js"></script>
     <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
     <script src="~/js/site.js" asp-append-version="true"></script>
</enviroment>
@RenderSection("scripts", required: false)

Infine, aggiungere nella configurazione il comando per l’uso dei file statici UseStaticFiles() così che l’applicazione possa accedere ai file CSS/JS all’interno della root del progetto:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseStaticFiles();
    app.UseMvc(routes =>
    {
       routes.MapRoute(
       name: "default",
       template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Effettuate queste modifiche, eseguendo la compilazione del progetto, gli unici errori che dovremmo ricevere sono quelli relativi ai servizi; prima, però, di riportare i nostri servizi all’interno del nuovo progetto, ricordiamoci di includere la parte di DbContext da cui questi dipendono. La configurazione del DbContext necessita di alcune accortezze. La classe del DbContext deve essere modificata

da:

public class AspNetWebAppDbContext : DbContext
{
    public AspNetWebAppDbContext() : base("AspNetWebAppDbContext")
    {}

    public DBSet<MusicSchool> MusicSchools { get; set; }
    public DBSet<Teacher> Teachers { get; set; }
   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

a:

public class AspNetCoreWebAppDbContext : DbContext
{
    public AspNetCoreWebAppDbContext(DbContextOptions<AspNetCoreWebAppDbContext> options) : base(options)
    {}

    public DbSet<MusicSchool> MusicSchools { get; set; }
    public DbSet<Teacher> Teachers { get; set; }
   
}

Adeguando la firma del costruttore a quella della nuova classe base. Aggiungiamo nella classe StartUp, la registrazione del nostro DbContext:

public void ConfigureServices(IServiceCollection services)
{
   services.AddDbContext<AspNetCoreWebAppDbContext>(options =>
   options.UseSqiServer(Configuration.GetConnectionString("AspNetCoreWebAppDbContext")));
   service.AddMvc();
}

Vediamo come la registrazione del DbContext impone la dipendenza da una connection string. È necessario, quindi, riportare la connection string della configurazione del progetto ASP.NET MVC nella nostra nuova configurazione.
Nei progetti ASP.NET CORE non esiste il file web.config, al suo posto è presente un file appsettings.json nel quale andremo ad aggiungere la stessa connection string vista in precedenza:

"ConnectionStrings": {
   "AspNetCoreWebAppDbContext": "Data Source=RONCO-PC\\RONCO2017;Initial Catalog=MusicSchool;Integrated Security=SSPI;"
},

A questo punto ci possiamo occupare dei nostri servizi; creiamo una cartella “Services” nella quale riportiamo le interfacce dei servizi con le loro implementazioni.

Dovremo aggiungere ad ogni implementazione il costruttore che accetta come parametro il DbContext (l’Injection intrinseca di ASP.NET CORE farà sì che non saranno necessarie ulteriori modifiche):

private readonly AspNetCoreWebAppDbContext _context;
public TeacherService(AspNetCoreWebAppDbContext context)
{
   _context = context;
}

L’ultimo passo da effettuare sui servizi è la loro registrazione in fase di start up. Aggiungiamo il seguente codice all’interno del metodo ConfigureServices nella classe StartUp:

services.AddTransient<IMusicSchoolService, MusicSchoolService>();
services.AddTransient<ITeacherService, TeacherService>();

Notiamo anche che non è necessaria l’aggiunta di un container IoC (nel caso della nostra app ASP.NET era stato utilizzato Unity.MVC5) poiché il suo utilizzo è intrinseco a seguito della registrazione in fase di start up dei servizi: i servizi registrati potranno essere iniettati ovunque sarà necessario all’interno dell’applicazione (questa automazione è di default per i componenti nativi di .Net Core!).

L’ultimo passo da compiere per completare il porting del progetto è aggiungere le migrazioni alla soluzione. Purtroppo NON è possibile riportare le migrazioni effettuate con Entity Framework all’interno del progetto .Net Core, ma è necessario rieseguire l’aggiunta delle migrazioni utilizzando, quindi, Entity Framework Core. Come tutta la struttura di .Net Core, infatti, anche le migrazioni sono state completamente riscritte.

La nuova implementazione di EF, in fase di inserimento delle migrations, genera un file chiamato Snapshot che racchiude in sé tutta la struttura attuale del database. Questo file viene aggiornato all’aggiunta di ogni nuova migration ed è proprio questo file che viene interrogato dal framework  quando l’applicazione necessita di accedere alla struttura della base dati.

Per questo motivo consiglio, onde evitare di sprecare inutilmente tempo, di rilanciare le migrations dal nuovo progetto ASP.NET CORE senza provare a riutilizzare le vecchie.

Aggiungendo le migrations tramite i classici comandi da Console “Add-Migration” e “Update-Database”, la nuova applicazione diviene pronta ad essere utilizzata!

E, cosa molto importante, siamo stati in grado di utilizzare esattamente le stesse porzioni di codice sia per la logica applicativa, che per i modelli dati che avevamo creato nel progetto ASP.NET di origine.

Conclusioni

È indiscutibile che Microsoft stia sponsorizzando pesantemente la nuova versione del Framework .Net Core e, sicuramente, il futuro dello sviluppo software si sposterà in questa direzione. Personalmente l’ho trovato fin da subito più intuitivo, più veloce e più strutturato del suo predecessore. Anche la parte di View si avvantaggia di un nuovo potente strumento: gli Helper TAG con i quali il markup delle pagine risulta più leggibile e di più facile comprensione (nell’articolo non sono stati affrontati i nuovi tag).

Come già preannunciato ad inizio lettura, con il presente articolo volevo dimostrare che evolvere un applicativo esistente portandolo sull’ultima frontiera del Framework .NET è possibile, anche se, a mio avviso, con alcune limitazioni ed uno sforzo non trascurabile.

Il percorso prima descritto, infatti, risulta facilmente realizzabile per progetti di piccola/media entità che non dipendano da librerie di terze parti (potenzialmente incompatibili con .NET Core) e con una base dati modesta. Come consigliato da Microsoft stessa, vedo poco realizzabile un intervento così massiccio su applicazioni enterprise complesse e consolidate in anni di lavoro. In tal caso è preferibile rimanere su ASP.NET e preventivare un intervento di riscrittura evolutiva in ottica .NET Core, piuttosto con un porting fine a se stesso.

Se invece si parla di nuovo progetto, la risposta è solo una: .NET Core .

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *