Sei sulla pagina 1di 300

ASP.

NET Core
Daniele Bochicchio
Chief Digital Officer @icubedsrl
Microsoft Regional Director
Microsoft ASP.NET MVP

Network Manager @aspitalia


danieleb@icubed.it
@dbochicchio
Cos’è .NET Core
Piattaforma di sviluppo cross-platform
Windows, macOS, Linux
Flexible deployment
Side-by-side o machine-wide
Open source su GitHub
Supportato da Microsoft
Eredita Base Class Library di .NET Framework 4.6
Rivisto: no dipendenze Windows
Compatibile .NET Framework, Xamarin and Mono
Com’è fatto .NET Core
.NET Runtime
• Type system, assembly loading
• RyuJIT, GC
• Native interop
Framework libraries
• System.*
• IO, Network, Data, Patterns
• Distribuiti tramite NuGet
.NET Core SDK
• Tools
• Compilatori
dotnet host app
• Lancia app e ospita il runtime
Linguaggi
C# e F# da .NET Core 1
VB arrivato con .NET Core 2
Si compilano tramite SDK
Editor testuali
• Multi piattaforma: Visual Studio Code, Sublime Text, Vim
• Windows: Visual Studio
.NET Core app = sono tutte console app
Usato è usato anche da:
• ASP.NET Core
• Windows 10 Universal Windows Platform (UWP)
• Xamarin.Forms
Installazione
Microsoft .NET Core
• CoreCLR runtime
• dotnet app launcher
Microsoft .NET Core SDK
• Gestione pacchetti
• Compilazione
Dal sito https://www.microsoft.com/net/core
• Windows: installer
• Linux (Ubuntu, debian, Fedora, openSuse, CentOS): scripts
• macOS: installer
• Docker: microsoft/dotnet:latest
Architettura
Ambiente
• x86 (Windows)
• x64
• ARM64 e ARM32 in arrivo

90% codice condiviso

Codice specifico
• CoreCLR
• System.IO e System.Security.Cryptography.Algorithms
.NET Core CLI (Command Line Interface)
Linea di comando
• cmd, powershell, bash
Intero ciclo di sviluppo
Cross platform
• Windows, MacOS, Linux (Ubuntu, debian, Fedora, openSuse, CentOS)
Distribuito con .NET Core runtime
• Side by side (multiversione)
• .NET Core SDK per sviluppare, altrimenti solo launcher
Perché?
• Strumento base degli IDE
• Gestione Entity Framework, Cache, Bundling ecc
Comandi di .NET Core CLI
New
• Linguaggio: C#, F#, VB
• Tipologie: console, web, lib, xunittest
• Eventualmente usare yeoman
Restore
• Sulla base delle reference di progetto (o project.json)
• Nuget v3: cache, performante
• No Powershell
Build
• Incremental compilation
• Portable o self-contained
• Multi framework: .NET Core o .NET Framework
• Multi runtime (osx, deb)
• Script pre/post build
Demo
DotNet CLI exploration
Comandi di .NET Core CLI
Run
• Compila e lancia
• Multi target framework
• Diverso da dotnet file.dll
Test
• Unit test, nunit o xunit
• Motori installati via NuGet
• Sezione testRunner in project.json
Pack
• Pacchetto nuget, con i simboli
Publish
• Deployment su cartella
• Script pre/post publish
Tools
Comandi aggiuntivi
• Deploy mediante NuGet
• Sostituiscono Powershell
Usati e creati da Microsoft
• Microsoft.Extensions.SecretManager.Tools
• Microsoft.DotNet.Watcher.Tools
• Microsoft.AspNetCore.Server.IISIntegration.Tools
• Microsoft.EntityFrameworkCore.Tools
• Microsoft.AspNetCore.Razor.Tools
• Microsoft.Extensions.Caching.SqlConfig.Tools
Terze parti
• BundlerMinifier.Core
.NET Standard Library
Specifiche per set API
• BCL indipendente dalla
piattaforme

Comune a tutti i runtime


• .NET Core, .NET Framework

Garanzia di compatibilità
• Anche all’indietro grazie alle
versioni
• Più comodo rispetto a PCL

Official Artifacts da seguire


.NET Standard Library
Ogni versione è retro- .NET Standard 1.01.0 1.11.1 1.21.2 1.31.3 1.41.4 1.51.5 1.61.6 2.02.0
compatibile con le .NET Core 1.01.0 1.01.0 1.01.0 1.01.0 1.01.0 1.01.0 1.01.0 2.02.0
precedenti .NET Framework

1.6: .NET Core 1 (con .NET Core 1.x 4.54.5 4.54.5 4.5.14.5.1 4.64.6 4.6.14.6.1 4.6.24.6.2
SDK))
2.0: .NET Core 2
.NET Framework
(con .NET Core 2.0 4.54.5 4.54.5 4.5.14.5.1 4.64.6 4.6.14.6.1 4.6.14.6.1 4.6.14.6.1 4.6.14.6.1
SDK)
Mono 4.64.6 4.64.6 4.64.6 4.64.6 4.64.6 4.64.6 4.64.6 5.45.4
Xamarin.iOS 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.1410.14
Xamarin.Mac 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.83.8
Xamarin.Android 7.07.0 7.07.0 7.07.0 7.07.0 7.07.0 7.07.0 7.07.0 8.08.0

10.0.1629910.0. 10.0.1629910.0. 10.0.1629910.0.


UWP 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0
16299 16299 16299

Windows 8.08.0 8.08.0 8.18.1

Windows Phone 8.18.1 8.18.1 8.18.1

Silverlight per
8.08.0
Windows Phone
.NET Standard Library
Pacchetti NuGet
• Estrema modularizzazione
• Assembly/Package per namespace

Pacchetti ufficiali e terze parti con target multipli


• Netxxx (es: net461): target su .NET Framework
• netstandardx.x (es. netstandard1.6): target su .NET Standard
• Compatibile quindi con .NET Core e .NET Framework 4.6.1
• netcoreappx.x (es. netcoreapp1.0): target su .NET Core

https://www.nuget.org/packages/NETStandard.Library
• Metapackage: insieme di tutti i pacchetti .NET STandard
https://www.nuget.org/packages/Microsoft.NETCore.App
• Metapackage: insieme di tutti i pacchetti .NET Core
Cosa vuol dire .NET Standard 2
Grazie agli shim inclusi in .NET Standard 2, si può referenziare
qualsiasi libreria .NET Framework – senza compilazione
Si ha accesso ad una miriade di funzionalità che mancavano in
.NET Core 1.x
• SmptClient ☺
• DataSet 
• System.XML 
• +20K API e il 70% dei package di NuGet
Comparazione con .NET Framework
App model
• .NET Core supporta solo console e ASP.NET
• WPF, WinForms non supportati perché specifici per Windows
• Tecnicamente possono esserci pacchetti NuGet per WPF, per esempio, compatibili .NET
Standard, ma funzionanti solo su Windows
API
• La maggior parte in comune, ma in assembly diversi (riorganizzati)
• Non API specifiche di Windows
• No Code Access Security

.NET Core è multi piattaforma e open source


• .NET Framework è read only per un sottoinsieme
Console application con .NET Core
public class Program
Entry point {
• In Program.cs public static void Main(string[] args)
{
• Metodo statico Main Console.WriteLine("Hello world!");
}
Parametri opzionali attraverso args }
Namespace System.Console
• Lettura e scrittura
• Colorazione
Esecuzione
• dotnet run: compila, necessario SDK installato
• dotnet nomedll: lancia l’assembly compilato
csproj
Con VS 2017 .NET Core <Project Sdk="Microsoft.NET.Sdk">
ha il classico csproj <PropertyGroup>
• Ma è una versione semplificata <VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha</VersionSuffix>
• Nelle preview veniva usato
<TargetFrameworks>netcoreapp2
project.json </TargetFrameworks>
</PropertyGroup>

Ogni framework ha opzionalmente </Project>

• Specifiche dipendenze
• Dipendenze su GAC
• Opzioni di compilazione
csproj
<Target Name="MyPreCompileTarget"
Script da eseguire BeforeTargets="Build">
• Prima e dopo compilazione o pubblicazione <Exec Command="generateCode.cmd" />
</Target>
• Array o singola istruzione <Target Name="MyPostCompileTarget"
• Bat, cmd, sh, bash, DOS AfterTargets="Publish">
<Exec Command="obfuscate.cmd" />
<Exec Command="removeTempFiles.cmd" />
</Target>

Opzioni del runtime <PropertyGroup>


<ServerGarbageCollection>true</ServerGarbageCollection>
• Modalità del Garbage Collection <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection>
• Utile in ASP.NET <!– esempio, non sono valori suggeriti -->
• Impostazioni del thread pool <ThreadPoolMinThreads>10</ThreadPoolMinThreads>
<ThreadPoolMaxThreads>100</ThreadPoolMaxThreads>
</ProeprtyGroup>
Compilazione
Genera in release dotnet publish –c release
• Cartella release\netcoreapp2.0

outputName.dll
• l’applicazione con l’entry point

outputName.runtimeconfig.json
• le runtimeOptions specificate nel progetto (opzionale)

outputName.deps.json
• lista di tutte le dipendenze. Necessario in ambienti dinamici, come ASP.NET
Framework-dependent deployments
Modello predefinito
dotnet publish –c release -f netcoreapp2.0

Si distribuisce
• App con le dipendenze (comprese .NET Core libraries)
• Non il .NET Core Runtime
• Possibile variazione di comportamento in caso di patch

Richiesto .NET Core sulla target machine


• (Microsoft Azure per esempio già ne dispone)

Parametro -f necessario solo se multi framework


• Default è già netcoreapp2.0
Self-contained deployments
Si distribuisce
• App con le dipendenze (comprese .NET Core libraries)
• .NET Core Runtime
• Controllo totale
Non è richiesto .NET Core sulla target machine
Si indicano i runtime da supportare
"runtimes": {
"win10-x64": {},
"osx.10.10-x64": {}
Occorre generare più output per piattaforma }
• Maggiori dimensioni

dotnet publish -c release -r win10-x64


dotnet publish -c release -r osx.10.10-x64
Sviluppare librerie
Ha senso che siano compatibili con .NET Framework

frameworks => netstandard2


• O altra versione, secondo la tabella

{
dependencies => NETStandard.Library "dependencies":{
"NETStandard.Library":"1.6.0"
},
"frameworks":{
"netstandard1.3":{}
}
}
Demo
Cos’è ASP.NET Core?
Una rivoluzionaria versione di ASP.NET
Basato su .NET Core
Un taglio netto rispetto al passato
Completamente riscritta
Cross-platform
Born in the cloud
Performante
ASP.NET Core
Milestone Data
Beta6 27/07/2015
Beta7 2/09/2015
Beta8 15/10/2015
RC1 Novembre 2015
RC2 Maggio 2016
1.0.0* Giugno 2016
1.1.1** Marzo 2017
1.2 Q2 2017
2.0 Q3 2017
2.1 Q1 2018

I tool di VS 2015 sono in preview ** I tool sono solo per VS 2017


https://github.com/aspnet/home/wiki/roadmap
Che fine ha fatto ASP.NET 5?
ASP.NET Core 1.0 è ASP.NET 5
Nel 2016 è stato cambiato di nome, per chiarire meglio che è un framework
tutto nuovo
Troverete ogni tanto riferimenti non aggiornati in documentazione e su
internet
ASP.NET 4.6 e ASP.NET Core
ASP.NET 2017

