Eventi
19 nov, 23 - 21 nov, 23
Partecipa alle sessioni online di Microsoft Ignite create per espandere le tue competenze e aiutarti a risolvere i problemi complessi di oggi.
Iscriviti subitoQuesto browser non è più supportato.
Esegui l'aggiornamento a Microsoft Edge per sfruttare i vantaggi di funzionalità più recenti, aggiornamenti della sicurezza e supporto tecnico.
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Di Tom Dykstra, Jeremy Likness e Jon P Smith
Questa è la prima di una serie di esercitazioni che illustrano come usare Entity Framework (EF) Core in un'app ASP.NET Core Razor Pages . Con queste esercitazioni viene creato un sito Web per una fittizia Contoso University. Il sito include funzionalità, come ad esempio l'ammissione di studenti, la creazione di corsi e le assegnazioni degli insegnati. L'esercitazione usa l'approccio code first. Per informazioni su come seguire questa esercitazione usando il primo approccio al database, vedere questo problema di GitHub.
Scaricare o visualizzare l'app completata. Istruzioni per il download.
Le istruzioni per Visual Studio usano SQL Server Local DB, una versione di SQL Server Express eseguita solo in Windows.
Se si verifica un problema che non è possibile risolvere, confrontare il codice con il progetto completato. Un buon modo per ottenere assistenza consiste nel pubblicare una domanda per StackOverflow.com, usando il tag ASP.NET Core o il EF Core tag .
L'applicazione compilata in queste esercitazioni è il sito Web di base di un'università. Gli utenti possono visualizzare e aggiornare le informazioni che riguardano studenti, corsi e insegnanti. Di seguito sono disponibili alcune schermate dell'esercitazione.
Lo stile dell'interfaccia utente del sito è basato sui modelli di progetto predefiniti. L'obiettivo dell'esercitazione è come usare EF Core con ASP.NET Core, non su come personalizzare l'interfaccia utente.
Questo passaggio è facoltativo. La compilazione dell'app completata è consigliata quando si verificano problemi che non è possibile risolvere. Se si verifica un problema che non è possibile risolvere, confrontare il codice con il progetto completato. Istruzioni per il download.
Selezionare questa opzione ContosoUniversity.csproj
per aprire il progetto.
Compilare il progetto.
Nella console di Gestione pacchetti eseguire il comando seguente:
Update-Database
Eseguire il progetto per il seeding del database.
Avviare Visual Studio 2022 e selezionare Crea un nuovo progetto.
Nella finestra di dialogo Crea un nuovo progetto selezionare ASP.NET Core Web App e quindi selezionare Avanti.
Nella finestra di dialogo Configura il nuovo progetto immettere ContosoUniversity
per Nome progetto. È importante denominare il progetto ContosoUniversity, inclusa la corrispondenza con la maiuscola, in modo che gli spazi dei nomi corrispondano quando si copia e incolla il codice di esempio.
Selezionare Avanti.
Nella finestra di dialogo Informazioni aggiuntive selezionare .NET 6.0 (supporto a lungo termine) e quindi selezionare Crea.
Copiare e incollare il codice seguente nel Pages/Shared/_Layout.cshtml
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/ContosoUniversity.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2021 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Il file di layout imposta l'intestazione, il piè di pagina e il menu del sito. Il codice precedente apporta le modifiche seguenti:
In Pages/Index.cshtml
sostituire il contenuto del file con il codice seguente:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
@* <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
*@ </p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
@* <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
*@ </p>
</div>
</div>
</div>
</div>
Il codice precedente sostituisce il testo relativo a ASP.NET Core con testo su questa app.
Eseguire l'app per verificare che venga visualizzata la home pagina.
Nelle sezioni seguenti viene descritto come creare un modello di dati:
Uno studente può iscriversi a un numero qualsiasi di corsi e un corso può avere un numero qualsiasi di studenti iscritti.
Models/Student.cs
con il codice seguente:namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà ID
diventa la colonna chiave primaria della tabella del database che corrisponde a questa classe. Per impostazione predefinita, EF Core interpreta una proprietà denominata ID
o classnameID
come chiave primaria. Il nome alternativo riconosciuto automaticamente per la chiave primaria della classe Student
è quindi StudentID
. Per altre informazioni, vedere EF Core - Chiavi.
La proprietà Enrollments
rappresenta una proprietà di navigazione. Le proprietà di navigazione contengono altre entità correlate a questa entità. In questo caso la proprietà Enrollments
di un'entità Student
contiene tutte le entità Enrollment
correlate a tale studente. Ad esempio, se una riga Student nel database presenta due righe Enrollment correlate, la proprietà di navigazione Enrollments
contiene questi due entità Enrollment.
Nel database una riga Enrollment è correlata a una riga Student se la relativa StudentID
colonna contiene il valore ID dello studente. Si supponga, ad esempio, che una riga Student abbia ID=1. Le righe di registrazione correlate avranno StudentID
= 1. StudentID
è una chiave esterna nella tabella Enrollment.
La proprietà Enrollments
è definita come ICollection<Enrollment>
perché potrebbero essere presenti più entità Enrollment correlate. È possibile usare altri tipi di raccolta, ad esempio List<Enrollment>
o HashSet<Enrollment>
. Quando ICollection<Enrollment>
viene utilizzato, EF Core crea una HashSet<Enrollment>
raccolta per impostazione predefinita.
Creare Models/Enrollment.cs
con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
La proprietà EnrollmentID
è la chiave primaria. Questa entità usa il modello classnameID
anziché ID
da solo. Per un modello di dati di produzione, molti sviluppatori scelgono un modello e lo usano in modo coerente. Questa esercitazione usa entrambi solo per illustrare che entrambi funzionano. L'uso di ID
senza classname
rende più semplice l'implementazione di alcuni tipi di modifiche al modello di dati.
La proprietà Grade
è un oggettoenum
. Il punto interrogativo dopo la dichiarazione del tipo Grade
indica che la proprietà Grade
ammette i valori Null. Un grado null è diverso da un grado zero. Null significa che un voto non è noto o non è ancora stato assegnato.
La proprietà StudentID
è una chiave esterna e la proprietà di navigazione corrispondente è Student
. Un'entità Enrollment
è associata a un'entità Student
, pertanto la proprietà contiene un'unica entità Student
.
La proprietà CourseID
è una chiave esterna e la proprietà di navigazione corrispondente è Course
. Un'entità Enrollment
è associata a un'entità Course
.
EF Core interpreta una proprietà come chiave esterna se è denominata <navigation property name><primary key property name>
. Ad esempio, StudentID
è la chiave esterna per la proprietà di navigazione Student
, dato che la chiave primaria Student
dell'entità è ID
. Le proprietà di chiave esterna possono anche essere denominate <primary key property name>
. Ad esempio, CourseID
poiché la chiave primaria Course
dell'entità è CourseID
.
Creare Models/Course.cs
con il codice seguente:
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà Enrollments
rappresenta una proprietà di navigazione. È possibile correlare un'entità Course
a un numero qualsiasi di entità Enrollment
.
L'attributo DatabaseGenerated
consente all'app di specificare la chiave primaria anziché chiedere al database di generarla.
Compilazione dell'app. Il compilatore genera diversi avvisi sulla modalità null
di gestione dei valori. Per altre informazioni, vedere questo problema di GitHub, i tipi di riferimento Nullable e Esercitazione: Esprimere più chiaramente la finalità di progettazione con tipi riferimento nullable e non nullable.
Per eliminare gli avvisi dai tipi riferimento nullable, rimuovere la riga seguente dal ContosoUniversity.csproj
file:
<Nullable>enable</Nullable>
Il motore di scaffolding attualmente non supporta tipi riferimento nullable, pertanto i modelli usati nello scaffolding non possono neanche.
Rimuovere l'annotazione del ?
tipo riferimento nullable da public string? RequestId { get; set; }
in Pages/Error.cshtml.cs
in modo che il progetto venga compilato senza avvisi del compilatore.
In questa sezione viene usato lo strumento di scaffolding ASP.NET Core per generare:
DbContext
. Il contesto è la classe principale che coordina le funzionalità di Entity Framework per un determinato modello di dati. Deriva dalla classe Microsoft.EntityFrameworkCore.DbContext.Student
.SchoolContext
di ContosoUniversityContext
. Nome del contesto aggiornato: ContosoUniversity.Data.SchoolContext
Vengono installati automaticamente i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Se il passaggio precedente ha esito negativo, compilare il progetto e ripetere il passaggio di scaffolding.
Il processo di scaffolding:
Create.cshtml
e Create.cshtml.cs
Delete.cshtml
e Delete.cshtml.cs
Details.cshtml
e Details.cshtml.cs
Edit.cshtml
e Edit.cshtml.cs
Index.cshtml
e Index.cshtml.cs
Data/SchoolContext.cs
.Program.cs
.appsettings.json
.Lo strumento di scaffolding genera un stringa di connessione nel appsettings.json
file.
Il stringa di connessione specifica SQL Server LocalDB:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Local DB è una versione leggera del motore di database di SQL Server Express appositamente pensato per lo sviluppo di app e non per la produzione. Per impostazione predefinita, Local DB crea file con estensione mdf nella directory C:/Users/<user>
.
La classe principale che coordina la EF Core funzionalità per un determinato modello di dati è la classe di contesto del database. Il contesto è derivato da Microsoft.EntityFrameworkCore.DbContext. Il contesto specifica le entità incluse nel modello di dati. In questo progetto la classe è denominata SchoolContext
.
Aggiornare Data/SchoolContext.cs
con il codice seguente:
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Il codice precedente passa dal singolare DbSet<Student> Student
al plurale DbSet<Student> Students
. Per fare in modo che il Razor codice pages corrisponda al nuovo DBSet
nome, apportare una modifica globale da: _context.Student.
A: _context.Students.
Sono presenti 8 occorrenze.
Poiché un set di entità contiene più entità, molti sviluppatori preferiscono che i nomi delle DBSet
proprietà siano plurali.
Il codice evidenziato:
OnModelCreating
: SchoolContext
è stato inizializzato, ma prima che il modello sia stato protetto e usato per inizializzare il contesto.Student
avrà riferimenti alle altre entità.Ci auguriamo di risolvere questo problema in una versione futura.
ASP.NET Core viene compilato con l'inserimento di dipendenze. I servizi, ad esempio , vengono registrati con l'inserimento SchoolContext
delle dipendenze durante l'avvio dell'app. I componenti che richiedono questi servizi, ad esempio Razor Pages, vengono forniti tramite parametri del costruttore. Più avanti nell'esercitazione viene illustrato il codice del costruttore che ottiene un'istanza del contesto di database.
Lo strumento di scaffolding ha registrato automaticamente la classe di contesto nel contenitore di inserimento delle dipendenze.
Le righe evidenziate seguenti sono state aggiunte dallo scaffolder:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
Il nome della stringa di connessione viene passato al contesto chiamando un metodo in un oggetto DbContextOptions. Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge il stringa di connessione dal appsettings.json
file o appsettings.Development.json
.
Aggiungere AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint come illustrato nel codice seguente:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
Aggiungere il pacchetto NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .
Nella console di Gestione pacchetti immettere quanto segue per aggiungere il pacchetto NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Il Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
pacchetto NuGet fornisce ASP.NET middleware Core per le pagine di errore di Entity Framework Core. Questo middleware consente di rilevare e diagnosticare gli errori con le migrazioni di Entity Framework Core.
fornisce AddDatabaseDeveloperPageExceptionFilter
informazioni utili sull'errore nell'ambiente di sviluppo per gli errori delle migrazioni di Entity Framework.
Aggiornare Program.cs
per creare il database, se non esiste:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Il EnsureCreated metodo non esegue alcuna azione se esiste un database per il contesto. Se non esiste alcun database, vengono creati il database e lo schema. EnsureCreated
abilita il flusso di lavoro seguente per la gestione delle modifiche al modello di dati:
EmailAddress
.EnsureCreated
crea un database con il nuovo schema.Questo flusso di lavoro funziona all'inizio dello sviluppo quando lo schema è in rapida evoluzione, purché i dati non debbano essere mantenuti. La situazione è diversa quando è necessario mantenere i dati immessi nel database. In tal caso, usare le migrazioni.
Più avanti nella serie di esercitazioni viene eliminato il database creato da EnsureCreated
e vengono usate le migrazioni. Non è possibile aggiornare un database creato da EnsureCreated
usando le migrazioni.
Il metodo EnsureCreated
crea un database vuoto. In questa sezione viene aggiunto il codice che popola il database con dati di test.
Creare Data/DbInitializer.cs
con il codice seguente:
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Il codice controlla se esistono studenti nel database. Se non sono presenti studenti, aggiunge i dati di test al database. I dati di test vengono creati in matrici anziché in raccolte List<T>
per ottimizzare le prestazioni.
Program.cs
rimuovere //
dalla DbInitializer.Initialize
riga:using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
}
Arrestare l'app se è in esecuzione ed eseguire il comando seguente nella console di Gestione pacchetti:
Drop-Database -Confirm
Rispondere con Y
per eliminare il database.
Student
e lo schema della tabella Student
.La programmazione asincrona è la modalità predefinita per ASP.NET Core e EF Core.
Per un server Web è disponibile un numero limitato di thread e in situazioni di carico elevato tutti i thread disponibili potrebbero essere in uso. In queste circostanze il server non può elaborare nuove richieste finché i thread non saranno liberi. Con il codice sincrono, molti thread possono essere collegati mentre non stanno eseguendo operazioni perché sono in attesa del completamento dell'I/O. Con il codice asincrono, se un processo è in attesa del completamento dell'operazione I/O, il thread viene liberato e il server lo può usare per l'elaborazione di altre richieste. Di conseguenza, il codice asincrono consente un uso più efficiente delle risorse del server e il server può gestire una maggiore quantità di traffico senza ritardi.
Il codice asincrono comporta un minimo sovraccarico in fase di esecuzione. In caso di traffico ridotto, il calo delle prestazioni è irrilevante, mentre nelle situazioni di traffico elevato, è essenziale ottimizzare le prestazioni potenziali.
Nel codice seguente la parola chiave async, il valore restituito Task
, la parola chiave await
e il metodo ToListAsync
consentono di eseguire il codice in modo asincrono.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async
parola chiave indica al compilatore di: Task
rappresenta il lavoro in corso.await
indica al compilatore di suddividere il metodo in due parti. La prima parte termina con l'operazione avviata in modo asincrono. La seconda parte viene inserita in un metodo di callback che viene chiamato al termine dell'operazione.ToListAsync
è la versione asincrona del metodo di estensione ToList
.Alcuni aspetti da tenere presenti durante la scrittura di codice asincrono che usa EF Core:
ToListAsync
, SingleOrDefaultAsync
, FirstOrDefaultAsync
e SaveChangesAsync
. Non sono incluse le istruzioni che modificano solo un oggetto IQueryable
, ad esempio var students = context.Students.Where(s => s.LastName == "Davolio")
.Per altre informazioni sulla programmazione asincrona in .NET, vedere Panoramica della programmazione asincrona e Programmazione asincrona con async e await.
Avviso
L'implementazione asincrona di Microsoft.Data.SqlClient presenta alcuni problemi noti (#593, #601 e altri). Se si verificano problemi di prestazioni imprevisti, provare a usare l'esecuzione del comando di sincronizzazione, soprattutto quando si usano valori binari o di testo di grandi dimensioni.
In generale, una pagina Web non deve caricare un numero arbitrario di righe. Una query deve usare il paging o un approccio di limitazione. Ad esempio, la query precedente può usare Take
per limitare le righe restituite:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
L'enumerazione di una tabella di grandi dimensioni in una vista potrebbe restituire una risposta HTTP 200 parzialmente costruita se si verifica un'eccezione di database in modo parziale tramite l'enumerazione .
Il paging viene illustrato più avanti nell'esercitazione.
Per altre informazioni, vedere Considerazioni sulle prestazioni.For more information, see Performance considerations (EF).
Questa è la prima di una serie di esercitazioni che illustrano come usare Entity Framework (EF) Core in un'app ASP.NET Core Razor Pages . Con queste esercitazioni viene creato un sito Web per una fittizia Contoso University. Il sito include funzionalità, come ad esempio l'ammissione di studenti, la creazione di corsi e le assegnazioni degli insegnati. L'esercitazione usa l'approccio code first. Per informazioni su come seguire questa esercitazione usando il primo approccio al database, vedere questo problema di GitHub.
Scaricare o visualizzare l'app completata. Istruzioni per il download.
Le istruzioni per Visual Studio usano SQL Server Local DB, una versione di SQL Server Express eseguita solo in Windows.
Se si verifica un problema che non è possibile risolvere, confrontare il codice con il progetto completato. Un buon modo per ottenere assistenza consiste nel pubblicare una domanda per StackOverflow.com, usando il tag ASP.NET Core o il EF Core tag .
L'applicazione compilata in queste esercitazioni è il sito Web di base di un'università. Gli utenti possono visualizzare e aggiornare le informazioni che riguardano studenti, corsi e insegnanti. Di seguito sono disponibili alcune schermate dell'esercitazione.
Lo stile dell'interfaccia utente del sito è basato sui modelli di progetto predefiniti. L'obiettivo dell'esercitazione è come usare EF Core con ASP.NET Core, non su come personalizzare l'interfaccia utente.
Questo passaggio è facoltativo. La compilazione dell'app completata è consigliata quando si verificano problemi che non è possibile risolvere. Se si verifica un problema che non è possibile risolvere, confrontare il codice con il progetto completato. Istruzioni per il download.
Selezionare questa opzione ContosoUniversity.csproj
per aprire il progetto.
Update-Database
Eseguire il progetto per il seeding del database.
ContosoUniversity
per Nome progetto. È importante usare questo nome esatto, inclusa la combinazione di maiuscole e minuscole, quindi ogni namespace
corrispondenza viene eseguita quando viene copiato il codice.Copiare e incollare il codice seguente nel Pages/Shared/_Layout.cshtml
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2021 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
Il file di layout imposta l'intestazione, il piè di pagina e il menu del sito. Il codice precedente apporta le modifiche seguenti:
In Pages/Index.cshtml
sostituire il contenuto del file con il codice seguente:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
<a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
<a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
</p>
</div>
</div>
</div>
</div>
Il codice precedente sostituisce il testo relativo a ASP.NET Core con testo su questa app.
Eseguire l'app per verificare che venga visualizzata la home pagina.
Nelle sezioni seguenti viene descritto come creare un modello di dati:
Uno studente può iscriversi a un numero qualsiasi di corsi e un corso può avere un numero qualsiasi di studenti iscritti.
Creare una cartella Models nella cartella del progetto.
Creare Models/Student.cs
con il codice seguente:
using System;
using System.Collections.Generic;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà ID
diventa la colonna chiave primaria della tabella del database che corrisponde a questa classe. Per impostazione predefinita, EF Core interpreta una proprietà denominata ID
o classnameID
come chiave primaria. Il nome alternativo riconosciuto automaticamente per la chiave primaria della classe Student
è quindi StudentID
. Per altre informazioni, vedere EF Core - Chiavi.
La proprietà Enrollments
rappresenta una proprietà di navigazione. Le proprietà di navigazione contengono altre entità correlate a questa entità. In questo caso la proprietà Enrollments
di un'entità Student
contiene tutte le entità Enrollment
correlate a tale studente. Ad esempio, se una riga Student nel database presenta due righe Enrollment correlate, la proprietà di navigazione Enrollments
contiene questi due entità Enrollment.
Nel database una riga Enrollment è correlata a una riga Student se la relativa StudentID
colonna contiene il valore ID dello studente. Si supponga, ad esempio, che una riga Student abbia ID=1. Le righe di registrazione correlate avranno StudentID
= 1. StudentID
è una chiave esterna nella tabella Enrollment.
La proprietà Enrollments
è definita come ICollection<Enrollment>
perché potrebbero essere presenti più entità Enrollment correlate. È possibile usare altri tipi di raccolta, ad esempio List<Enrollment>
o HashSet<Enrollment>
. Quando ICollection<Enrollment>
viene utilizzato, EF Core crea una HashSet<Enrollment>
raccolta per impostazione predefinita.
Creare Models/Enrollment.cs
con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
La proprietà EnrollmentID
è la chiave primaria. Questa entità usa il modello classnameID
anziché ID
da solo. Per un modello di dati di produzione, molti sviluppatori scelgono un modello e lo usano in modo coerente. Questa esercitazione usa entrambi solo per illustrare che entrambi funzionano. L'uso di ID
senza classname
rende più semplice l'implementazione di alcuni tipi di modifiche al modello di dati.
La proprietà Grade
è un oggettoenum
. Il punto interrogativo dopo la dichiarazione del tipo Grade
indica che la proprietà Grade
ammette i valori Null. Un grado null è diverso da un grado zero. Null significa che un voto non è noto o non è ancora stato assegnato.
La proprietà StudentID
è una chiave esterna e la proprietà di navigazione corrispondente è Student
. Un'entità Enrollment
è associata a un'entità Student
, pertanto la proprietà contiene un'unica entità Student
.
La proprietà CourseID
è una chiave esterna e la proprietà di navigazione corrispondente è Course
. Un'entità Enrollment
è associata a un'entità Course
.
EF Core interpreta una proprietà come chiave esterna se è denominata <navigation property name><primary key property name>
. Ad esempio, StudentID
è la chiave esterna per la proprietà di navigazione Student
, dato che la chiave primaria Student
dell'entità è ID
. Le proprietà di chiave esterna possono anche essere denominate <primary key property name>
. Ad esempio, CourseID
poiché la chiave primaria Course
dell'entità è CourseID
.
Creare Models/Course.cs
con il codice seguente:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà Enrollments
rappresenta una proprietà di navigazione. È possibile correlare un'entità Course
a un numero qualsiasi di entità Enrollment
.
L'attributo DatabaseGenerated
consente all'app di specificare la chiave primaria anziché chiedere al database di generarla.
Compilare il progetto per verificare che non siano presenti errori del compilatore.
In questa sezione viene usato lo strumento di scaffolding ASP.NET Core per generare:
DbContext
. Il contesto è la classe principale che coordina le funzionalità di Entity Framework per un determinato modello di dati. Deriva dalla classe Microsoft.EntityFrameworkCore.DbContext.Student
.SchoolContext
di ContosoUniversityContext
. Nome del contesto aggiornato: ContosoUniversity.Data.SchoolContext
Se lo scaffolding non riesce con l'errore 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.'
, eseguire di nuovo lo strumento di scaffolding o vedere questo problema di GitHub.
Vengono installati automaticamente i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Se il passaggio precedente ha esito negativo, compilare il progetto e ripetere il passaggio di scaffolding.
Il processo di scaffolding:
Create.cshtml
e Create.cshtml.cs
Delete.cshtml
e Delete.cshtml.cs
Details.cshtml
e Details.cshtml.cs
Edit.cshtml
e Edit.cshtml.cs
Index.cshtml
e Index.cshtml.cs
Data/SchoolContext.cs
.Startup.cs
.appsettings.json
.Lo strumento di scaffolding genera un stringa di connessione nel appsettings.json
file.
Il stringa di connessione specifica SQL Server LocalDB:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=CU-1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Local DB è una versione leggera del motore di database di SQL Server Express appositamente pensato per lo sviluppo di app e non per la produzione. Per impostazione predefinita, Local DB crea file con estensione mdf nella directory C:/Users/<user>
.
La classe principale che coordina la EF Core funzionalità per un determinato modello di dati è la classe di contesto del database. Il contesto è derivato da Microsoft.EntityFrameworkCore.DbContext. Il contesto specifica le entità incluse nel modello di dati. In questo progetto la classe è denominata SchoolContext
.
Aggiornare Data/SchoolContext.cs
con il codice seguente:
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Il codice precedente passa dal singolare DbSet<Student> Student
al plurale DbSet<Student> Students
. Per fare in modo che il Razor codice pages corrisponda al nuovo DBSet
nome, apportare una modifica globale da: _context.Student.
A: _context.Students.
Sono presenti 8 occorrenze.
Poiché un set di entità contiene più entità, molti sviluppatori preferiscono che i nomi delle DBSet
proprietà siano plurali.
Il codice evidenziato:
OnModelCreating
: SchoolContext
è stato inizializzato, ma prima che il modello sia stato protetto e usato per inizializzare il contesto.Student
avrà riferimenti alle altre entità.Compilare il progetto per verificare che non siano presenti errori di compilazione.
ASP.NET Core viene compilato con l'inserimento di dipendenze. I servizi, ad esempio , vengono registrati con l'inserimento SchoolContext
delle dipendenze durante l'avvio dell'app. I componenti che richiedono questi servizi, ad esempio Razor Pages, vengono forniti tramite parametri del costruttore. Più avanti nell'esercitazione viene illustrato il codice del costruttore che ottiene un'istanza del contesto di database.
Lo strumento di scaffolding ha registrato automaticamente la classe di contesto nel contenitore di inserimento delle dipendenze.
Le righe evidenziate seguenti sono state aggiunte dallo scaffolder:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}
Il nome della stringa di connessione viene passato al contesto chiamando un metodo in un oggetto DbContextOptions. Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge il stringa di connessione dal appsettings.json
file.
Aggiungere AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint come illustrato nel codice seguente:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Aggiungere il pacchetto NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .
Nella console di Gestione pacchetti immettere quanto segue per aggiungere il pacchetto NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Il Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
pacchetto NuGet fornisce ASP.NET middleware Core per le pagine di errore di Entity Framework Core. Questo middleware consente di rilevare e diagnosticare gli errori con le migrazioni di Entity Framework Core.
fornisce AddDatabaseDeveloperPageExceptionFilter
informazioni utili sull'errore nell'ambiente di sviluppo per gli errori delle migrazioni di Entity Framework.
Aggiornare Program.cs
per creare il database, se non esiste:
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace ContosoUniversity
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
CreateDbIfNotExists(host);
host.Run();
}
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred creating the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Il EnsureCreated metodo non esegue alcuna azione se esiste un database per il contesto. Se non esiste alcun database, vengono creati il database e lo schema. EnsureCreated
abilita il flusso di lavoro seguente per la gestione delle modifiche al modello di dati:
EmailAddress
.EnsureCreated
crea un database con il nuovo schema.Questo flusso di lavoro funziona all'inizio dello sviluppo quando lo schema è in rapida evoluzione, purché i dati non debbano essere mantenuti. La situazione è diversa quando è necessario mantenere i dati immessi nel database. In tal caso, usare le migrazioni.
Più avanti nella serie di esercitazioni viene eliminato il database creato da EnsureCreated
e vengono usate le migrazioni. Non è possibile aggiornare un database creato da EnsureCreated
usando le migrazioni.
Il metodo EnsureCreated
crea un database vuoto. In questa sezione viene aggiunto il codice che popola il database con dati di test.
Creare Data/DbInitializer.cs
con il codice seguente:
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Il codice controlla se esistono studenti nel database. Se non sono presenti studenti, aggiunge i dati di test al database. I dati di test vengono creati in matrici anziché in raccolte List<T>
per ottimizzare le prestazioni.
In Program.cs
rimuovere //
dalla DbInitializer.Initialize
riga:
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
Arrestare l'app se è in esecuzione ed eseguire il comando seguente nella console di Gestione pacchetti:
Drop-Database -Confirm
Rispondere con Y
per eliminare il database.
Student
e lo schema della tabella Student
.La programmazione asincrona è la modalità predefinita per ASP.NET Core e EF Core.
Per un server Web è disponibile un numero limitato di thread e in situazioni di carico elevato tutti i thread disponibili potrebbero essere in uso. In queste circostanze il server non può elaborare nuove richieste finché i thread non saranno liberi. Con il codice sincrono, molti thread possono essere collegati mentre non stanno eseguendo operazioni perché sono in attesa del completamento dell'I/O. Con il codice asincrono, se un processo è in attesa del completamento dell'operazione I/O, il thread viene liberato e il server lo può usare per l'elaborazione di altre richieste. Di conseguenza, il codice asincrono consente un uso più efficiente delle risorse del server e il server può gestire una maggiore quantità di traffico senza ritardi.
Il codice asincrono comporta un minimo sovraccarico in fase di esecuzione. In caso di traffico ridotto, il calo delle prestazioni è irrilevante, mentre nelle situazioni di traffico elevato, è essenziale ottimizzare le prestazioni potenziali.
Nel codice seguente la parola chiave async, il valore restituito Task
, la parola chiave await
e il metodo ToListAsync
consentono di eseguire il codice in modo asincrono.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async
parola chiave indica al compilatore di: Task
rappresenta il lavoro in corso.await
indica al compilatore di suddividere il metodo in due parti. La prima parte termina con l'operazione avviata in modo asincrono. La seconda parte viene inserita in un metodo di callback che viene chiamato al termine dell'operazione.ToListAsync
è la versione asincrona del metodo di estensione ToList
.Alcuni aspetti da tenere presenti durante la scrittura di codice asincrono che usa EF Core:
ToListAsync
, SingleOrDefaultAsync
, FirstOrDefaultAsync
e SaveChangesAsync
. Non sono incluse le istruzioni che modificano solo un oggetto IQueryable
, ad esempio var students = context.Students.Where(s => s.LastName == "Davolio")
.Per altre informazioni sulla programmazione asincrona in .NET, vedere Panoramica della programmazione asincrona e Programmazione asincrona con async e await.
In generale, una pagina Web non deve caricare un numero arbitrario di righe. Una query deve usare il paging o un approccio di limitazione. Ad esempio, la query precedente può usare Take
per limitare le righe restituite:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
L'enumerazione di una tabella di grandi dimensioni in una vista potrebbe restituire una risposta HTTP 200 parzialmente costruita se si verifica un'eccezione di database in modo parziale tramite l'enumerazione .
MaxModelBindingCollectionSize il valore predefinito è 1024. Il codice seguente imposta MaxModelBindingCollectionSize
:
public void ConfigureServices(IServiceCollection services)
{
var myMaxModelBindingCollectionSize = Convert.ToInt32(
Configuration["MyMaxModelBindingCollectionSize"] ?? "100");
services.Configure<MvcOptions>(options =>
options.MaxModelBindingCollectionSize = myMaxModelBindingCollectionSize);
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
Vedere Configurazione per informazioni sulle impostazioni di configurazione, ad esempio MyMaxModelBindingCollectionSize
.
Il paging viene illustrato più avanti nell'esercitazione.
Per altre informazioni, vedere Considerazioni sulle prestazioni.For more information, see Performance considerations (EF).
La configurazione di registrazione viene comunemente fornita dalla sezione Logging
dei file appsettings.{Environment}.json
. Per registrare istruzioni SQL, aggiungere "Microsoft.EntityFrameworkCore.Database.Command": "Information"
al appsettings.Development.json
file:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"AllowedHosts": "*"
}
Con il codice JSON precedente, le istruzioni SQL vengono visualizzate nella riga di comando e nella finestra di output di Visual Studio.
Per altre informazioni, vedere Registrazione in .NET Core e ASP.NET Core e questo problema di GitHub.
Questa è la prima di una serie di esercitazioni che illustrano come usare Entity Framework (EF) Core in un'app ASP.NET Core Razor Pages . Con queste esercitazioni viene creato un sito Web per una fittizia Contoso University. Il sito include funzionalità, come ad esempio l'ammissione di studenti, la creazione di corsi e le assegnazioni degli insegnati. L'esercitazione usa l'approccio code first. Per informazioni su come seguire questa esercitazione usando il primo approccio al database, vedere questo problema di GitHub.
Scaricare o visualizzare l'app completata. Istruzioni per il download.
Le istruzioni per Visual Studio usano SQL Server Local DB, una versione di SQL Server Express eseguita solo in Windows.
Le istruzioni per Visual Studio Code usano SQLite, un motore di database multipiattaforma.
Se si sceglie di usare SQLite, scaricare e installare uno strumento di terze parti per la gestione e la visualizzazione di un database SQLite, ad esempio DB Browser per SQLite.
Se si verifica un problema che non è possibile risolvere, confrontare il codice con il progetto completato. Un buon modo per ottenere assistenza consiste nel pubblicare una domanda per StackOverflow.com, usando il tag ASP.NET Core o il EF Core tag .
L'applicazione compilata in queste esercitazioni è il sito Web di base di un'università. Gli utenti possono visualizzare e aggiornare le informazioni che riguardano studenti, corsi e insegnanti. Di seguito sono disponibili alcune schermate dell'esercitazione.
Lo stile dell'interfaccia utente del sito è basato sui modelli di progetto predefiniti. L'obiettivo dell'esercitazione è come usare EF Core, non su come personalizzare l'interfaccia utente.
Seguire il collegamento nella parte superiore della pagina per ottenere il codice sorgente per il progetto completato. La cartella cu30 contiene il codice per la versione di ASP.NET Core 3.0 dell'esercitazione. I file che riflettono lo stato del codice per le esercitazioni 1-7 si trovano nella cartella cu30snapshots.
Per eseguire l'app dopo il download del progetto completato:
Compilare il progetto.
Nella console di Gestione pacchetti eseguire il comando seguente:
Update-Database
Eseguire il progetto per il seeding del database.
Configurare l'intestazione, il piè di pagina e il menu del sito aggiornando Pages/Shared/_Layout.cshtml
:
Modificare tutte le occorrenze di "ContosoUniversity" in "Contoso University". Le occorrenze sono tre.
Eliminare le voci di Home menu e Privacy e aggiungere voci per About, Students, Courses, Instructors e Departments.
Le modifiche sono evidenziate.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2019 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
In Pages/Index.cshtml
sostituire il contenuto del file con il codice seguente per sostituire il testo relativo a ASP.NET Core con testo sull'app:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
<a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
<a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
</p>
</div>
</div>
</div>
</div>
Eseguire l'app per verificare che venga visualizzata la home pagina.
Nelle sezioni seguenti viene descritto come creare un modello di dati:
Uno studente può iscriversi a un numero qualsiasi di corsi e un corso può avere un numero qualsiasi di studenti iscritti.
Creare una cartella Models nella cartella del progetto.
Creare Models/Student.cs
con il codice seguente:
using System;
using System.Collections.Generic;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà ID
diventa la colonna chiave primaria della tabella del database che corrisponde a questa classe. Per impostazione predefinita, EF Core interpreta una proprietà denominata ID
o classnameID
come chiave primaria. Il nome alternativo riconosciuto automaticamente per la chiave primaria della classe Student
è quindi StudentID
. Per altre informazioni, vedere EF Core - Chiavi.
La proprietà Enrollments
rappresenta una proprietà di navigazione. Le proprietà di navigazione contengono altre entità correlate a questa entità. In questo caso la proprietà Enrollments
di un'entità Student
contiene tutte le entità Enrollment
correlate a tale studente. Ad esempio, se una riga Student nel database presenta due righe Enrollment correlate, la proprietà di navigazione Enrollments
contiene questi due entità Enrollment.
Nel database, una riga Enrollment è correlata a una riga Student se la relativa colonna StudentID contiene il valore ID dello studente. Si supponga, ad esempio, che una riga Student abbia ID=1. Le righe Enrollment correlate avranno StudentID = 1. StudentID è una chiave esterna nella tabella Enrollment.
La proprietà Enrollments
è definita come ICollection<Enrollment>
perché potrebbero essere presenti più entità Enrollment correlate. È possibile usare altri tipi di raccolta, ad esempio List<Enrollment>
o HashSet<Enrollment>
. Quando ICollection<Enrollment>
viene utilizzato, EF Core crea una HashSet<Enrollment>
raccolta per impostazione predefinita.
Creare Models/Enrollment.cs
con il codice seguente:
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
La proprietà EnrollmentID
è la chiave primaria. Questa entità usa il modello classnameID
anziché ID
da solo. Per un modello di dati di produzione, scegliere un modello e usarlo in modo coerente. Questa esercitazione usa entrambi solo per illustrare che entrambi funzionano. L'uso di ID
senza classname
rende più semplice l'implementazione di alcuni tipi di modifiche al modello di dati.
La proprietà Grade
è un oggettoenum
. Il punto interrogativo dopo la dichiarazione del tipo Grade
indica che la proprietà Grade
ammette i valori Null. Un grado null è diverso da un grado zero. Null significa che un voto non è noto o non è ancora stato assegnato.
La proprietà StudentID
è una chiave esterna e la proprietà di navigazione corrispondente è Student
. Un'entità Enrollment
è associata a un'entità Student
, pertanto la proprietà contiene un'unica entità Student
.
La proprietà CourseID
è una chiave esterna e la proprietà di navigazione corrispondente è Course
. Un'entità Enrollment
è associata a un'entità Course
.
EF Core interpreta una proprietà come chiave esterna se è denominata <navigation property name><primary key property name>
. Ad esempio, StudentID
è la chiave esterna per la proprietà di navigazione Student
, dato che la chiave primaria Student
dell'entità è ID
. Le proprietà di chiave esterna possono anche essere denominate <primary key property name>
. Ad esempio, CourseID
poiché la chiave primaria Course
dell'entità è CourseID
.
Creare Models/Course.cs
con il codice seguente:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La proprietà Enrollments
rappresenta una proprietà di navigazione. È possibile correlare un'entità Course
a un numero qualsiasi di entità Enrollment
.
L'attributo DatabaseGenerated
consente all'app di specificare la chiave primaria anziché chiedere al database di generarla.
Compilare il progetto per verificare che non siano presenti errori del compilatore.
In questa sezione si userà lo strumento di scaffolding di ASP.NET Core per generare:
Microsoft.EntityFrameworkCore.DbContext
.Student
.Vengono installati automaticamente i pacchetti seguenti:
Microsoft.VisualStudio.Web.CodeGeneration.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.Extensions.Logging.Debug
Microsoft.EntityFrameworkCore.Tools
Se si riscontra un problema con il passaggio precedente, compilare il progetto e riprovare a eseguire il passaggio di scaffolding.
Il processo di scaffolding:
Create.cshtml
e Create.cshtml.cs
Delete.cshtml
e Delete.cshtml.cs
Details.cshtml
e Details.cshtml.cs
Edit.cshtml
e Edit.cshtml.cs
Index.cshtml
e Index.cshtml.cs
Data/SchoolContext.cs
.Startup.cs
.appsettings.json
.Il appsettings.json
file specifica il stringa di connessione SQL Server LocalDB.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext6;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Local DB è una versione leggera del motore di database di SQL Server Express appositamente pensato per lo sviluppo di app e non per la produzione. Per impostazione predefinita, Local DB crea file con estensione mdf nella directory C:/Users/<user>
.
La classe principale che coordina la EF Core funzionalità per un determinato modello di dati è la classe di contesto del database. Il contesto è derivato da Microsoft.EntityFrameworkCore.DbContext. Il contesto specifica le entità incluse nel modello di dati. In questo progetto la classe è denominata SchoolContext
.
Aggiornare Data/SchoolContext.cs
con il codice seguente:
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Il codice evidenziato crea una DbSet<TEntity> proprietà per ogni set di entità. Nella EF Core terminologia:
Dato che un set di entità contiene più entità, le proprietà DBSet devono essere nomi plurali. Dato che lo strumento di scaffolding ha creato un DBSet Student
, questo passaggio lo modifica nel plurale Students
.
Per fare in modo che il Razor codice Pages corrisponda al nuovo nome DBSet, apportare una modifica globale nell'intero progetto di _context.Student
in _context.Students
. Sono presenti 8 occorrenze.
Compilare il progetto per verificare che non siano presenti errori di compilazione.
ASP.NET Core viene compilato con l'inserimento di dipendenze. I servizi ,ad esempio il contesto del EF Core database, vengono registrati con inserimento delle dipendenze durante l'avvio dell'applicazione. I componenti che richiedono questi servizi,ad esempio Razor Pages, vengono forniti tramite parametri del costruttore. Più avanti nell'esercitazione viene illustrato il codice del costruttore che ottiene un'istanza del contesto di database.
Lo strumento di scaffolding ha registrato automaticamente la classe di contesto nel contenitore di inserimento delle dipendenze.
In ConfigureServices
le righe evidenziate sono state aggiunte dallo scaffolder:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}
Il nome della stringa di connessione viene passato al contesto chiamando un metodo in un oggetto DbContextOptions. Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge il stringa di connessione dal appsettings.json
file.
Aggiornare Program.cs
per creare il database, se non esiste:
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace ContosoUniversity
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
CreateDbIfNotExists(host);
host.Run();
}
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred creating the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Il EnsureCreated metodo non esegue alcuna azione se esiste un database per il contesto. Se non esiste alcun database, vengono creati il database e lo schema. EnsureCreated
abilita il flusso di lavoro seguente per la gestione delle modifiche al modello di dati:
EmailAddress
.EnsureCreated
crea un database con il nuovo schema.Questo flusso di lavoro funziona correttamente nelle fasi iniziali dello sviluppo quando lo schema è in rapida evoluzione, purché non sia necessario conservare i dati. La situazione è diversa quando è necessario mantenere i dati immessi nel database. In tal caso, usare le migrazioni.
Più avanti nella serie di esercitazioni si vedrà come eliminare il database creato da EnsureCreated
e usare invece le migrazioni. Non è possibile aggiornare un database creato da EnsureCreated
usando le migrazioni.
Il metodo EnsureCreated
crea un database vuoto. In questa sezione viene aggiunto il codice che popola il database con dati di test.
Creare Data/DbInitializer.cs
con il codice seguente:
using ContosoUniversity.Data;
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Il codice controlla se esistono studenti nel database. Se non sono presenti studenti, aggiunge i dati di test al database. I dati di test vengono creati in matrici anziché in raccolte List<T>
per ottimizzare le prestazioni.
In Program.cs
sostituire la EnsureCreated
chiamata con una DbInitializer.Initialize
chiamata:
// context.Database.EnsureCreated();
DbInitializer.Initialize(context);
Arrestare l'app se è in esecuzione ed eseguire il comando seguente nella console di Gestione pacchetti:
Drop-Database
Riavviare l'app.
Selezionare la pagina Students per visualizzare i dati di seeding.
Student
e lo schema della tabella Student
.La programmazione asincrona è la modalità predefinita per ASP.NET Core e EF Core.
Per un server Web è disponibile un numero limitato di thread e in situazioni di carico elevato tutti i thread disponibili potrebbero essere in uso. In queste circostanze il server non può elaborare nuove richieste finché i thread non saranno liberi. Con il codice sincrono, può succedere che molti thread siano vincolati nonostante in quel momento non stiano eseguendo alcuna operazione. Rimangono tuttavia in attesa che l'operazione I/O sia completata. Con il codice asincrono, se un processo è in attesa del completamento dell'operazione I/O, il thread viene liberato e il server lo può usare per l'elaborazione di altre richieste. Di conseguenza, il codice asincrono consente un uso più efficiente delle risorse del server e il server può gestire una maggiore quantità di traffico senza ritardi.
Il codice asincrono comporta un minimo sovraccarico in fase di esecuzione. In caso di traffico ridotto, il calo delle prestazioni è irrilevante, mentre nelle situazioni di traffico elevato, è essenziale ottimizzare le prestazioni potenziali.
Nel codice seguente la parola chiave async, il valore restituito Task<T>
, la parola chiave await
e il metodo ToListAsync
consentono di eseguire il codice in modo asincrono.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async
parola chiave indica al compilatore di: Task<T>
rappresenta il lavoro in corso.await
indica al compilatore di suddividere il metodo in due parti. La prima parte termina con l'operazione avviata in modo asincrono. La seconda parte viene inserita in un metodo di callback che viene chiamato al termine dell'operazione.ToListAsync
è la versione asincrona del metodo di estensione ToList
.Alcuni aspetti da tenere presenti durante la scrittura di codice asincrono che usa EF Core:
ToListAsync
, SingleOrDefaultAsync
, FirstOrDefaultAsync
e SaveChangesAsync
. Non sono incluse le istruzioni che modificano solo un oggetto IQueryable
, ad esempio var students = context.Students.Where(s => s.LastName == "Davolio")
.Per altre informazioni sulla programmazione asincrona in .NET, vedere Panoramica della programmazione asincrona e Programmazione asincrona con async e await.
Feedback su ASP.NET Core
ASP.NET Core è un progetto di open source. Selezionare un collegamento per fornire feedback:
Eventi
19 nov, 23 - 21 nov, 23
Partecipa alle sessioni online di Microsoft Ignite create per espandere le tue competenze e aiutarti a risolvere i problemi complessi di oggi.
Iscriviti subito