ASP.
NET Core
Daniele Bochicchio
Chief Digital Officer @icubedsrl
Microsoft Regional Director
Microsoft [Link] MVP
Network Manager @aspitalia
danieleb@[Link]
@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:
• [Link] Core
• Windows 10 Universal Windows Platform (UWP)
• [Link]
Installazione
Microsoft .NET Core
• CoreCLR runtime
• dotnet app launcher
Microsoft .NET Core SDK
• Gestione pacchetti
• Compilazione
Dal sito [Link]
• 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
• [Link] e [Link]
.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 [Link])
• 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 [Link]
Test
• Unit test, nunit o xunit
• Motori installati via NuGet
• Sezione testRunner in [Link]
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
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
Terze parti
• [Link]
.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 [Link].1 4.64.6 [Link].1 [Link].2
SDK))
2.0: .NET Core 2
.NET Framework
(con .NET Core 2.0 4.54.5 4.54.5 [Link].1 4.64.6 [Link].1 [Link].1 [Link].1 [Link].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
[Link] 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.010.0 10.1410.14
[Link] 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.03.0 3.83.8
[Link] 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
[Link]
• Metapackage: insieme di tutti i pacchetti .NET STandard
[Link]
• 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
• [Link]
• +20K API e il 70% dei package di NuGet
Comparazione con .NET Framework
App model
• .NET Core supporta solo console e [Link]
• 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 [Link] public static void Main(string[] args)
{
• Metodo statico Main [Link]("Hello world!");
}
Parametri opzionali attraverso args }
Namespace [Link]
• 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="[Link]">
ha il classico csproj <PropertyGroup>
• Ma è una versione semplificata <VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha</VersionSuffix>
• Nelle preview veniva usato
<TargetFrameworks>netcoreapp2
[Link] </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="[Link]" />
</Target>
• Array o singola istruzione <Target Name="MyPostCompileTarget"
• Bat, cmd, sh, bash, DOS AfterTargets="Publish">
<Exec Command="[Link]" />
<Exec Command="[Link]" />
</Target>
Opzioni del runtime <PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
• Modalità del Garbage Collection <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection>
• Utile in [Link] <!– 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
[Link]
• l’applicazione con l’entry point
[Link]
• le runtimeOptions specificate nel progetto (opzionale)
[Link]
• lista di tutte le dipendenze. Necessario in ambienti dinamici, come [Link]
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 => [Link] "dependencies":{
"[Link]":"1.6.0"
},
"frameworks":{
"netstandard1.3":{}
}
}
Demo
Cos’è [Link] Core?
Una rivoluzionaria versione di [Link]
Basato su .NET Core
Un taglio netto rispetto al passato
Completamente riscritta
Cross-platform
Born in the cloud
Performante
[Link] 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
[Link]
Che fine ha fatto [Link] 5?
[Link] Core 1.0 è [Link] 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
[Link] 4.6 e [Link] Core
[Link] 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 -> [Link]
[Link] CodeDOM -> [Link] Application DNX (Roslyn)
4.x Loose, GAC,
NuGet
Libraries NuGet
Application
FCL, GAC, NuGet NuGet
Open Source
Frameworks
IIS, [Link]
IIS Web Server Kestrel
Application
[Link] DNX
Host
Platform .NET BCL & FCL
.NET BCL & FCL
Libraries .NET on NuGet
.NET CLR
.NET CLR Runtime .NET Core CLR
IIS: [Link] Runtime
DNX
Exe: OS Loader
[Link]
Operating Windows, OSX, Core
Windows
System Linux
[Link] Core 101
[Link] Core = runtime + [Link] MVC + [Link] Web API
Usando [Link] Core finiamo inevitabilmente ad usare [Link] MVC
Addio [Link]
• 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 [Link] dentro il progetto
Novità di [Link] 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 [Link]
Server
[Link] è disegnato per essere disaccoppiato dal server
Tradizionalmente, [Link] è stato legato ad IIS
[Link] Core gira su IIS sfruttandolo come reverse proxy
Ci sono due server web:
• [Link] (Kestrel, cross-platform)
• [Link] (WebListener, Windows)
La configurazione è nel file [Link]
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 [Link]:
• 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 [Link] Core e le funzionalità aggiuntive
Ad esempio, i file statici sono supportati attraverso un MiddleWare
• Vedi [Link] / Configure -> UseStaticFiles
C’è una nuova impostazione del progetto, per gestire i file statici
• Vedi wwwroot
• Vedi UseContentRoot in [Link]
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 [Link]?
Esiste solo per compatibilità con IIS
Sostituito da un file chiamato [Link]
I parametri di configurazione sono in file JSON
• Con supporto degli environement
Dichiarazione in codice vs parsing a runtime di un file XML
[Link]
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 [Link] 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
• [Link]
[Link]
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
• [Link] include già un motore di DI
DI con [Link] Core
Per configurare [Link]
public void ConfigureServices(IServiceCollection services)
{
…
[Link]<IMyService, MyService>();
[Link]<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 [Link]
È 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();
[Link]<DefaultModule>();
[Link](services);
var container = [Link]();
return new AutofacServiceProvider(container);
}
In questo caso usiamo Autofac e la DI va configurata in DefaultModule
Demo
Environment
Di default [Link] ha 3 ambienti preconfigurati (nome case insensitive)
• Development
• Staging
• Production
Il valore attuale viene letto dalla variabile ASPNETCORE_ENVIRONMENT
• Definibile nel [Link], nella config, su env variable, Azure, etc
Le informazioni per lo sviluppo sono persistite in un file chiamato
[Link] 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 [Link]
È 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 [Link] risponderà alle richieste
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
[Link]([Link]("Logging"));
[Link]();
if ([Link]())
{
[Link]();
[Link]();
[Link]();
}
else
{
[Link]("/Home/Error");
}
[Link]();
[Link]();
[Link](routes =>
{
[Link](
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Il metodo ConfigureServices
Per configurare [Link]
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
[Link]<ApplicationDbContext>(options =>
[Link]([Link]("DefaultConnection")));
[Link]<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
[Link]();
// Add application services.
[Link]<IEmailSender, AuthMessageSender>();
[Link]<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 [Link]
Demo
Logging
Integrato all’interno del runtime
Legato all’interfaccia ILoggerFactory o ILogger<T>
var logger = [Link]("LogCategory");
[Link]("No items found");
// con ILogger<T> non serve creare il logger esplicitamente
// logger = ILogger<Todo> che arriva da DI
[Link]("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 [Link],
anziché [Link]
Il motore di DI registra già tutti i servizi di logging
Provider e filtri aggiuntivi vanno specificati in [Link]
Vedi [Link]
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([Link]…)
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 [Link]/Configure
[Link]([Link]("Logging"));
[Link]([Link]);
Il livello corrente viene letto da [Link]
Demo
Gestione degli errori
La gestione degli errori avviene in [Link]/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:
[Link]("~/errors/{0}");
Demo
[Link] Core = [Link] MVC
[Link] Core include MVC e WebAPI in un solo motore
Di fatto, [Link] Core, al netto del runtime, è [Link] Core MVC
MVC = Model View Controller
• Pattern per disegnare la UI
Rispetto ad [Link] MVC, [Link] Core MVC ha diverse novità
Come funziona MVC
[Link] Web
Browser Server
[Link]
[Link]
(View)
ProductsController
(Controller)
[Link]
(View)
Product
(Model) SQL
[Link] 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
• [Link] Core 1 = .NET Core + [Link] Core MVC + WebAPI
• [Link] 4.6 = .NET Framework 4.6 + [Link] MVC 5/Core MVC + WebAPI
[Link] 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à [Link] MVC, come JSP/Struts in Java è basato su Model 2
• [Link]
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 [Link] normale viene usata la pagina fisica
• A meno che non si usino HttpHandler
Con [Link] 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
[Link].
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 [Link], [Link], etc.
Il dato prelevato è nel Model e passato al Controller tramite il metodo View,
per visualizzare le informazioni a video
Demo
Novità di [Link] Core MVC
[Link] 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
• @[Link](…)
Partial View con [Link]
• Ci ricordano gli User Control
[Link] 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 = "[Link] Core Guida completa",
Author = "Daniele Bochicchio",
Category = "[Link]",
Price = 19.99,
ReleaseDate = [Link](-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>@[Link]</h1>
<p>Prezzo: @[Link]()</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";
[Link] = "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 [Link]
La sua sintassi è basata su C#
@
La "chiocciolina" magica
Istruzione su singola riga
@{ var theMonth = [Link]; }
<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 è: ""[Link] Core"""; }
<p>@myQuote</p>
Se le stringhe contengono le virgolette è sufficiente raddoppiarle
Sintassi e tip
@if (IsPost) {
<p>Ciao, oggi è @[Link] e questo è un post!</p>
}
else {
<p>Ciao <em>straniero</em>, oggi è: <br /> </p> @[Link]
}
Razor permette di innestare codice client con codice server
Sintassi e tip
@if (IsPost) {
@: Oggi è: <br /> @[Link]
<br />
@[Link] @: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
@[Link]("_Partial", Model)
PartialView async
[Link] Core introduce il supporto per le partial async
Utile per migliorare la scalabilità, qualora ci sia codice async nella view
@await [Link]("_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 [Link]
@{ [Link] = [Link]; }
<h2>Libro</h2>
<p>
@[Link]
</p>
<p>
@[Link]
</p>
<p>
@[Link]
</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/[Link]" />
<link rel="stylesheet" href="~/css/[Link]" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet"
href="[Link]
asp-fallback-href="~/lib/bootstrap/dist/css/[Link]"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/[Link]" asp-append-version="true" />
</environment>
Importazione globale per View
Possiamo registrare funzionalità a livello globale nel file
Shared\_ViewImports.cshtml
@using WebApplication1
@using [Link]
@using [Link]
@using [Link]
@addTagHelper *, [Link]
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 [Link] 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, [Link] 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
[Link]<RazorViewEngineOptions>(options =>
{
[Link]();
[Link]("/Backoffice/{2}/Views/{1}/{0}.cshtml");
[Link]("/Backoffice/{2}/Views/Shared/{0}.cshtml");
[Link]("/Views/Shared/{0}.cshtml");
});
Indicare l’area
A differenza di [Link] MVC, in [Link] 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 [Link], 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)
{
[Link]<IMyService, MyService>();
}
public IActionResult About([FromServices] IMyService service)
{
ViewData["Message"] = [Link]();
return View();
}
Injection dei servizi
In alternativa è sempre possibile iniettare la dipendenza dal costruttore del
controller
[Link] 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
@[Link]()
Demo
View Components
Novità di [Link] 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 [Link]("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 [Link]/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 [Link]
<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 [Link]
<div asp-validation-
summary="[Link]"></div>
Tag helper: select
Genera un tag <select> con le opzioni specificate all’interno della proprietà
Items
E’ l’alternativa di [Link]
<select asp-for="Country"
asp-items="[Link]"></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)
{
[Link] = "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)
{
[Link] = "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)
{
[Link] = "a"; // Replaces <email> with <a> tag
var address = MailTo + "@" + EmailDomain;
[Link]("href", "[Link] + address);
[Link](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 = [Link])]
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
[Link] 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/[Link]" />
<link rel="stylesheet" href="~/dev/[Link]" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="~/prod/[Link]" />
<link rel="stylesheet" href="~/prod/[Link]" />
</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()
• [Link]
• Metodo List(string category)
• [Link]
• Metogo Details(int id)
• [Link]
Da [Link] MVC…
La magia avviene nella classe RouteConfig, richiamata allo startup dal
[Link]
Viene sfruttato il ModelBinding per associare i parametri della route
public static void RegisterRoutes(RouteCollection routes)
{
[Link]("{resource}.axd/{*pathInfo}");
[Link](
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = [Link] });
}
…ad [Link] 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)
{
[Link](routes =>
{
[Link](name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
// equivalente
[Link]();
}
Personalizzazione
È possibile personalizzare il comportamento di routing semplicemente
aggiungendo constraint sulle proprietà o eventuali token per gestire, ad
esempio, la localizzazione
[Link](
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 [Link]
{
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 = [Link](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 [Link] 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
• [Link]
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="[Link]" />
</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 [Link]
• Key, Required, MaxLenght…
Possono influenzare lo schema del database
• Namespace [Link]
• 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)
{
[Link]("Admin");
//Map entity to table
[Link]<Student>().ToTable("StudentInfo");
[Link]<Student>().ToTable("StandardInfo", "anotherSchema");
}
Property Mapping
Configurazione della chiave primaria
[Link]<Student>().HasKey<int>(s => [Link]);
Configurazione di altre proprietà
[Link]<Student>().Property(p => [Link])
.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>();
[Link](new Student() { Name = "Studente 1" });
[Link](new Student() { Name = "Studente 2" });
[Link](new Student() { Name = "Studente 3" });
[Link](fakeStudents);
[Link]();
[Link](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
[Link]<CreditCard>().ToList();
Demo
Entity Framework
Configurazione per [Link] 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
[Link] [Link] 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)
{
[Link](optionsBuilder);
[Link](@"myConnectionString");
}
}
Mapping
Riscritta completamente la parte relativa ai provider
Lunga vita alla Dependency Injection!
var serviceProvider = [Link]<IServiceProvider>();
var typeMapper = [Link]<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
[Link]();
Dall’EDMX al futuro
L’EDMX non è supportato da EFCore, è richiesto un nuovo modello
Sono necessari questi pacchetti di NuGet:
[Link]
[Link].{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
• [Link]
• 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)
{
[Link]();
foreach (var entry in [Link]().Where(e => [Link] == [Link]))
{
//modify [Link] here
}
[Link] = false;
var result = [Link](acceptAllChangesOnSuccess);
[Link] = true;
return result;
}
Custom conventions
Non c’è più l’interfaccia IStoreModelConvention di EF6
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in [Link]())
foreach (var property in [Link]())
{
var columnName = [Link]().ColumnName;
if ([Link] > 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 = [Link]<IServiceScopeFactory>().CreateScope())
{
var ctx = [Link]<Context>();
[Link]();
[Link]();
}
public static void EnsureSeedData(this Context context)
{
[Link](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 ([Link])
Locali (su controller o action)
L'infrastruttura dei filtri
Simili agli HttpModule di [Link]
Sono pensati per [Link] 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)
{
...
[Link](option =>
{
// istanza per ogni richiesta
[Link](new MyFilterAttribute());
// ogni richiesta crea una nuova istanza
[Link](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 = [Link] 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 [Link] 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 [Link]
• HttpHandler implementano l’interfaccia IHttpHanderler
• HttpHandler: processi che vengono eseguiti in risposta ad una richiesta
<configuration>
<[Link]>
<modules>
<add name="HelloWorldModule" type="HelloWorldModule"/>
</modules>
</[Link]>
</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 [Link])
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. [Link]("/maptest", HandleMapTest);
Use: consente di aggiungere un delegato alla richiesta
Middleware come Filter
[Link] 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 [Link]
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 [Link]
L’ordine di esecuzione è gestito da una serie di eventi BeginRequest,
AuthenticationRequest, ecc
L’ordine di esecuzione è configurabile nel [Link]
Migrare da HttpModule a Middleware
Middleware
HttpModules, HttpHandler, [Link] e [Link] 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
• [Link]
• [Link]
• [Link]
handler-code-to-middleware
Identità e sicurezza
Authorization e differenze rispetto al passato
[Link] Identity
CookieAuthentication
Data Protection
[Link] 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
[Link] 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 [Link] 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 [Link], prima di UseMvc:
[Link](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 [Link]("MyCookieMiddlewareInstance",
principal,
new AuthenticationProperties
{
ExpiresUtc = [Link](20),
IsPersistent = true
});
Per fare logout
await [Link]("MyCookieMiddlewareInstance");
Autenticazione
Con [Link] 1.x l’autenticazione è configurata con Middleware
Con [Link] 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
[Link]
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
[Link] 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
[Link]
• Perfetto per le Consumer API
[Link]
• Espone tutto il core del sistema di protezione di [Link], inclusa la gestione delle
chiavi, operazioni di crittografia e configurazioni.
[Link]
• 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
[Link]
• Può essere installato nelle applicazioni .NET 4.x per gestire il «vecchio» machinekey
con il nuovo layer di sicurezza
[Link]
• 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 = [Link]("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 = [Link]("MyProtector");
_timeLimitedProtector = [Link]();
}
public IActionResult Index()
{
_protectedData = _timeLimitedProtector.Protect("Hello world", [Link](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 = [Link]())
{
[Link](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\[Link]
public void ConfigureServices(IServiceCollection services)
{
[Link]()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\directory\")) // Percorso dove salvare le chiavi
.ProtectKeysWithDpapi() // Cifratura automatica con DPAPI
.SetDefaultKeyLifetime([Link](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%\[Link]\DataProtection-Keys"
• Questa cartella è sincronizzata con tutte le macchine che hostano l’app
Se è presente un profilo utente
• Nella cartella "%LOCALAPPDATA%\[Link]\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 [Link] 4.x
Le applicazioni [Link] 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 [Link] Core
• Katana = UseCookieAuthentication
Supporto dal framework 4.5.1+
E’ necessario installare da NuGet il pacchetto
[Link]
Condivisione di authentication cookie
con [Link] 4.x
[Link](new CookieAuthenticationOptions
{
AuthenticationType = [Link],
CookieName = ".[Link]",
TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(
[Link](new DirectoryInfo(@"c:\shared-keys\"))
.CreateProtector("[Link]
[Link]",
"Cookies",
"v2")))
});
Rimpiazzare <machineKey>
Il nuovo sistema di protezione di [Link] Core può essere utilizzato per
rimpiazzare machineKey su applicativi [Link] 4.5.1+
Bisogna installare da NuGet il pacchetto
[Link]
Verranno aggiunte queste righe nel [Link]
<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)
{
[Link]()
.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="[Link], 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:
[Link]();
Modifica del progetto, aggiungendo una reference a:
[Link]
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 [Link]
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:
• [Link]: 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
[Link] 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
[Link]
[Link] : 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
[Link]
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
[Link] - Installazione
Sito di riferimento: [Link]
Utilizzo a linea di comando:
$> Node –v
$> npm install NomePackage
[Link] – 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
[Link]
Attorno a [Link] 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 [Link]
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 [Link] (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 [Link]
E’ possibile ricercare i pacchetti [Link]
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: [Link]
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 [Link]: [Link]
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 [Link]
npm Gestione dei pacchetti
Home del progetto: [Link]
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: [Link]
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: [Link]
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 [Link], 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 [Link] Core
[Link] 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 [Link]
Sviluppo di servizi con [Link] Core
[Link] Core utilizza [Link] per la configurazione
I controller WebApi diventano normali controller
• Non sono più presenti ApiController, [Link] 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
• [Link]
• [Link]
• [Link]
• [Link]
• [Link]
Si usa CORS per consentire certi domini
Abilitare CORS
Prima si configura il servizio
public void ConfigureServices(IServiceCollection services)
{
[Link]();
}
Poi si configurano le policy
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
...
[Link](builder => [Link]("[Link]
...
}
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 [Link] 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 [Link]
@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 [Link] Core
E’ sempre bene creare un file [Link]
• 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/[Link] Punto
Resources/Controllers/[Link] Cartelle (percorso)
Convenzioni delle risorse
Anche «Resources» come percorso è configurabile
public void ConfigureServices(IServiceCollection services)
{
[Link]();
// aggiungiamo la localizzazione
[Link](options => [Link] = "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)
{
[Link]().AddDataAnnotationsLocalization();
}
[Required(ErrorMessage = "Il campo Password è obbligatorio")]
[DataType([Link])]
[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") };
[Link](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
[Link]
AcceptLanguageHeaderRequestCultureProvider
• Non è un metodo infallibile
CookieRequestCultureProvider
RequestCultureProvider custom
Si possono anche creare provider custom, grazie alla Dependency Injection
di [Link] Core
[Link]<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("it") };
[Link] = new RequestCulture(culture: "en-US", uiCulture: "en-US");
[Link] = supportedCultures;
[Link] = supportedCultures;
[Link](0, new CustomRequestCultureProvider(async context =>
{
return new ProviderCultureResult("en");
}));
});
Demo
Pubblicazione su IIS
Integrazione con IIS
Necessario includere il package [Link]
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 [Link] (più performante)
Occorre rimuovere Kestrel e poi aggiungere in WebHostBuilder
.UseWebListener(options =>
{
[Link] = true;
var auth = [Link];
[Link] = true;
[Link] = [Link];
})
V2: WebListener rinominato in [Link]
• Utilizzato per l’hosting di [Link] 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
[Link]
Necessario includere il package all’interno del file [Link]
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 [Link] Core SDK
• Visual Studio 2015 Tools for Docker
• Docker for Windows
Aggiungere alla solution il supporto a Docker, verranno aggiunti i file:
• [Link]
• dockerfile
• [Link]
• 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
• [Link]
• [Link]
• [Link]+Redux
Angular e React già inclusi, gli altri scaricabili
Semplificano la creazione di un nuovo progetto SPA con backend
basato su [Link] Core
Performance con [Link] 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
[Link] Core SignalR
Arriva nelle prossime settimane
Riscritto per [Link] Core
Client library slegata da jQuery
[Link]
Demo
SignalR con [Link] 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
[Link] 07284390965