.NET Framework 4.6 .NET Core


.NET Framework per tutti gli scenari a cui siamo Librerie modulari e runtime ottimiazzato per il
abituati e solo su Windows cloud, cross-platform
MSBuild -> csc.exe
ASP.NET CodeDOM -> csc.exe Application DNX (Roslyn)

4.x Loose, GAC,


NuGet
Libraries NuGet

Application
FCL, GAC, NuGet NuGet

Open Source
Frameworks
IIS, HTTP.SYS
IIS Web Server Kestrel
Application
System.Web DNX
Host
Platform .NET BCL & FCL
.NET BCL & FCL
Libraries .NET on NuGet
.NET CLR
.NET CLR Runtime .NET Core CLR

IIS: WebEngine4.dll Runtime


DNX
Exe: OS Loader
ASP.NET
Operating Windows, OSX, Core
Windows
System Linux
ASP.NET Core 101
ASP.NET Core = runtime + ASP.NET MVC + ASP.NET Web API
Usando ASP.NET Core finiamo inevitabilmente ad usare ASP.NET MVC
Addio web.config
• Configurazione basata su codice/JSON
Modulare e componentizzabile
Gira su IIS o Self-hosted
• Kestrel
Dependency Injection già integrata
L’app web è di fatto una app console
• Vedi Startup.cs dentro il progetto
Novità di ASP.NET Core 2
Performance migliorate
Partenza più rapida, maggior numero di richieste al secondo
Partenza più rapida
Template semplificati
Benefici introdotti da .NET Core 2
.NET Standard 2, etc
Refactoring di componenti chiave
Runtime, hosting con IIS, autenticazione, logging
Si installa da https://get.asp.net
Server
ASP.NET è disegnato per essere disaccoppiato dal server
Tradizionalmente, ASP.NET è stato legato ad IIS
ASP.NET Core gira su IIS sfruttandolo come reverse proxy
Ci sono due server web:
• Microsoft.AspNetCore.Server.Kestrel (Kestrel, cross-platform)
• Microsoft.AspNetCore.Server.WebListener (WebListener, Windows)
La configurazione è nel file Program.cs
IIS vs Kestrel
Usando VS, viene usato IIS Express come reverse proxy
Usando IIS
• Possiamo optare per un HttpModule che fa da reverse proxy
• O usare WebListener, che gira direttamente su http.sys:
• più performante
• supporto all'autenticazione di Windows
• varie applicazioni in ascolto sulla stessa porta TCP
• uso di Server Name Indication, ovvero la possibilità di installare vari certificati SSL per lo stesso indirizzo IP del server;
• HTTP/2 su TLS (sfruttabile da Windows 10)
• invio diretto di file statici
• Caching delle risposte
• Supporto a WebSockets (da Windows 8 in poi)

Usando Kestrel
• È sempre necessario un reverse proxy
Possiamo implementare un server custom, implementando le classi IHttpRequestFeature e
IHttpResponseFeature
Demo
Esempio di Middleware: i file statici
Offre un ponte tra ASP.NET Core e le funzionalità aggiuntive
Ad esempio, i file statici sono supportati attraverso un MiddleWare
• Vedi Startup.cs / Configure -> UseStaticFiles
C’è una nuova impostazione del progetto, per gestire i file statici
• Vedi wwwroot
• Vedi UseContentRoot in Program.cs
Se si vogliono vedere file aggiuntivi, bisogna utilizzare UseDirectoryBrowser
Se si vuole servire una pagina di default statica, bisogna utilizzare
UseDefaultFiles (prima di UseStaticFiles)
Demo
Dov’è il web.config?
Esiste solo per compatibilità con IIS
Sostituito da un file chiamato Startup.cs
I parametri di configurazione sono in file JSON
• Con supporto degli environement

Dichiarazione in codice vs parsing a runtime di un file XML


Startup.cs
Classe che definisce l’entry point
Può stare in un assembly esterno, basta specificare la chiave
Hosting:Application
Può a propria volta accettare DI nel costruttore
Gestisce la Configuration attraverso un ConfigurationBuilder
Accende le opzioni di ASP.NET Core
• Es: file statici, MVC, etc
Semplificazione del progetto con .NET
Core 2
Aggiunta di un nuovo metapackage che include tutti i pacchetti di
uso comune
• Microsoft.AspNetCore.All
https://www.nuget.org/packages/Microsoft.AspNetCore.All
Resta possibile referenziare i singoli pacchetti a mano
Runtime Store
• Contiene tutti gli asset di runtime, precompilati, che non vengono
scaricati da NuGet – usa il nuovo metapackage
WebHostBuild di default
Tutto già preconfigurato per gli scenari più diffusi
Dependency Injection
• Motore di Inversion Of Control integrato
• Gestione del ciclo di vita (lifetime) dell’istanza
• Possibilità di utilizzare plugin per motori di terze parti
• ASP.NET include già un motore di DI
DI con ASP.NET Core
Per configurare ASP.NET
public void ConfigureServices(IServiceCollection services)
{

services.AddTransient<IMyService, MyService>();
services.AddScoped<IMyRepository, MyRepository>();
}

AddTransient: ogni richiesta crea un’istanza. Indicato per servizi stateless.


AddScoped: un’istanza per richiesta (es: Repository, Context di Entity
Framework)
AddSingleton: l’istanza viene creata alla prima richiesta e poi conservata.
Come usare la DI
Costruttore nei controller
public class MyRepository : IMyRepository
{
private readonly ApplicationDbContext _dbContext;

public MyRepository(ApplicationDbContext dbContext)


{
_dbContext = dbContext;
}
}

Oppure con [FromServices]


public IActionResult Index([FromServices] customerRepository)
{
...
}
IoC
È anche possibile accedere ai servizi con HttpContext.RequestServices
È comunque suggerito fare uso di DI quando possibile
Possiamo specificare un nostro IoC container così:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
// Autofac
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}

In questo caso usiamo Autofac e la DI va configurata in DefaultModule


Demo
Environment
Di default ASP.NET ha 3 ambienti preconfigurati (nome case insensitive)
• Development
• Staging
• Production

Il valore attuale viene letto dalla variabile ASPNETCORE_ENVIRONMENT


• Definibile nel web.config, nella config, su env variable, Azure, etc

Le informazioni per lo sviluppo sono persistite in un file chiamato


launchSettings.json dentro la directory Properties
IHostingEnvironment in Razor
Basta utilizzare il tag environment per fare output di pezzi di markup
differenti, ad esempio per gestire i CSS diversi tra ambienti

<environment names="Development">
...
</environment>
<environment names="Staging,Production">
...
</environment>
Convenzioni per startup
Se esiste una classe con nome Startup{EnvironmentName}, viene utilizzata
quella al posto di Startup
• Es: StartupDevelopment

Stesso discoro per il metodo Configure che vedremo a breve


Torniamo su Startup.cs
È l’entry point della configurazione
Rappresenta tutto quello che deve essere necessario al runtime per
funzionare
Carica servizi, middleware
Configura l’IoC
Gestisce gli environment
Il metodo Configure
Gestisce come ASP.NET risponderà alle richieste
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}

app.UseStaticFiles();

app.UseIdentity();

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Il metodo ConfigureServices
Per configurare ASP.NET
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

services.AddMvc();

// Add application services.


services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
In dettaglio
Use
• Consente di aggiungere funzionalità esterne attraverso il Middleware

ConfigureServices
• Consente di gestire i servizi utilizzati dall’applicazione
• Sfrutta il meccanismo di Dependency Injection di ASP.NET
Demo
Logging
Integrato all’interno del runtime
Legato all’interfaccia ILoggerFactory o ILogger<T>
var logger = loggerFactory.CreateLogger("LogCategory");
logger.LogInformation("No items found");

// con ILogger<T> non serve creare il logger esplicitamente


// logger = ILogger<Todo> che arriva da DI
logger.LogInformation("No items found");

Di default viene registrato un logger che scrive su Console/Output Windows


di VS
Si possono agganciare provider esterni
Verbosità di logging
Trace
• Livello più dettagliato, in genere riservato allo sviluppatore
Debug
• Informazioni utili solo al debug
Information
• Informazioni utili per tracciare il flusso applicativo
Warning
• Informazioni utili a tracciare stati non previsti
Error
• Da utilizzare per tracciare errori
Critical
• Da utilizzare per tracciare errori gravi o crash
Logging v2
ILoggingFactory e co sono integrati di default all’interno di Program.cs,
anziché Startup.cs
Il motore di DI registra già tutti i servizi di logging
Provider e filtri aggiuntivi vanno specificati in Program.cs
Vedi https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/

Già inclusa la possibilità di fare logging su


Console
Debug
EventLog
AzureAppServices
TraceSource (solo .NET Framework)
EventSource
Utilizzare le verbosità
Ci sono degli extension method con nome (es: LogInformation), oppure si può
utilizzare Log(LogLevel.Information…)
Ogni tipo di verbosità consente di passare informazioni aggiuntive, come ad
esempio
• Messaggio dettagliato di testo
• EventID
• Exception

Possiamo gestire il livello di verbosità in Startup.cs/Configure


loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(LogLevel.Trace);
Il livello corrente viene letto da appsettings.json
Demo
Gestione degli errori
La gestione degli errori avviene in Startup.cs/Configure
Grazie agli Environment, differenziamo il comportamento in base
all’ambiente
Negli altri casi, viene utilizzato il Controller specificato, con una view che
mostra l’errore

Di default non viene differenziato in base al codice HTTP di errore, ma


possiamo utilizzare questo codice per farlo:
app.UseStatusCodePagesWithRedirects("~/errors/{0}");
Demo
ASP.NET Core = ASP.NET MVC
ASP.NET Core include MVC e WebAPI in un solo motore
Di fatto, ASP.NET Core, al netto del runtime, è ASP.NET Core MVC

MVC = Model View Controller


• Pattern per disegnare la UI

Rispetto ad ASP.NET MVC, ASP.NET Core MVC ha diverse novità


Come funziona MVC
http://myserver.com/Products/ Web
Browser Server
http://myserver.com/Products/Edit/5

List.cshtml
(View)
ProductsController
(Controller)
Edit.cshtml
(View)
Product
(Model) SQL
ASP.NET MVC vs WebForms
Sono modelli paralleli
• WebForm per facilità
• MVC per controllo totale

MVC è per la testabilità


• TDD (Test Driven Development)
• Certezza che l’interfaccia non produca errori

MVC è il futuro
• ASP.NET Core 1 = .NET Core + ASP.NET Core MVC + WebAPI
• ASP.NET 4.6 = .NET Framework 4.6 + ASP.NET MVC 5/Core MVC + WebAPI
ASP.NET MVC
MVC non è il 3 tier
• È specifico dell’interfaccia
3 tier è un concetto architetturale
MVC è un concetto di design del software

Model
• Oggetti di business
View
• Presentazione
Controller
• Logica
Parliamo di pattern
MVC sta per Model View Controller

Ma in realtà ASP.NET MVC, come JSP/Struts in Java è basato su Model 2


• https://en.wikipedia.org/wiki/JSP_model_2_architecture
MVC in pillole
Il Model ha il compito di contenere i
dati da visualizzare ed i metodi che ne
permettono l'accesso al nostro engine
di persistenza dati

La View ha il compito di visualizzare i


dati da mostrare nella nostra User
Interface

Il Controller è il vero cuore, si occupa


delle iterazioni con l'utente invocando i
metodi presenti nel Model e
cambiando l'output della nostra
interfaccia tramite la View
Obiettivi di MVC
Separation of Concerns
• Testing
• Red/Green TDD
• Alta manutenibilità (tutto è testabile)

Estendibile e pluggabile con maggior facilità


• Con WebForm spesso il modello di estendibilità porta lavoro aggiuntivo (vedi control
adapter)
MVC in dettaglio
Il browser richiede /Products/
La Route viene gestita
Model Il controller viene attivato
Il metodo del controller è invocato
Il controller fa quello che s’ha da fare
Render della View, passando i ViewData

View Controller
La chiamata di una pagina con MVC
Con ASP.NET normale viene usata la pagina fisica
• A meno che non si usino HttpHandler

Con ASP.NET MVC la chiamata viene indirizzata al Controller

L’URL Routing interno rigira la chiamata

Il Controller deve ereditare da una classe base Controller del namespace


Microsoft.AspNetCore.Mvc.
Demo
La View con MVC
Dopo Controller e Model entra in gioco la View

La View è una pagina con codice HTML e server side con lo stesso nome
dell’action del Controller
• Es List.cshtml, Details.cshtml, etc.

Il dato prelevato è nel Model e passato al Controller tramite il metodo View,


per visualizzare le informazioni a video
Demo
Novità di ASP.NET Core MVC
ASP.NET Core MVC semplifica il concetto di controller
• Una qualsiasi classe che restituisca in un metodo il tipo IActionResult è una Action

I controller possono essere POCO


• Plain Old CLR Objection, cioè classi che non ereditano per forza da Controller

Un controller ha action che restituiscono risposte diverse


• View
• Servizi REST
• Immagini
• Testo
• etc
Come funziona l’interazione
URL:

/Products/Edit/5
ProductsController
(Controller) /Products/Edit/5 [POST]

Product
(Model) SQL
View
Le action solitamente non ritornano semplici stringhe
Normalmente le action restituiscono un’istanza del tipo ActionResult o un
tipo derivato
Le action sono un componente molto importante dei controller
• Possono restituire una view
• Possono restituire un file
• Possono fare il redirect
La view per convenzione deve stare in
• Views/<ControllerName>/<ViewName>.cshtml
• Views/Shared/<ViewName>.cshtml

Possiamo comunque indicare la view che vogliamo


I controlli?
Andati. Non ci sono, non sarebbe “giusto” per il modello

La classe HtmlHelper ci aiuta


• @Html.TextBox(…)

Partial View con Html.Partial


• Ci ricordano gli User Control

ASP.NET Core introduce i tag Helper


Passare dati dal controller alla View
Il Controller può passare dati in modo semplicissimo alla View
Nella Action, è sufficiente istanziare la classe ViewResult, passando al
costruttore i dati del modello
public ActionResult BookView()
{
return View(new BookViewModel
{
Title = "ASP.NET Core Guida completa",
Author = "Daniele Bochicchio",
Category = "ASP.NET",
Price = 19.99,
ReleaseDate = DateTime.Today.AddDays(-40)
});
}
Definizione del Model
Il model è una classe che incapsula i dati ad uso e consumo della View
Non ci sono convenzioni sui nomi

public class BookViewModel


{
public string Title { get; set; }
public string Category { get; set; }
public string Author { get; set; }
public double Price { get; set; }
public bool OnSale { get; set; }
public DateTime ReleaseDate { get; set; }
}
Recupero del modello nella View
La view può recuperare il modello
@model BookViewModel

<h1>@Model.Title</h1>
<p>Prezzo: @Model.Price.ToString()</p>

Se non viene specificato il modello, la View è dinamica e funzionerà


comunque (perdiamo l’IntelliSense in Visual Studio)
Passaggio di parametri
Possiamo utilizzare ViewData e ViewBag
ViewData["Message"] = "messaggio";
ViewBag.Message = "messaggio";

ViewBag prevede un accesso dinamico al contento del ViewData, quindi


non richiede casting

Sostanzialmente sono la stessa cosa


Demo
View e Razor
Razor è il View engine di default per ASP.NET
La sua sintassi è basata su C#

@
La "chiocciolina" magica
Istruzione su singola riga

@{ var theMonth = DateTime.Now.Month; }


<p>Valore numerico del mese corrente: @theMonth</p>
Istruzione multi riga
@{
var outsideTemp = 35;
var weatherMessage = "Ciao, ci sono " + outsideTemp + " gradi.";
}

<p>Previsioni di oggi: @weatherMessage</p>

Ogni riga di codice, termina come in C# con il ;


È possibile dichiarare le variabili, utilizzando var
Uso di \ in una stringa
@{ var myFilePath = @"C:\MyFolder\"; }

<p>Path: @myFilePath</p>

Le stringhe devono essere racchiuse tra virgolette

In caso la stringa rappresenti un percorso dobbiamo utilizzare l’operatore @,


esattamente come in C#
Virgolette nelle stringhe
@{ var myQuote = @"Il mio libro preferito è: ""ASP.NET Core"""; }

<p>@myQuote</p>

Se le stringhe contengono le virgolette è sufficiente raddoppiarle


Sintassi e tip
@if (IsPost) {
<p>Ciao, oggi è @DateTime.Now e questo è un post!</p>
}
else {
<p>Ciao <em>straniero</em>, oggi è: <br /> </p> @DateTime.Now
}

Razor permette di innestare codice client con codice server


Sintassi e tip
@if (IsPost) {
@: Oggi è: <br /> @DateTime.Now
<br />
@DateTime.Now @:is the <em>current</em> time.
}

È possibile inserire testo semplice in linea utilizzando @

È possibile anche inserire più righe, semplicemente precedendo ogni riga


con @
Sintassi e tip
@* Commento singolo. *@

@* Commento multi riga.


Generalmente continuerà su altre righe ancora. *@

@{
@* Questo è un commento in linea. *@
var theVar = 17;
}

È possibile aggiungere commenti sia nell’HTML che nel codice

In alternativa in un blocco di codice è possibile utilizzare la sintassi di C#


Da VS -> CTRL+K, CTRL+C – CTRL+K, CTRL+U
Operatori e loop

If/else/else if switch

for foreach

while

Necessitano sempre di un blocco di { } in cui racchiudere il codice


Partial View
View parziale (può essere tipizzata) e condivisa

Le view parziali devono essere salvante dentro la dir Shared o in quella


delle View
• Sarà una partial view globale o solo di un gruppo di view

Consente di evitare la duplicazione di codice

Per convenzione si preferisce utilizzare il carattere underscore come


prefisso di tutte le partial view

@Html.Partial("_Partial", Model)
PartialView async
ASP.NET Core introduce il supporto per le partial async
Utile per migliorare la scalabilità, qualora ci sia codice async nella view

@await Html.PartialAsync("_List")
Sfruttare le layout view nel progetto
Consente di centralizzare un layout comune a tutte le view

Viene chiamata (per convenzione) è _Layout.cshtml

Non è una view tipizzata, perché sarà utilizzata in un gran numero di


situazioni e vogliamo lasciare alle singole action la flessibilità di utilizzare il
model più consono alla view che dovrà essere mostrata
Sfruttare le layout view nel progetto

<!DOCTYPE html>
<html>
<head>... </head>
<body> Nella pagina di layout @RenderBody
<div id="body"> determina dove il contenuto deve
@RenderBody() essere renderizzato
</div>
</body>
</html>
Sfruttare le layout view nel progetto
Nella pagina inseriamo il contenuto e specifichiamo la pagina di layout
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

@model MyBookShop.Models.BookViewModel
@{ ViewBag.Title = Model.Title; }

<h2>Libro</h2>
<p>
@Model.Title
</p>
<p>
@Model.Author
</p>
<p>
@Model.Category
</p>
Sfruttare le layout view nel progetto
Impostare ogni volta la pagina di layout può essere tedioso e ripetitivo

Limita la manutenzione

All’interno di Views\_ViewStart.cshtml imposta globalmente la Layout View


per tutte le pagine

Basta sovrascriverla localmente nella View per non utilizzarla


Environment
Possiamo diversificare il markup in base all’ambiente

<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet"
href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
Importazione globale per View
Possiamo registrare funzionalità a livello globale nel file
Shared\_ViewImports.cshtml

@using WebApplication1
@using WebApplication1.Model
@using Microsot.AspNetCore.Identity
@using Microsoft.AspNetCore.Mvc.Razor
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Il file _ViewStart
Possiamo esegurie codice per ogni View aggiungendolo all’interno del
Shared\_ViewStart.cshtml
Generalmente viene sfruttato per registrare globalmente una layout page
Definire sezioni aggiuntive in una
layout view
Possiamo definire nuove sezioni
• anche opzionali

_Layout.cshtml _view.cshtml

<footer> @section Footer {


@RenderSection("Footer") <p>Footer della content view</p>
@RenderSection("OptionalSection", false) }
</footer>
Verifica dell’esistenza di una sezione

<div id="footer">
@if (IsSectionDefined("Footer"))
{
@RenderSection("Footer")
}
È possibile verificare
else l’esistenza della sezione e,
{
<p>Contenuto di default definito su layout view</p>
in alternativa, mostrare del
} contenuto standard
@RenderSection("OptionalSection", required: false)
</div>
Demo
Progetti complessi: le Aree
In ASP.NET MVC la struttura di directory del progetto non rispecchia, a
differenza di quanto accade in WebForm, l'effettivo path delle pagine, che
invece è determinato esclusivamente dalle regole di routing.

Al contrario, esse hanno esclusivamente la funzione di contenitori, secondo


una struttura ben definita che stabilisce dove posizionare i vari file di model,
controller e view.

SCENARIO: Pensiamo per esempio al caso di un CMS, in cui magari abbiamo sia una parte pubblica,
visibile a tutti, che una sezione di backoffice amministrativo, magari con differenti layout, pattern di
routing e regole di accesso
Aggiungere un’area
Creare una directory Areas sotto la root
• All’interno aggiungere le directory Controllers, Views

In particolare, ASP.NET Core andrà a cercare le view così:


• /Areas/<Area>/Views/<Controller>/<Action>.cshtml
• /Areas/<Area>/Views/Shared/<Action>.cshtml
• /Views/Shared/<Action>.cshtml
Registrare un’area
Possiamo customizzare la registrazione in questo modo

services.Configure<RazorViewEngineOptions>(options =>
{
options.AreaViewLocationFormats.Clear();
options.AreaViewLocationFormats.Add("/Backoffice/{2}/Views/{1}/{0}.cshtml");
options.AreaViewLocationFormats.Add("/Backoffice/{2}/Views/Shared/{0}.cshtml");
options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
});
Indicare l’area
A differenza di ASP.NET MVC, in ASP.NET Core l’area viene indicata
dall’attributo Area

[Area("Backoffice")]
public class HomeController : Controller
{

}
Pubblicazione delle aree
Di deafult le aree non vengono pubblicate
Occorre modificare il file project.json, aggiunendo il riferimento

"publishOptions": {
"include": [
"Areas/**/*.cshtml",
....
....
]
Demo
Injection dei servizi
Possiamo fare injection in maniera semplice, registrando l’injection e poi
facendo riferimento nel controller

public void ConfigureServices(IServiceCollection services)


{
services.AddTransient<IMyService, MyService>();
}

public IActionResult About([FromServices] IMyService service)


{
ViewData["Message"] = service.Message();
return View();
}
Injection dei servizi
In alternativa è sempre possibile iniettare la dipendenza dal costruttore del
controller

ASP.NET Core supporta questi lifetime


• Transient
• Scoped
• Singleton
Injection dei servizi nelle view
Si può iniettare un servizio anche nelle View, usando @inject

@inject IMyService myService

@myService.Message()
Demo
View Components
Novità di ASP.NET MVC: sembrano partial view, ma sono più potenti

Non usano il model binding, ma usa il modello passato

Sono indicati quando oltre alla logica di rendering, c’è anche logica
applicativa

Concettualmente sono molto simili ai Custom Control


Creare un View Component
Bisogna creare una classe che eredita da ViewComponent e finisce con il
suffisso ViewComponent

Decorare la classe con [ViewComponent]

Come i controller, un View Component deve essere pubblico, non astratto e


il suo nome è quello indicato al netto del suffisso obbligatorio

Definisce la logica nel suo metodo InvoceAsync


Creare un View Component
Le View saranno cercate in
• Views/<controller>/Components/<viewcomponent>/<name>.cshtml
• Views/Shared/Components/<viewcomponent>/<name>.cshtml
Il nome di default è Default.

Possiamo ritornare direttamente un ViewComponent da una Action, al posto


di una View

Oppure invocarlo da codice


@await Component.InvokeAsync("Menu", new { ShowAll = true })
Demo
Tag Helper: che cosa sono
I tag helper permettono a codice server-side di generare codice HTML
Possono essere invocati in base al nome dell’elemento o dell’attributo
I tag helper sono come gli html helper, ma riducono l’utilizzo di sintassi C#
all’interno della pagina
Codice più leggibile e mantenibile
Supporto all’IntelliSense di Visual Studio

<label asp-for="Email"></label>
<label for="Email">Email</label>
Aggiungere un Tag Helper
Si possono aggiungere i tag helper sfruttando la direttiva @addTagHelper,
di solito all’interno del file _ViewImports

Allo stesso modo è possibile rimuovere uno specifico tag helper all’interno di
una pagina specificando la direttiva @removeTagHandler

@addTagHelper "*, TagHelperNamespace"


@removeTagHelper "MyTagHelper, TagHelperNamespace"
Disabilitare un tag helper
E possibile specificare l’utilizzo dei tag helper su tutti i controlli tranne che su
un attributo specifico
E’ sufficiente aggiungere all’apertura del tag HTML il carattere «!»

<!span asp-validation-for="Email"
class="text-danger"></!span>
Gestire le form
Eliminare l’utilizzo degli HTML helper semplifica il codice generato,
soprattutto all’interno delle form di inserimento dati
Grazie agli helper per controller ed action, si può semplificare la navigazione
durante il metodo POST

<form asp-controller="Demo"
asp-action="Register"
asp-route="CustomRoute"
method="post">
<!-- Input and Submit elements -->
</form>
Gestire le form
Se lato controller nella action in HTTP POST è impostato l’attributo
[ValidateAntiForgeryToken], allora verrà aggiunto in automatico nella form
un campo hidden con il Request Validation Token per prevenire attacchi
cross-site

<input name="__RequestVerificationToken"
type="hidden"
value="XXX" />
Attributo di input
Imposta l’attributo type in base alla proprietà nel modello e alla
DataAnnotation associata (tranne se è esplicitamente impostato)
Genera la validazione secondo gli standard HTML5 in base al modello con
gli attributi data-val-*
E’ l’equivalente di Html.EditorFor/TextBoxFor

<input asp-for="<Expression Name>" />


La validazione: validation message
Il tag helper di input aggiunge gli attributi di validazione client-side in base
alle DataAnnotation trovate sul modello. La validazione avviene anche lato
server nel caso che il javascript sia disattivato nel browser
E’ il corrispondente di Html.ValidationMessageFor

<span asp-validation-for="Email"></span>

<span class="field-validation-valid"
data-valmsg-for="Email"
data-valmsg-replace="true"></span>
La validazione: validation summary
Viene utilizzato per mostrare un report dei messaggi di validazione
E’ il corrispondente di Html.ValidationSummary

<div asp-validation-
summary="ValidationSummary.ModelOnly"></div>
Tag helper: select
Genera un tag <select> con le opzioni specificate all’interno della proprietà
Items
E’ l’alternativa di Html.DropDownListFor

<select asp-for="Country"
asp-items="Model.Countries"></select>
Tag Helper custom
Un TagHelper è una classe che implementa dall’interfaccia ITagHelper, ma
per comodità si eredita direttamente da TagHelper per avere accesso al
metodo Process

Per convenzione è meglio utilizzare il nome del tag seguito da TagHelper,


ma non è obbligatorio.

public class EmailTagHelper : TagHelper


{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "a"; // Sostituisce il tag <email> con <a>
}
}
Tag Helper custom
Context contiene le informazioni associate all’esecuzione del tag
corrispondente

Output contiene invece le informazioni relative all’elemento HTML


corrispondente al tag sorgente

public class EmailTagHelper : TagHelper


{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "a"; // Sostituisce il tag <email> con <a>
}
}
Tag Helper custom
Per rendere il tag helper custom raggiungibile ed utilizzabile, è necessario
registrarlo con la direttiva @addTagHelper (come wildcard, oppure tramite
nome esplicito)

@addTagHelper "*, TagHelperCustomNamespace"


Tag Helper custom
E’ possibile aggiungere attributi personalizzati all’elemento HTML
renderizzato
public class EmailTagHelper : TagHelper
{
public string MailTo { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)


{
output.TagName = "a"; // Replaces <email> with <a> tag

var address = MailTo + "@" + EmailDomain;


output.Attributes.SetAttribute("href", "mailto:" + address);
output.Content.SetContent(address);
}
}
Tag Helper custom
Se le proprietà vengono create con sintassi Pascal-case, allora gli attributi
vengono analizzati con sintassi lower-kebab case

<email mail-to="" />

E’ anche possibile accettare proprietà complesse, in questo caso si deve


passare l’intero oggetto così come verrà interpretato da C#
Tag Helper custom
E’ possibile decorare i tag helper custom con degli attributi per ridefinire il
comportamento

[HtmlTargetElement(TagStructure = TagStructure.WithoutEndTag)]

L’ordine di valutazione di questi attributi può alterare il risultato.


Due proprietà separate da virgola all’interno dello stesso attributo vengono
valutate come AND, se fanno parte di attributi differenti sono valutate come
OR.
Gestione degli ambienti
ASP.NET Core utilizza una particolare variabile d’ambiente che serve a
descrivere l’ambiente in uso.

ASPNETCORE_ENVIRONMENT
Development, Staging, Production, Custom
Gestione degli ambienti
L’accesso via HTML agli ambienti è consentito attraverso l’uso del tag
helper di environment

<environment names="Development">
<link rel="stylesheet" href="~/dev/bootstrap.css" />
<link rel="stylesheet" href="~/dev/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="~/prod/bootstrap.css" />
<link rel="stylesheet" href="~/prod/site.css" />
</environment>
Razor Pages
Semplificazione del modello di View
• La pagina ha sia logica che codice
Il contenuto è all’interno della directory Pages
Comode per scenari semplificati
Demo
Gli URL con MVC
Classe ProductsController

I metodi diventano Action


• Metodo Categories()
• http://www.miosito/Products/Categories
• Metodo List(string category)
• http://www.miosito/Products/List/ASP.NET
• Metogo Details(int id)
• http://www.miosito.it/Products/Details/15
Da ASP.NET MVC…
La magia avviene nella classe RouteConfig, richiamata allo startup dal
GlobaAsax.cs

Viene sfruttato il ModelBinding per associare i parametri della route

public static void RegisterRoutes(RouteCollection routes)


{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
…ad ASP.NET Core
La magia avviene nella classe Startup all’interno del metodo Configure
Il metodo UseMvc crea una nuova istanza di RouteMiddleware e la
aggiunge alla pipeline di esecuzione

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)


{
app.UseMvc(routes =>
{
routes.MapRoute(name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});

// equivalente
app.UseMvcWithDefaultRoute();
}
Personalizzazione
È possibile personalizzare il comportamento di routing semplicemente
aggiungendo constraint sulle proprietà o eventuali token per gestire, ad
esempio, la localizzazione

routes.MapRoute(
name: "us_english_products",
template: "en-US/Products/{id}",
defaults: new { area = "Backoffice", controller = "Products",
action = "Details" },
constraints: new { id = new IntRouteConstraint() },
dataTokens: new { locale = "en-US" });
Constraint per route
I parametri inclusi tra parentesi graffe devono fare match altrimenti la route
viene ignorata
Il carattere «?» rappresenta un valore opzionale
Il carattere «.» per le estensioni dei file è comunque un valore opzionale
Le constraint sono classi che implementano IRouteConstraint e possono
forzare il tipo di un parametro
namespace Microsoft.AspNetCore.Routing.Constraints
{
public class IntRouteConstraint : IRouteConstraint
{id:int} {
{active:bool} public IntRouteConstraint();
}
{name:lenght(5,20)} }
Attribute routing
Il routing può essere definito anche come attributo nella decorazione del
metodo corrispondente

In questo caso non viene calcolato il nome del controller perché verrà
mappato sempre su Home

[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult MyIndex()
{
return View("Index");
}
Gestione dei conflitti
Può succedere che ci siano due o più metodi corrispondenti alla stessa
route: in questo caso viene generato un conflitto

In caso di conflitti, se MVC non è in grado di trovare una «best» route, verrà
generata una AmbiguousActionException

Per evitarli, è bene:


• Specificare dettagliatamente il routing tramite attribute routing
• Impostare HTTP Verb specifici come decorazione dei metodi
• Considerare una route di fallback e di default per gestire un eventuale redirect
Ottenere l’url
E’ possibile recuperare l’url da una action attraverso la classe Url che
analizzerà la route corrispondente
Viene utilizzato anche con i tag helper per la gestione delle form

public IActionResult Source()


{
// Genera /custom/url/to/destination
var url = Url.Action(nameof(Destination));
return Content($"Url generato: {url}");
}

[HttpGet("custom/url/to/destination")]
public IActionResult Destination()
{
return View();
}
Demo
Entity Framework
• ORM di Microsoft basato sul .NET Framework
• Insieme di tecnologie ADO.NET per lo sviluppo software
• Definisce un modello di astrazione dei dati
• Traduce il nostro codice in query comprensibili dal DBMS
• Disaccoppiamento tra applicazione e dati
• Posso mantenere la stessa rappresentazione anche se cambia il modello fisico (es.
da SQL Server ad Oracle)
• Open source
• https://github.com/aspnet/EntityFramework
Cos’è un ORM?
• È una tecnica per convertire dati da type system incompatibili
• Da database ad object-oriented

• 3 caratteristiche fondamentali
• Mapping
• Definisce come il database si «incastra» negli oggetti e viceversa
• Fetching
• Sa come recuperare i dati dal database e materializzare i rispettivi oggetti
• Persistenza del grafo
• Sa come salvare le modifiche agli oggetti, generando le query SQL corrispondenti
Entity Framework: cos’è?
Entity Client Data Provider
• Livello di astrazione, che rende utilizzabile EF con più sorgenti dati

Entity Data Model


• Rappresenta il modello di mapping tra database ed oggetti

LINQ to Entities
• Flavour di LINQ che consente di utilizzare tutti gli operatori in unione con le entity di EF

Entity SQL
• Linguaggio speciale per interrogare EF con un linguaggio indipendente dal database
utilizzato, consentendo di creare facilmente query dinamiche
Come funziona
Diversi approcci
Database-First
• Il modello viene importato da un DB esistente
• Se modifico il database posso (quasi) sempre aggiornare il modello
Model-First
• Il modello del database viene creato dal designer di Visual Studio
• L’implementazione fisica è basata sul modello generato
• Non favorisce il riutilizzo del codice né la separazione tra contesto ed entità
• Poichè il modello definisce il DB, eventuali sue modifiche verranno perse
Code-First
• Il modello viene creato dal nostro codice
• L’implementazione fisica è basata sul nostro codice
Perché Code-First?
L’EDMX può essere complicato

Non siamo DBA


• Non ci dobbiamo occupare di mapping
• Non dobbiamo fare ottimizzazioni sul database

Siamo dev
• Focus sul domain design
• C# ci è più familiare delle query di SQL

Gli altri approcci non sono supportati da EF Core


Stato di Entity Framework

Runtime su NuGet
Tooling su Microsoft Download Center
Ultima versione inclusa in Visual Studio

Nuovi componenti su NuGet


Componenti Core runtime dentro .NET
Tooling in Visual Studio

Runtime nel .NET Framework


Tooling in Visual Studio
Il DbContext
Il DbSet
• E’ una classe che rappresenta le entity
• Serve per fare operazioni CRUD
• E’ definito come DbSet<TEntity>
• I metodi più utilizzati sono:
• Add, Remove, Find, SqlQuery
• Vengono mappate tutte le sue proprietà che hanno getter e setter
impostati
Il DbContext
public class Context : DbContext
{
public DbSet<Student> Students { get; set; }
public Context() : base("name=MyDBConnnectionString")
{
}
}

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<connectionStrings>
<add name="MyDBConnectionString" connectionString="data source=\SQLEXPRESS;in
itial catalog=dbName;integrated security=True;MultipleActiveResultSets=True;App=En
tityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Primary Key
public class Student public class Student
{ {
public int StudentID { get; set; } public int MyPrimaryKey { get; set; }

public string StudentName { get; set; } public string StudentName { get; set; }
public DateTime DateOfBirth { get; set; } public DateTime DateOfBirth { get; set; }
public Teacher Teacher { get; set; } public Teacher Teacher { get; set; }
} }

Convenzione sul nome della chiave Non usa la convenzione di code-first


primaria: Genera una ModelValidation
• Id Exception se non gestita con le
• <NomeClasse>ID DataAnnotations
Foreign Key
public class Student public class Course
{ {
public int StudentID { get; set; } public int CourseId { get; set; }

public string StudentName { get; set; } public string CourseName { get; set; }
public DateTime DateOfBirth { get; set; } public Teacher Teacher { get; set; }
public IList<Student> Students { get; set; }
public int CourseId { get; set; } }
public Course Course { get; set; }
}

La ForeignKey viene generata automaticamente da code-first ogni volta che


viene individuata una navigation property
Sempre bene rispettare le stesse convenzioni della PrimaryKey
DataAnnotations e Fluent API
Le DataAnnotations sono attributi che servono a specificare il
comportamento per fare l’override delle convenzioni di code-first
Possono influenzare le singole proprietà
• Namespace System.ComponentModel.DataAnnotations
• Key, Required, MaxLenght…
Possono influenzare lo schema del database
• Namespace System.ComponentModel.DataAnnotations.Schema
• Table, Column, NotMapped…
Le DataAnnotations sono limitate. Per il set completo bisogna andare di
Fluent API
DataAnnotations: Key
Override della convenzione sulla PrimaryKey
Viene applicato alle proprietà di una classe

[Key]
public int MyPrimaryKey { get; set; }
Model e Entity Mapping
Configurazione dello schema per tutto il database
Configurazione dello schema per singola tabella

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
modelBuilder.HasDefaultSchema("Admin");

//Map entity to table


modelBuilder.Entity<Student>().ToTable("StudentInfo");
modelBuilder.Entity<Student>().ToTable("StandardInfo", "anotherSchema");
}
Property Mapping
Configurazione della chiave primaria
modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentId);

Configurazione di altre proprietà


modelBuilder.Entity<Student>().Property(p => p.Age)
.HasColumnName("Eta")
.HasColumnOrder(3)
.HasColumnType("datetime2")
.IsRequired();
Metodo Seed
E’ integrato con il processo di inizializzazione del database
Serve per fornire dei dati di testing in fase di sviluppo

public class MyInitializer : CreateDatabaseIfNotExists<Context>


{
protected override void Seed(Context context)
{
var fakeStudents = new List<Student>();
fakeStudents.Add(new Student() { Name = "Studente 1" });
fakeStudents.Add(new Student() { Name = "Studente 2" });
fakeStudents.Add(new Student() { Name = "Studente 3" });

context.Students.AddRange(fakeStudents);
context.SaveChanges();
base.Seed(context);
}
}
Ereditarietà
Table per Hierarchy (TPH)
• Una classe per tutta la struttura di ereditarietà
• La tabella generata contiene un discriminante per distinguere gli elementi
• Strategia di default di Entity Framework
Table per Type (TPT)
• Una tabella per ogni classe della ereditarietà
Table per Class (TPC)
• Una tabella per ogni classe dell’ereditarietà ad esclusione del tipo astratto
• Le proprietà della classe astratta andranno dentro quelle concrete
Struttura di partenza
Table per hierarchy 1/2
Implementazione del modello
public abstract class BillingDetail public class CreditCard : BillingDetail
{ {
public int BillingDetailId { get; set; } public int CardType { get; set; }
public string Owner { get; set; } public string ExpiryMonth { get; set; }
public string Number { get; set; } public string ExpiryYear { get; set; }
} }

public class BankAccount : BillingDetail


{
public string BankName { get; set; }
public string Swift { get; set; }
}

public class Context : DbContext


{
public DbSet<BillingDetail> BillingDetails { get; set; }
}
Table per hierarchy 2/2
Implementazione fisica

I dati si recuperano con una query normalissima


ctx.BillingDetails.OfType<CreditCard>().ToList();
Demo
Entity Framework
Configurazione per ASP.NET Core
EF6.x non è compilato per .NET Core
Possiamo fare il deployment solo su Windows e non abbiamo tutti i vantaggi offerti
da EF Core
Ci obbliga a rimanere su Windows e non abbiamo tutti i vantaggi offerti da EFCore
EF Core è una versione riscritta di EF per .NET Core
• Supporto a nuove piattaforme e database
• Batching durante SaveChanges
• Client eval per query LINQ
• Shadow state property
• SQL Server sequences
• Alternate keys
Nuove funzionalità
Supporto a nuove piattaforme
Supporto a nuovi database

Batching durante SaveChanges


Client eval per query LINQ
Shadow state property
SQL Server sequences
Alternate keys
.NET FRAMEWORK .NET CORE XAMARIN

WPF Windows Forms UWP iOS


MODELS
APP

Android
ASP.NET ASP.NET Core OS X
LIBRARIES
BASE

Base Class Library Core Library Mono Class Library

COMMON INFRASTRUCTURE

Compilers Languages Runtime components

EF6.x EF Core
Nuove tipologie di database
Relazionali e non-relazionali

Alcuni provider

Solo relazionali per la v1.0.0


Core leggero ed estendibile
Stesse core API che già usiamo…

Costruito sopra una serie di servizi

Ottimizzato per memory e CPU usage

Componenti pay-per-play
EF Core
EF6 non era così male…

…ma c’è bisogno di cambiare


EF Core & EF6.x
EF6.x è stabile…

EF Core è una v2…


EF Core & EF6.x
EF6.x può essere (ancora) la scelta giusta

Valutare i requisiti per EF Core

Da EF6.x a EF Core? “Port” != “upgrade”


Configurazione di EF Core
Eliminata la connection string all’interno del costruttore!
Generalmente si iniettano direttamente i settings

public class BloggingContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }

// Setup the database to connect to


protected override void OnConfiguring(DbContextOptionsBuilder options)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(@"myConnectionString");
}
}
Mapping
Riscritta completamente la parte relativa ai provider
Lunga vita alla Dependency Injection!

var serviceProvider = db.Database.GetService<IServiceProvider>();


var typeMapper = serviceProvider.GetRequiredService<IRelationalTypeMapper>();
Migration
Sono ancora validi i «vecchi» comandi di EF6.x

Per le PCL con .NET Core sono stati aggiunti nuovi comandi:
dotnet ef migrations add {MigrationName}
dotnet ef migrations remove
dotnet ef database update

Lato applicativo si può controllare la migrazione con Migrate


db.Database.Migrate();
Dall’EDMX al futuro
L’EDMX non è supportato da EFCore, è richiesto un nuovo modello

Sono necessari questi pacchetti di NuGet:


Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.{Provider}.Design

Si genera il nuovo modello tramite scaffolding:


Scaffold-DbContext "<connection string>" <database provider name>
Demo
Entity Framework Core
EF Core 1.1
Rilasciato con .NET Core 1.1

Novità:
• Linq Translator migliorato
• DbSet.Find
• Supporto a tabelle memory-optimized in SQL Server
• Explicit Loading
• ChangeTracker API da EF6.x
• Connection resiliency
• Service replacement semplificato
• Oltre 1000 bug fix
Interceptor

public override int SaveChanges(bool acceptAllChangesOnSuccess)


{
ChangeTracker.DetectChanges();

foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added))


{
//modify entry.Entity here
}

ChangeTracker.AutoDetectChangesEnabled = false;
var result = base.SaveChanges(acceptAllChangesOnSuccess);
ChangeTracker.AutoDetectChangesEnabled = true;
return result;
}
Custom conventions
Non c’è più l’interfaccia IStoreModelConvention di EF6

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
foreach (var property in entityType.GetProperties())
{
var columnName = property.SqlServer().ColumnName;

if (columnName.Length > 30)


throw new InvalidOperationException($"La colonna {columnName} non può essere più
lunga di 30 caratteri");
}
}
Seeding
Non esiste più il seeding di EF6, bisogna farlo tramite interceptor allo startup

using (var scope = Database.GetService<IServiceScopeFactory>().CreateScope())


{
var ctx = scope.ServiceProvider.GetService<Context>();
ctx.Database.Migrate();
ctx.EnsureSeedData();
}

public static void EnsureSeedData(this Context context)


{
context.People.Add(new Person("Matteo", "Tumiati", 25));
}
EF Core 2
Modello
Supporto ai complex types
Simple type conversions
Spatial data types
Relazioni molti-a-molti con TPT e TPC

Query
Miglioramento di LINQ Translator (GroupBy sul DB), Lazy Loading

Schema
Aggiornamento del modello dal DB

Integrazione con Visual Studio


Wizard per l’importazione dal database
EF Core 2
Provider
Supporto a Redis e a database non relazionali

Operazioni CRUD
Seed dei dati
Simple command interception

Operazioni specifiche sui relazionali


Supporto alle stored procedure (senza FromSql)

.NET Standard 2
Supporto anche per Xamarin
Filters
Consentono di aggiungere comportamenti a controller
ed action

Ad esempio:
• [Authorize]
• [HttpPost]

Globali (Startup.cs)
Locali (su controller o action)
L'infrastruttura dei filtri
Simili agli HttpModule di ASP.NET
Sono pensati per ASP.NET Core

Dal punto di vista strettamente pratico, si tratta di classi che implementano una
serie di interfacce, ognuna specifica per una particolare funzionalità.
Tipologie di filtri
Action filter: implementano l'interfaccia IActionFilter e ci consentono di
gestire le fasi immediatamente precedenti e successive all'esecuzione della
action
Result filter: simili ai precedenti, implementano l'interfaccia IResponseFilter
e ci danno la possibilità di iniettare codice nelle fasi immediatamente
precedenti e successive all'esecuzione del result di una action
Exception filter: implementano IExceptionFilter e contengono codice che
gestisce situazioni di errore, permettendo di intercettare un'eccezione non
gestita sul server
Authorization filter: implementano IAuthorizationFilter e ci permettono di
gestire le policy secondo cui consentire l'esecuzione di una determinata
action
Ordine d’esecuzione dei filtri
Controller: OnActionExecuting
Global: OnActionExecuting
Class: OnActionExecuting
Method: OnActionExecuting
Method: OnActionExecuted
Class: OnActionExecuted
Global: OnActionExecuted
Controller: OnActionExecuted
Impostare un filtro
A livello di classe o metodo, mettere Order < 0

[MyFilter(Order=-1)]
public IActionResult Index()
{
return View();
}
Costruire un custom filter
Classe che eredita da FilterAttribute

OnAuthorization
• prima dell'esecuzione della action per verificare l’accesso
OnActionExecuting
• subito prima dell'esecuzione del codice della action
OnActionExecuted
• appena dopo l'esecuzione della action
OnResultExecuting
• subito prima dell'esecuzione del risultato ritornato dalla action
OnResultExecuted
• appena dopo il termine dell'elaborazione del risultato
OnException
• nel caso in cui il flusso di gestione della richiesta abbia sollevato un'eccezione
Registazione globale dei filtri
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc(option =>
{
// istanza per ogni richiesta
option.Filters.Add(new MyFilterAttribute());
// ogni richiesta crea una nuova istanza
option.Filters.Add(typeof(MyFilterAttribute ()));
});
}
}
Registazione locale dei filtri
// verrà utilizzata la DI
// dobbiamo ricordarci di registrare MyFilterAttribute
[ServiceFilter(typeof(MyFilterAttribute))]
public IActionResult Index()
{
return View();
}

// non necessita di registrazione via DI


[TypeFilter(typeof(MyFilterAttribute),
Arguments = new object[] { "Param1", "Value" })]
public IActionResult Index()
{
return View();
}
Utilizzo dei filtri su Controller/Action
I filtri possono essere applicati più volte a:
• Controller
• Action

[HandleError(ExceptionType = typeof(DbException), View = "DatabaseError")]


[HandleError(ExceptionType = typeof(AppException), View = "ApplicationError")]
public class ProductController
{
}
Filtri custom

public class MyActionFilter : ActionFilterAttribute


{
}

I filtri estendono una delle classi base a seconda del tipo di filtro da implementare
È possibile controllare l’ordine d’esecuzione dei filtri appartenente al solito tipo
impostando la proprietà Order
Esempi di filtri
Action Filter
• Per gestire il ciclo di vita della action/view
Resource Filter
• Per gestire un output sotto forma di risorsa
Exception Filter
• Per gestire un errore custom (es: risposta in JSON per WebAPI,
anziché pagina di errori in HTML)
Result Filter
• Per gestire direttamente il risultato
Demo
Filtri o Middleware
Sono simil in scope
Filtri = MVC
MiddleWare = ASP.NET Core
I filtri hanno il vantaggio di stare più vicino ad MVC, quindi
accedo alle sue caratteristiche
• Es: model validation
• Es: ViewData/ViewBag
Middleware evoluzione di HttpModule
HttpModule: prima di ASP.NET Core

HttpModule: moduli riutilizzabili inseriti nella pipeline e vengono attivati da


eventi definiti all’interno di HttpApplication

Middleware: unione di HttpModule e HttpHandler. Eseguono operazioni


intermedie durante l’elaborazione delle richieste.

Middleware: possono produrre un risultato, in questo caso sono l’ultima


operazione della pipeline
Prima: HttpHandler e HttpModule
• HttpModule implementano l’interfaccia IHttpModule
• Necessitano la registrazione nel file web.config
• HttpHandler implementano l’interfaccia IHttpHanderler
• HttpHandler: processi che vengono eseguiti in risposta ad una richiesta

<configuration>
<system.webserver>
<modules>
<add name="HelloWorldModule" type="HelloWorldModule"/>
</modules>
</system.webserver>
</configuration>
Dopo: Middleware
E’ un componente software inserito in una pipeline che consente di gestire
le richieste e le risposte

Ogni componente esegue le operazioni e le invia come richieste al


componente successivo

Ogni componente può eseguire operazioni prima e dopo il prossimo


componente della pipeline
Middleware
Le richieste vengono eseguite una di seguito all’altra

Ogni elemento può interrompere l’esecuzione della pipeline (short-circuiting)

Nel template di default (in sequenza)


• Error handling
• Static file server
• Authentication
• MVC
Middleware
In .NET core le richieste vengono gestite implementando IApplicationBuilder
Metodi Run, Map e Use
La configurazione viene passata al metodo Configure della classe Startup (nel
file Startup.cs)
Possibilità di utilizzare chiamate a metodi anomini
Possibilità di utilizzare chiamate a classi intermedie (Middleware)
Middleware
L’ordine in cui vengono aggiunti i middleware è l’ordine in cui hanno effetto
sulle richieste (e al contrario sulle risposte)

Run: interrompe l’esecuzione della pipeline. Dovrebbe essere inserito al


termine della pipeline

Map: consente di mappare le richieste in base ad un particolare path

es. app.Map("/maptest", HandleMapTest);

Use: consente di aggiungere un delegato alla richiesta


Middleware come Filter
ASP.NET Core 1.1 introduce la possibilità di registrare un
Middleware direttamente come se fosse un filtro

[MiddlewareFilter(typeof(CompressionPipeline))]
public IEnumerable<Product> Index()
{
return GetAllProducts();
}
Migrare da HttpModule a Middleware
Prima del Middelware: HttpModule
Handler:
Classi che implementano IHttpHandler
Usati per gestire richieste con un nome file o estensione
Configurati nel file web.config
Migrare da HttpModule a Middleware
Prima del Middelware: HttpModule

Modules
Classi che implementano IHttpModule
Chiamati ad ogni richiesta
Possono interrompere la pipeline
Possono creare una risposta HTTP oppure crearne custom
Configurati nel web.config
L’ordine di esecuzione è gestito da una serie di eventi BeginRequest,
AuthenticationRequest, ecc
L’ordine di esecuzione è configurabile nel web.config
Migrare da HttpModule a Middleware
Middleware

HttpModules, HttpHandler, Global.asax e Web.config sono obosoleti

Il ruolo degli HttpModules e HttpHanlder è unificato

Le pipeline possono inviare le richieste ad uno specifico Middleware,


non solo in base ad un URL specifico, ma anche in base a headers,
querystring ecc.
Migrare da HttpModule a Middleware
Middleware

Sono simili ai moduli


Invocati ad ogni richiesta
Possono interrompere una richiesta, senza passarla al prossimo
middleware
Possibilità di creare risposte HTTP custom
Migrare da HttpModule a Middleware
Middleware

Middleware e Moduli sono processati in ordine differente


L’ordine del middleware sono gestiti in base all’ordine in cui sono inseriti
nella pipeline

L’ordine delle risposte viene gestito in ordine inverso rispetto alle


richieste: l’ordine per gli HttpModules per richieste e risposte è lo stesso
Migrare da HttpModule a Middleware
Demo migrazione – INTEGRARE
• https://docs.asp.net/en/latest/migration/http-modules.html
• https://docs.asp.net/en/latest/migration/http-modules.html#id6
• https://docs.asp.net/en/latest/migration/http-modules.html#migrating-
handler-code-to-middleware
Identità e sicurezza
Authorization e differenze rispetto al passato
ASP.NET Identity
CookieAuthentication
Data Protection
ASP.NET Identity
Nuovo sistema di gestione dell’identity basato sul concetto di claim
• Un claim è una dichiarazione: es "username" = "Daniele"

Basato su provider
• modello estendibile facilmente con provider, Entity Framework già incluso

Supporta provider multipli per il login


• Cookie, social, Active Directory, Office 365, Federation, etc
ASP.NET Identity
Ha già tutta la logica necessaria a gestire two-factor authentication, reset dei
PIN via SMS/mail
• Il template di default di VS si occupa di creare un po’ tutto

Si integra alla perfezione con Controller/View


Demo
Cookie Middleware
Se non vogliamo gestire con gli automatismi di ASP.NET Identity, possiamo
gestire con Cookie Middleware
Saremo responsabili di gestire gli utenti e sfrutteremo un cookie per
assicurarci che il ticket di authentication sia giusto
In startup.cs, prima di UseMvc:

app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new PathString("/Account/Unauthorized/"),
AccessDeniedPath = new PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
Gestire il cookie di autenticazione
È sufficiente, dopo aver controllato il database, fare una chiamata come la
seguente
await HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance",
principal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
IsPersistent = true
});

Per fare logout


await HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");
Autenticazione
Con ASP.NET 1.x l’autenticazione è configurata con Middleware
Con ASP.NET 2 l’autenticazione è configurata con i Services
• Supporto a Cookie, OpenID Connect, JWT Bearer, Facebook, Twitter,
Google, Microsoft Account

Nuove estensioni ad HttpContext anziché utilizzare


IAuthenticationManager

https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-
2x/identity-2x
Demo
Data Protection
Le applicazioni web hanno spesso bisogno di salvare dati in modo sicuro
• DPAPI è per applicazioni desktop
• Azure Right Management è pensato per il cloud

ASP.NET Core ha un intero stack dedicato alla sicurezza out-of-the-box


• Pensato principalmente per rimpiazzare machinekey
• Garantisce un meccanismo di persistenza affidabile
• Performance senza precedenti

E’ pensato per gli sviluppatori


• Configurazione ed API semplici, non richiede conoscenze di algoritmi di cifratura
3 audience differenti
Sviluppatore che deve scrivere l’applicativo web
• L’applicativo deve essere subito pronto e funzionante
• Richiede le Consumer API

Sviluppatore di framework e sistemisti


• Adatto a chi ha alcune configurazioni non-standard
• Richiede le Configuration API

Chiunque abbia custom policy


• Per tutti gli esperti di sicurezza che devono implementare soluzioni totalmente custom
• Richiede le Extensibility API
5 pacchetti di NuGet
Microsoft.AspNetCore.DataProtection.Abstractions
• Perfetto per le Consumer API

Microsoft.AspNetCore.DataProtection
• Espone tutto il core del sistema di protezione di ASP.NET, inclusa la gestione delle
chiavi, operazioni di crittografia e configurazioni.

Microsoft.AspNetCore.DataProtection.Extensions
• Adatto per chi ha bisogno delle Extensibility API
• Niente dependency injection
• Include la possibilità di gestire la durata di protezione del payload
5 pacchetti di NuGet
Microsoft.AspNetCore.DataProtection.SystemWeb
• Può essere installato nelle applicazioni .NET 4.x per gestire il «vecchio» machinekey
con il nuovo layer di sicurezza

Microsoft.AspNetCore.Cryptography.KeyDerivation
• Implementazione dell’algoritmo PBKDF2 per la gestione delle password
Data Protection: Consumer API
Viene creato un IDataProtector per ogni scopo di utilizzo e quindi viene
protetto/rivelato il contenuto

private readonly IDataProtector _protector;

public HomeController(IDataProtectionProvider provider)


{
_protector = provider.CreateProtector("MyProtector");
ProtectAndUnprotectData("Hello world");
}

private void ProtectAndUnprotectData(string input)


{
string protectedPayload = _protector.Protect(input);
string unprotectedPayload = _protector.Unprotect(protectedPayload);
}
Data Protection: Consumer API
I componenti vanno creati con purpose differenti, altrimenti dall’esterno è
facile replicare il comportamento e risalire al contenuto originale
E’ possibile passare una lista di stringhe come purpose, ma null non è
ammesso
Ambiente multi tenant
Tutti i purpose sono concatenabili, garantendo sicurezza ma facilità di
sviluppo
Limitare la durata del payload
Scenario: token generato per il reset della password, dopo 24 ore non è più
valido

public HomeController(IDataProtectionProvider provider)


{
var protector = provider.CreateProtector("MyProtector");
_timeLimitedProtector = protector.ToTimeLimitedDataProtector();
}

public IActionResult Index()


{
_protectedData = _timeLimitedProtector.Protect("Hello world", TimeSpan.FromSeconds(15));
ViewData["Message"] = _protectedData;
return View();
}
Password hashing
Le funzioni di derivazione per fare password hashing sono incluse in un
pacchetto separato che non ha dipendenze con tutto il sistema di data
protection.

private string HashPassword(string password)


{
// genera una chiave di cifratura random a 128-bit
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}

// genera una chiave con l'algoritmo PBKDF2


var keyDerivation = KeyDerivation.Pbkdf2(password, salt, KeyDerivationPrf.HMACSHA1, 10000, 256 / 8);
return Convert.ToBase64String(keyDerivation);
}
Data Protection: Configuration API
E’ possibile applicare configurazioni custom attraverso ConfigureServices
I sistemisti possono impostare dei valori di default attraverso HKLM
• HKLM\SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection

public void ConfigureServices(IServiceCollection services)


{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\directory\")) // Percorso dove salvare le chiavi
.ProtectKeysWithDpapi() // Cifratura automatica con DPAPI
.SetDefaultKeyLifetime(TimeSpan.FromDays(10)) // La durata di default delle chiavi è 90 giorni
.SetApplicationName("MyApplicationName") // Consente di condividere il payload protetto tra due app
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings() // Configurazione di nuovi algoritmi
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
}
Dove vengono mantenute le chiavi
Microsoft Azure
• Nella cartella virtuale "%HOME%\ASP.NET\DataProtection-Keys"
• Questa cartella è sincronizzata con tutte le macchine che hostano l’app

Se è presente un profilo utente


• Nella cartella "%LOCALAPPDATA%\ASP.NET\DataProtection-Keys"
• Se il sistema operativo è Windows, verrà usato DPAPI di default

Se l’applicativo gira in IIS


• Dentro HKLM in un registro protetto da ACL
Chiave di esempio
<?xml version="1.0" encoding="utf-8"?>
<key id="80732141-ec8f-4b80-af9c-c4d2d1ff8901" version="1">
<creationDate>2015-03-19T23:32:02.3949887Z</creationDate>
<activationDate>2015-03-19T23:32:02.3839429Z</activationDate>
<expirationDate>2015-06-17T23:32:02.3839429Z</expirationDate>
<descriptor deserializerType="{deserializerType}">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<enc:encryptedSecret decryptorType="{decryptorType}" xmlns:enc="...">
<encryptedKey>
<!-- This key is encrypted with Windows DPAPI. -->
<value>AQAAANCM...8/zeP8lcwAg==</value>
</encryptedKey>
</enc:encryptedSecret>
</descriptor>
</descriptor>
</key>
Data Protection: Extensibility API
Permettono di ridefinire tutto il ciclo di cifratura a partire dall’interfaccia
IAuthenticatedEncryption
Condivisione di authentication cookie
con ASP.NET 4.x
Le applicazioni ASP.NET 4.x che fanno uso di Katana come middleware per
generare i cookie, possono essere configurate per essere generare cookie
compatibili con il middleware di ASP.NET Core
• Katana = UseCookieAuthentication

Supporto dal framework 4.5.1+

E’ necessario installare da NuGet il pacchetto


Microsoft.Owin.Security.Interop
Condivisione di authentication cookie
con ASP.NET 4.x

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNetCore.Cookies",
TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(
DataProtectionProvider.Create(new DirectoryInfo(@"c:\shared-keys\"))
.CreateProtector("Microsoft.AspNetCore.Authentication.Coo
kies.CookieAuthenticationMiddleware",
"Cookies",
"v2")))
});
Rimpiazzare <machineKey>
Il nuovo sistema di protezione di ASP.NET Core può essere utilizzato per
rimpiazzare machineKey su applicativi ASP.NET 4.5.1+

Bisogna installare da NuGet il pacchetto


Microsoft.AspNetCore.DataProtection.SystemWeb

Verranno aggiunte queste righe nel web.config


<machineKey compatibilityMode="Framework45" dataProtectorType="..." />
<add key="aspnet:dataProtectionStartupType" value="" /></appSettings>
Rimpiazzare <machineKey>
Poiché le chiavi sono salvate dentro il file system, questo meccanismo non
funzionerà se l’applicativo è distribuito in un sistema scalabile: si crea una
classe custom a partire da DataProtectionStartup
public class MyDataProtectionStartup : DataProtectionStartup
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.SetApplicationName("my-app")
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\shared-keys\"))
.ProtectKeysWithCertificate("thumbprint");
}
}
Rimpiazzare <machineKey>
Rimpiazziamo la riga aggiunta automaticamente all’installazione del
pacchetto di NuGet con l’implementazione specifica della classe custom
appena creata

<add key="aspnet:dataProtectionStartupType"
value="DataProtectionDemo.MyDataProtectionStartup, DataProtectionDemo" />
Demo
Meccanismi di Caching
I dati memorizzati all’interno della cache sono accessibili molto più
velocemente rispetto all’accesso diretto alla sorgente

Miglioramento delle prestazioni delle applicazioni in termini di


performance e scalabilità

Applicazioni su server multipli devono gestire una cache distribuita


Sono previsti numerose modalità di caching, incluso il Memory
Caching

Memory Cache si utilizza implementando l’interfaccia IMemoryCache


In Memory Caching - Configurazione
Gestita tramite Dependency Injection modificando il metodo
ConfigureServices aggiungendo:

services.addMemoryCache();

Modifica del progetto, aggiungendo una reference a:


Microsoft.Extensions.Caching.Memory

Inserire all’interno del nostro controller o nel controller del nostro middleware
un’istanza di IMemoryCache
In Memory Caching – Lettura/Scrittura
Il metodo Invoke consente di ritornare i dati presenti all’interno della cache

Metodo Get e metodo TryGet per ottenere dati dalla cache (key per la
ricerca del dato nella cache

Metodo Set per salvare un valore nella cache (key per il recupero e
MemoryCacheEntryOptions)

E’ possibile impostare un ExpireTimes per i dati memorizzati all’interno della


cache
In Memory Caching - Callbacks
Possibilità di gestire callback:

Al momento della Set è possibile aggiungere

.RegisterPostEvictionCallback(key,value,reason,substate){…}

Reason Type: None


Removed
Replaced
Expired
TokenExpired
Capacity
In Memory Caching – Distributed
Cache
Migliora la gestione nella Cache nel cloud
La cache è coerente su tutti i server
La cache viene mantenuta anche in caso di riavvio del singolo server
L’archivio sorgente ha meno accessi

Utilizzo dell’interfaccia IDistributedCache, sia in modalità sincrona che


asincrona

Per la configurazione:
Specificare la dipendenza nel project.json
Configurare l’implementazione di IDistributedCache nel ConfigureServices(…)
Richiedere un’istanza di IDistributedCache all’interno del nostro Middleware o nel controller
In Memory Chaching
Possibilità di utilizzo con Redis localmente o con Azure

Possibilità di utilizzo con Sql Distributed Cache utilizzando:


• Microsoft.Extensions.Caching.SqlConfig.Tools: 1.0.0-preview2-final
• dotnet sql-cache create DataSource nomeDb

Get(…)
Set(…)

Usando un’istanza di
IDistributedCache e non SqlServerCache
Response Caching
Cache specifica per le risposte HTTP gestite tramite action di
ASP.NET MVC Core
Riduce il numero di richieste che un client o proxy deve effettuare
verso il server web
Cache-Control:
• Public
• Private
• No-cache
Response Caching
ResponseCacheAttribute: utilizzato per specificare gli header
che un controller utilizza per la gestione della cache

• Duration
• Location
• NoStore
• VeryByHeader
• CacheProfileName
• Order
Demo
Strumenti per lo sviluppo Client Side
Bower
Grunt

Gulp

npm
Yeoman
Node.js

Node.js : framework per la realizzazione di applicazioni utilizzando


Javascript

L’idea: utilizzare un linguaggio tipicamente cliente-side per la realizzazione


di applicazioni server-side

Basato su un modello event-driven: si esegue un’azione quando succede


qualcosa.

Gestione delle azioni in modalità asincrona


Node.js
Il funzionamento sincrono è di tipo sequenziale

Le chiamate Ajax : per natura sono chiamate di tipo asincrono

Si basa su Chrome’s Engine V.8 Javascript

Multipiattaforma : Windows, Mac e Linux


Node.js - Installazione
Sito di riferimento: http://nodejs.org

Utilizzo a linea di comando:

$> Node –v
$> npm install NomePackage
Node.js – Perché?
Compilazione del linguaggio di programmazione JavaScript:
Nessun nuovo linguaggio da impare!

Programmazione asincrona

Trasmissione tramite protocollo HTTP: velocità e affidabilità

Ampia community di sviluppatori e di package utilizzabili

Tool integrati all’interno di Visual Studio


Node.js
Attorno a Node.js sono nati una serie di tools che permettono di
automatizzare operazioni ricorrenti:

• Minimizzare file .css e/o javascript


• Concatenamento di file
• Ottimizzazione immagini

L’infrastruttura comune è quella di Node.js


Package Manager
Molto spesso si utilizzano componenti di terze parti all’interno di progetti
• Preprocessori di file CSS
• Strumenti di compressione dati
• Strumenti per semplificare il codice

L’utilizzo di pacchetti provenienti da fonti differenti: perdita di tempo!

Problemi di dipendenza tra i pacchetti: meglio risolverle automaticamente

Fonti differenti: sorgenti datati!


Package Manager
La soluzione è l’utilizzo di Package Manager

Tipicamente sono programmi a linea di comando

I più diffusi in ambito Web


• npm : presente nativamente in Node.Js (npm is not an acronym)
• Bower: si autodefinisce un package manager per il web
npm (Node Package Manager)
Npm è il sistema di gestione dei pacchetti integrato in Node.js
E’ possibile ricercare i pacchetti https://www.npmjs.com/
Repository centralizzato in cui sono presenti migliaia di progetti

In particolare sono presenti pacchetti per il web:


• Gulp
• Grunt
• Yeoman
• Autoprefixer
• BrowserSync
Bower
Sito di riferimento: http://www.bower.io
Rende più semplice e rapido l’utilizzo di librerie per il web

Nel suo repository sono presenti tra gli altri:


• Bootstrap
• AngularJS
• jQuery
• Font Awesome
• jQuery UI
Bower vs npm
Potremmo trovare pacchetti disponibile per entrambi
Entrambi permettono lo scaricamento di pacchetti rapidamente
Entrambi a linea di comando
Bower per la gestione di pacchetti front-end
Bower incluso nel progetto finale
Npm pacchetti legati allo sviluppo
Npm NON incluso nel progetto finale
Creazione di una soluzione con npm
Requisito Node.js: http://www.nodejs.org
Scaricato automaticamente
Per aggiornare la versione: npm install npm –g
Posizionarsi all’interno della cartella progetto
Inizializzare un progetto:

$> npm init

Ci verranno richieste una serie di domande


Al termine verrà creato il file package.json
npm Gestione dei pacchetti
Home del progetto: https://www.npmjs.com/
E’ prevista la ricerca di pacchetti specifici
E’ possibile ricercare anche direttamente dalla linea di comando

npm search <package>

E’ possibile installare il pacchetto

npm install <package>


Bower Gestione dei pacchetti
Home del progetto: http://bower.io/search/.
E’ prevista la ricerca di pacchetti specifici
Non è prevista la possibilità di ricercare pacchetti da linea di comando

E’ possibile installare il pacchetto

bower install <package>


Utilizzo dei pacchetti scaricati – npm
I pacchetti (tipicamente) vengono utilizzati da linea di comando

Comandi con funzionalità differenti


• Compilazione
• Compressione
• Combinazione di file

Possibilità di automatizzare i task impostando task runners


Gulp
Prerequisito: Node.js

Scrittura del codice utilizzando il runtime JavaScript di Chrome (V8)

Permette di automatizzare le operazioni più frequenti

Si aggiunge ad un progetto esistente creato con:

$> npm init


Gulp
Esempio di Task
• Analisi del codice JavaScript
• Compilazione dei file .less
• Concatenazione dei file JavaScript per ridurre le richieste HTTP
• Minificazione e rinominazione dei file creati
Minifying
E’ un processo che consente di ottimizzare le dimensioni di un file

Le operazioni di ottimizzazione non influenzano il normale funzionamento


del codice

Semplicemente vengono effettuate operazioni di eliminazione di spazi,


newline, commenti, ecc…
Grunt
Consente di creare task a partire da codice scritto in JavaScript

Disponibile tramite npm

Disponibili differenti plugin

Utilizzato per task come la minificazione, compilazione e operazioni di


unit testing
Yeoman
Creazione della struttura di un’applicazione a partire da template
Utilizza Node.js, Bower e Grunt
Disponibili numerosi template online (anche per .NET Core)
Template aspnet e aspnetcore-spa

$> npm install -g yo bower grunt-cli


$> npm install -g generator-webapp
$> yo webapp
Demo
Sviluppo di servizi con ASP.NET Core
ASP.NET Core ha template per la creazione di:
• Servizi basati su Web API
• Web Application
• Empty Application

Dependency Injection (Costructor Injection / Service Injection)

Non utilizza la System.Web.dll


Sviluppo di servizi con ASP.NET Core
ASP.NET Core utilizza Startup.cs per la configurazione

I controller WebApi diventano normali controller


• Non sono più presenti ApiController, System.Web.Http e IHttpActionResult

Utilizzo di UseMvc() per la configurazione del routing basato su


attributi/rotte

[Route("api/[controller]")]

// GET api/values/5
[HttpGet("{id}")]
CORS (Cross-Origin Requests)
In chiamate Ajax, sono tutti URL diversi
• http://example.com
• http://example.net
• http://example.com:9000/foo.htm
• https://example.com/foo.html
• http://www.example.com/foo.htm

Si usa CORS per consentire certi domini


Abilitare CORS
Prima si configura il servizio
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}

Poi si configurano le policy


public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
...
app.UseCors(builder => builder.WithOrigins("http://example.com"));
...
}
Anche in MVC
Basta usare gli attributi
• DisableCors: per disabilitare
• EnableCors: per abilitare

Sono filtri che agiscono su Controller/Action o globalmente


Demo
Internazionalizzazione
E’ fondamentale per rendere il sito web raggiungibile da chiunque
E’ la «somma» di globalizzazione e localizzazione

Globalizzazione
• Supporto a diverse culture per quanto riguarda input, display ed ouput dei dati
(formato di data e ora, numeri, valuta, case e text orientation, comparazione di
stringhe, etc…)

Localizzazione
• Supporto di nuove lingue (tramite ResourceManager)
Supporto da ASP.NET Core
Middleware
Supporto ai file .resx
Se ci fanno comodo, si possono usare i TagHelper
Localizzazione: le basi
Si parte dalla Dependency Injection dell’interfaccia IStringLocalizer
• Sfrutta ancora la parte funzionante di ResourceManager e ResourceReader

Non contiene altro che un dizionario con le chiavi ed i valori corrispondenti


da associare

Non richiede obbligatoriamente i file .resx per partire


• Viene fatto un controllo sull’esistenza della chiave dentro il dizionario, se non esiste
viene ritornato il valore stesso
• Nelle chiavi ci può anche essere html (ma è sconsigliato)
Localizzazione: le basi
Esempio di applicativo WebAPI

public class AboutController : Controller


{
private readonly IStringLocalizer<AboutController> _localizer;

public AboutController(IStringLocalizer<AboutController> localizer)


{
_localizer = localizer;
}

[HttpGet]
public string Get()
{
return _localizer["About Title"];
}
}
Localizzazione: le basi
Si può usare anche l’interfaccia IHtmlLocalizer per fare encoding di Html

public class AboutController : Controller


{
private readonly IHtmlLocalizer<AboutController> _localizer;

public AboutController(IHtmlLocalizer<AboutController> localizer)


{
_localizer = localizer;
}

[HttpGet]
public LocalizedHtmlString Get(string name)
{
return _localizer["<b>Hello</b><i> {0}</i>", name];
}
}
Localizzazione: la view
Esiste un localizer specifico per la view: IViewLocalizer
• Continuano ad essere supportati anche IStringLocalizer e IHtmlLocalizer

@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer ViewLocalizer

<h2>@ViewLocalizer["Hello World"]</h2>
Lavorare con le risorse
Si utilizzano i .resx per localizzare il testo in funzione della lingua
• Classico dizionario chiave-valore

I .resx possono essere specifici per Controller, View e tutti i file che sono nel
progetto ASP.NET Core

E’ sempre bene creare un file X.LC.resx


• X è il nome del file
• LC è il Language Code (formato ISO)
• Senza il language code viene creata una normale classe, da evitare!
Convenzioni delle risorse
Poiché tutti i file sono localizzabili tramite file di risorse, c’è bisogno di
sfruttare delle convenzioni

Nome della risorsa Notazione

Resources/Controllers.HomeController.it.resx Punto

Resources/Controllers/HomeController.it.resx Cartelle (percorso)


Convenzioni delle risorse
Anche «Resources» come percorso è configurabile

public void ConfigureServices(IServiceCollection services)


{
services.AddMvc();

// aggiungiamo la localizzazione
services.AddLocalization(options => options.ResourcesPath = "Resources");
}
Supporto alle DataAnnotations
Le DataAnnotations hanno di default un messaggio che viene utilizzato
durante la validazione, possiamo sfruttarlo e localizzarlo

public void ConfigureServices(IServiceCollection services)


{
services.AddMvc().AddDataAnnotationsLocalization();
}

[Required(ErrorMessage = "Il campo Password è obbligatorio")]


[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
Middleware per la localizzazione
Per impostare valori di default per le lingue, per indicare quali sono le lingue
supportate (e così via), è disponibile un middleware

var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("it") };

app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("it", "it"),
SupportedCultures = supportedCultures, // globalizzazione...
SupportedUICultures = supportedCultures // localizzazione
});
RequestCultureProvider
Ogni richiesta viene analizzata e parsata la lista di RequestCultureProvider
per determinare in modo efficiente qual è la lingua da utilizzare

QueryStringRequestCultureProvider
• Di solito viene utilizzato solo per scopi di debug
http://localhost:5000/?culture=it-IT&ui-culture=en-US

AcceptLanguageHeaderRequestCultureProvider
• Non è un metodo infallibile

CookieRequestCultureProvider
RequestCultureProvider custom
Si possono anche creare provider custom, grazie alla Dependency Injection
di ASP.NET Core

services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("it") };

options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");


options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;

options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(async context =>


{
return new ProviderCultureResult("en");
}));
});
Demo
Pubblicazione su IIS
Integrazione con IIS

Necessario includere il package Microsoft.AspNetCore.Server.IISIntegration


all’interno delle reference

Per poter utilizzare il middleware di IIS è necessario aggiungere a


WebHostBuilder:

.UseIISIntegration()

Non è utilizzabile su sistemi operativi differenti (solo Windows)

Possibilità di estendere con IISOptions


IIS con WebListener
Scelta da preferire, perché si basa su http.sys (più performante)
Occorre rimuovere Kestrel e poi aggiungere in WebHostBuilder

.UseWebListener(options =>
{
options.EnableResponseCaching = true;
var auth = options.ListenerSettings.Authentication;
auth.AllowAnonymous = true;
auth.Schemes = AuthenticationSchemes.NTLM;
})

V2: WebListener rinominato in HTTP.sys


• Utilizzato per l’hosting di ASP.NET Core su IIS
Kestrel
Web server multi piattaforma sviluppato con librerie asincrone
• Si usa in reverse proxy
• [v2] Nuova proprietà Limit per modificare i limiti di risposta
• [v2] Nuove feature che lo rendono direttamente esponibile via internet

Microsoft.AspNetCore.Server.Kestrel

Necessario includere il package all’interno del file project.json


Per poter utilizzare il middleware di Kestrel è necessario aggiungere a
WebHostBuilder:

.UseKestrel()
Docker
Docker è un container per il deploy di applicazioni e servizi

Prerequisiti
• Visual Studio 2015 Update3
• Microsoft ASP.NET Core SDK
• Visual Studio 2015 Tools for Docker
• Docker for Windows

Aggiungere alla solution il supporto a Docker, verranno aggiunti i file:


• docker-compose.yml
• dockerfile
• dockerfile.debug
• dockerTask.ps1
Docker
Dockertask.ps1 è lo script PowerShell generato automaticamente per
solution corrente
$> docker-machine ls

Dal prompt di PowerShell è possibile visualizzare lo stato delle vm docker

Per effettuare il build ed il run


$> DockerTask.ps1 –Build –Environment Release
$> DockerTask.ps1 –Run –Environment Release
Demo
SPA Templates
Template per SPA già pronti per
• Angular (4)
• Aurelia
• Knokout.js
• React.js
• React.js+Redux
Angular e React già inclusi, gli altri scaricabili
Semplificano la creazione di un nuovo progetto SPA con backend
basato su ASP.NET Core
Performance con ASP.NET Core 2
25% di richieste al secondo in più, a parità di codice
<1 secondo per l’avvio dell’app
Razor View compilation
• Attivo di default
• View pronta a generare l’HTML, perché non compilata dinamicamente

Ditelo ai vostri amici ☺


One more thing
ASP.NET Core SignalR
Arriva nelle prossime settimane
Riscritto per ASP.NET Core
Client library slegata da jQuery

https://github.com/aspnet/SignalR
Demo
SignalR con ASP.NET Core
© 2018 iCubed Srl
La diffusione di questo materiale per scopi differenti da quelli per cui se
ne è venuti in possesso è vietata.

iCubed s.r.l.
Piazza Durante, 8 20131 MILANO
Phone: +39 02 57501057
P.IVA 07284390965

Potrebbero piacerti anche