Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Note
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Warning
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere .NET e .NET Core Support Policy. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
public decimal Price { get; set; }
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Il punto interrogativo dopo string indica che la proprietà è nullable. Per ulteriori informazioni, vedere tipi di riferimento nullable.
Aggiungere i pacchetti NuGet
Visual Studio installa automaticamente i pacchetti necessari.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Generare automaticamente le pagine film
Usare lo strumento di scaffolding per produrre Create, Read, Update e Delete (CRUD) pagine per il modello di film.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento scaffoldato.
Nella finestra di dialogo Aggiungi nuovo elemento con scaffolding:
- Nel riquadro sinistro, selezionare Installed>Common>MVC.
- Selezionare Controller MVC con visualizzazioni usando Entity Framework.
- Seleziona Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe Model, selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Nell'elenco a discesa provider Database selezionare SQL Server.
- Visualizzazioni e Nome controller: mantenere l'impostazione predefinita.
- Seleziona Aggiungi.
Se viene visualizzato un messaggio di errore, selezionare Aggiungi una seconda volta per riprovare.
Lo scaffolding aggiunge i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Lo scaffolding crea i seguenti elementi:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti ai pacchetti necessari nel file di progetto
MvcMovie.csproj. - Registra il contesto del database nel
Program.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine con scaffolding non possono essere ancora usate perché il database non esiste. L'esecuzione dell'app e la selezione del collegamento Movie App provoca un messaggio di errore Impossibile aprire il database oppure nessuna tabella chiamata: Movie.
Compilare l'app per verificare che non siano presenti errori.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere il comando seguente:
Add-Migration InitialCreate
-
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.
Viene visualizzato l'avviso seguente, risolto in un passaggio successivo:
Non è stato specificato alcun tipo di memorizzazione per la proprietà decimale 'Price' nel tipo di entità 'Movie'. I valori saranno troncati automaticamente senza avviso se non rientrano nella precisione e scala predefinite. Specificare in modo esplicito il tipo di colonna di SQL Server in grado di contenere tutti i valori in 'OnModelCreating' usando 'HasColumnType', specificare precisione e scala usando 'HasPrecision' o configurare un convertitore di valori con 'HasConversion'.
In PMC immettere il comando seguente:
Update-Database
-
Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Esegui l'app e seleziona il collegamento App Film.
Se si ottiene un'eccezione simile alla seguente, è possibile che il Update-Database comando non sia stato eseguito nel passaggio delle migrazioni:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; } = default!;
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
Iniezione delle dipendenze
ASP.NET Core viene compilato con dependency injection (DI). I servizi, come il contesto del database, vengono registrati con DI in Program.cs. Questi servizi vengono forniti ai componenti che li richiedono tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext") ?? throw new InvalidOperationException("Connection string 'MvcMovieContext' not found.")));
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-4ebefa10-de29-4dea-b2ad-8a8dc6bcf374;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave ConnectionString dal file appsettings.json.
Classe InitialCreate
Esaminare il file di Migrations/{timestamp}_InitialCreate.cs migrazione:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Iniezione delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore usa l'iniezione delle dipendenze per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico per gestire associazioni ritardate al fine di trasmettere informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella classe MoviesController e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:{PORT}/movies/details/1 imposta:
- Il controller al
moviescontroller, il primo segmento di URL. - Azione su
details, il secondo segmento di URL. - Il
idpassa a 1, l'ultimo segmento dell'URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:{PORT}/movies/details?id=1
Il id parametro è definito come tipo nullable (int?) nei casi in cui il valore id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati di route o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella visualizzazione Details.cshtml, ad esempio, il codice passa ogni campo del film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. I metodi e le viste Create e Edit passano anche un oggetto modello Movie.
Esaminare la Index.cshtml visualizzazione e il Index metodo nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Il codice restituisce i dettagli del problema se la Movie proprietà del contesto di dati è Null.
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La @model direttiva consente l'accesso all'elenco di film passati dal controller alla visualizzazione usando un Model oggetto fortemente tipizzato. Ad esempio, nella Index.cshtml visualizzazione il codice scorre i filmati con un'istruzione foreach sull'oggetto fortemente tipizzato Model :
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
public decimal Price { get; set; }
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Il punto interrogativo dopo string indica che la proprietà è nullable. Per ulteriori informazioni, vedere tipi di riferimento nullable.
Aggiungere i pacchetti NuGet
Visual Studio installa automaticamente i pacchetti necessari.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Eseguire lo scaffolding delle pagine dei film
Usare lo strumento di scaffolding per produrre Create, Read, Update, e Delete (CRUD) pagine per il modello film.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento scaffolding.
Nella finestra di dialogo Aggiungi nuovo elemento con scaffolding:
- Nel riquadro sinistro selezionare
- Selezionare Controller MVC con visualizzazioni usando Entity Framework.
- Seleziona Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe modello selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Nell'elenco a discesa provider Database selezionare SQL Server.
- Visualizzazioni e nome controller: mantenere l'impostazione predefinita.
- Seleziona Aggiungi.
Se viene visualizzato un messaggio di errore, selezionare Aggiungi una seconda volta per riprovare.
Lo scaffolding aggiunge i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Lo scaffolding crea quanto segue:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti necessari al pacchetto nel file di
MvcMovie.csprojprogetto. - Registra il contesto del database nel
Program.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine generate tramite scaffolding non possono essere utilizzate al momento perché il database non esiste. L'esecuzione dell'app e la selezione del collegamento Movie App comporta l'impossibilità di aprire un database o nessuna di queste tabelle: messaggio di errore movie.
Compilare l'app per verificare che non siano presenti errori.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere il comando seguente:
Add-Migration InitialCreate
-
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.
Viene visualizzato l'avviso seguente, risolto in un passaggio successivo:
Non è stato specificato alcun tipo di memorizzazione per la proprietà decimale 'Price' nel tipo di entità 'Movie'. I valori saranno troncati automaticamente senza avviso se non rientrano nella precisione e scala predefinite. Specificare in modo esplicito il tipo di colonna di SQL Server in grado di contenere tutti i valori in 'OnModelCreating' usando 'HasColumnType', specificare precisione e scala usando 'HasPrecision' o configurare un convertitore di valori con 'HasConversion'.
In PMC immettere il comando seguente:
Update-Database
-
Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Esegui l'app e seleziona il collegamento App Film.
Se si ottiene un'eccezione simile alla seguente, è possibile che il Update-Database comando non sia stato eseguito nel passaggio delle migrazioni:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; } = default!;
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
Iniezione delle dipendenze
ASP.NET Core viene compilato con dependency injection (DI). I servizi, come il contesto del database, vengono registrati con DI in Program.cs. Questi servizi vengono forniti ai componenti che li richiedono tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext") ?? throw new InvalidOperationException("Connection string 'MvcMovieContext' not found.")));
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-4ebefa10-de29-4dea-b2ad-8a8dc6bcf374;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave dal file appsettings.json.
Classe InitialCreate
Esaminare il file di migrazione Migrations/{timestamp}_InitialCreate.cs:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Iniezione delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore usa l'inserimento dipendenze per inserire il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico ad associazione tardiva per passare informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella MoviesController classe e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:{PORT}/movies/details/1 imposta:
- Il controller al controller
movies, rappresenta il primo segmento dell'URL. - Azione su
details, il secondo segmento di URL. - Da
ida 1, ultimo segmento di URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:{PORT}/movies/details?id=1
Il parametro id è definito come un tipo nullable (int?) nei casi in cui id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati del percorso o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella visualizzazione Details.cshtml, ad esempio, il codice passa ciascun campo relativo al film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. I metodi Create e Edit e le viste passano anche un oggetto modello Movie.
Esaminare la Index.cshtml visualizzazione e il Index metodo nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Il codice restituisce i dettagli del problema se la Movie proprietà del contesto di dati è Null.
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La @model direttiva consente l'accesso all'elenco di film passati dal controller alla visualizzazione usando un Model oggetto fortemente tipizzato. Ad esempio, nella Index.cshtml visualizzazione il codice scorre i filmati con un'istruzione foreach sull'oggetto fortemente tipizzato Model :
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
public decimal Price { get; set; }
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Il punto interrogativo dopo string indica che la proprietà è nullable. Per ulteriori informazioni, vedere tipi di riferimento nullable.
Aggiungere i pacchetti NuGet
Visual Studio installa automaticamente i pacchetti necessari.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Eseguire lo scaffolding delle pagine dei film
Usare lo strumento di scaffolding per produrre Create, Read, Update, e Delete (CRUD) pagine per il modello film.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento scaffolding.
Nella finestra di dialogo Aggiungi nuovo elemento Scaffolded:
- Nel riquadro sinistro, selezionare Installed >Common>MVC.
- Selezionare Controller MVC con visualizzazioni usando Entity Framework.
- Seleziona Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe modello selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Nell'elenco a discesa provider Database selezionare SQL Server.
- Visualizzazioni e nome controller: mantenere l'impostazione predefinita.
- Seleziona Aggiungi.
Se viene visualizzato un messaggio di errore, selezionare Aggiungi una seconda volta per riprovare.
Lo scaffolding aggiunge i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Lo scaffolding crea i seguenti elementi:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti necessari al pacchetto nel file
MvcMovie.csprojdel progetto. - Registra il contesto del database nel
Program.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine generate tramite scaffolding non possono essere utilizzate al momento perché il database non esiste. L'esecuzione dell'app e la selezione del collegamento Movie App si traducono in un messaggio di errore Impossibile aprire il database o nessuna tabella esistente: Movie.
Compilare l'app per verificare che non siano presenti errori.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere i comandi seguenti:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Il Update-Database comando genera l'avviso seguente:
Non è stato specificato alcun tipo di archiviazione per la proprietà decimale 'Price' nel tipo di entità 'Movie'. Ciò causerà il troncamento silenzioso dei valori se non si adattano alla precisione e all'intervallo predefiniti. Specificare in modo esplicito il tipo di colonna di SQL Server in grado di contenere tutti i valori in 'OnModelCreating' usando 'HasColumnType', specificare precisione e scala usando 'HasPrecision' o configurare un convertitore di valori con 'HasConversion'.
Ignorare l'avviso precedente, che viene risolto in un'esercitazione successiva.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Eseguire l'app e selezionare il collegamento Film App.
Se si ottiene un'eccezione simile alla seguente, è possibile che il Update-Database comando non sia stato eseguito nel passaggio delle migrazioni:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
Iniezione delle dipendenze
ASP.NET Core viene compilato con dependency injection (DI). I servizi, come il contesto del database, vengono registrati con DI in Program.cs. Questi servizi vengono forniti ai componenti che li richiedono tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave dal file appsettings.json.
Classe InitialCreate
Esaminare il file di migrazione Migrations/{timestamp}_InitialCreate.cs:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Iniezione delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore usa l'iniezione delle dipendenze per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico per gestire associazioni ritardate al fine di trasmettere informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella classe MoviesController e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:5001/movies/details/1 imposta:
- Il controller al
moviescontroller, il primo segmento di URL. - Azione su
details, il secondo segmento di URL. - Il
idpassa a 1, l'ultimo segmento dell'URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:5001/movies/details?id=1
Il id parametro è definito come tipo nullable (int?) nei casi in cui il valore id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati di route o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella vista Details.cshtml, il codice passa ogni campo del film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. Le viste e i metodi Create e Edit passano anche un oggetto modello Movie.
Esaminare la vista Index.cshtml e il metodo Index nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Il codice restituisce i dettagli del problema se la Movie proprietà del contesto di dati è Null.
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La direttiva @model consente l'accesso all'elenco di film che il controller ha passato alla vista utilizzando un oggetto Model fortemente tipizzato. Ad esempio, nella visualizzazione Index.cshtml, il codice scorre attraverso i film con un'istruzione foreach sull'oggetto fortemente tipizzato Model.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
public decimal Price { get; set; }
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Il punto interrogativo dopo string indica che la proprietà è nullable. Per ulteriori informazioni, vedere tipi di riferimento nullable.
Aggiungere i pacchetti NuGet
Visual Studio installa automaticamente i pacchetti necessari.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Eseguire lo scaffolding delle pagine Movie
Usare lo strumento di scaffolding per produrre le pagine Create, Read, Update e Delete (CRUD) per il modello di film.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento con scaffolding.
Nella finestra di dialogo Aggiungi Nuovo Elemento con Scaffolding:
- Nel riquadro sinistro, selezionare Installed>Common>MVC.
- Selezionare Controller MVC con visualizzazioni usando Entity Framework.
- Seleziona Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe modello selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Nell'elenco a discesa provider Database selezionare SQL Server.
- Visualizzazioni e Nome Controller: Mantieni l'impostazione predefinita.
- Seleziona Aggiungi.
Se viene visualizzato un messaggio di errore, selezionare Aggiungi una seconda volta per riprovare.
Lo scaffolding aggiunge i pacchetti seguenti:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Lo scaffolding crea i seguenti elementi:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti necessari del pacchetto nel file di progetto
MvcMovie.csproj. - Registra il contesto del database nel
Program.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine scaffoldate non possono ancora essere usate perché il database non esiste. Eseguire l'app e selezionare il collegamento Movie App risulta in un messaggio di errore come Impossibile aprire il database o nessuna tabella: Movie.
Compilare l'app per verificare che non siano presenti errori.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere i comandi seguenti:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Il Update-Database comando genera l'avviso seguente:
No type was specified for the decimal column 'Price' on entity type 'Movie'. (Nessun tipo specificato per la colonna decimale 'Price' nel tipo di entità 'Movie'). I valori saranno troncati automaticamente senza avviso se non rientrano nella precisione e scala predefinite. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Specificare in modo esplicito il tipo di colonna di SQL Server che può supportare tutti i valori usando 'HasColumnType()').
Ignorare l'avviso precedente, che viene risolto in un'esercitazione successiva.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Esegui l'app e seleziona il collegamento App Film.
Se si ottiene un'eccezione simile alla seguente, è possibile che il Update-Database comando non sia stato eseguito nel passaggio delle migrazioni:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
Iniezione delle dipendenze
ASP.NET Core viene compilato con dependency injection (DI). I servizi, come il contesto del database, vengono registrati con DI in Program.cs. Questi servizi vengono forniti ai componenti che li richiedono tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave ConnectionString dal file appsettings.json.
Classe InitialCreate
Esaminare il file di Migrations/{timestamp}_InitialCreate.cs migrazione:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Inserimento delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore utilizza la Dependency Injection per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico ad associazione tardiva per passare informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella MoviesController classe e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:5001/movies/details/1 imposta:
- Il controller al controller
movies, rappresenta il primo segmento dell'URL. - Azione su
details, il secondo segmento di URL. - Da
ida 1, ultimo segmento di URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:5001/movies/details?id=1
Il parametro id è definito come un tipo nullable (int?) nei casi in cui id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati del percorso o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella visualizzazione Details.cshtml, ad esempio, il codice passa ciascun campo relativo al film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. I metodi Create e Edit e le viste passano anche un oggetto modello Movie.
Esaminare la Index.cshtml visualizzazione e il Index metodo nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Il codice restituisce i dettagli del problema se la Movie proprietà del contesto di dati è Null.
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La @model direttiva consente l'accesso all'elenco di film passati dal controller alla visualizzazione usando un Model oggetto fortemente tipizzato. Ad esempio, nella Index.cshtml visualizzazione il codice scorre i filmati con un'istruzione foreach sull'oggetto fortemente tipizzato Model :
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models
{
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
public decimal Price { get; set; }
}
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Il punto interrogativo dopo string indica che la proprietà è nullable. Per ulteriori informazioni, vedere tipi di riferimento nullable.
Aggiungere i pacchetti NuGet
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console (PMC).
Nella console di Gestione pacchetti (PMC) eseguire il comando seguente:
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
I comandi precedenti aggiungono:
- Il provider EF Core SQL Server. Il pacchetto del provider installa il EF Core pacchetto come dipendenza.
- Le utilità utilizzate dai pacchetti installati automaticamente nella fase di scaffolding, più avanti nel tutorial.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Generare automaticamente le pagine film
Usare lo strumento di scaffolding per produrre Create, Read, Update e Delete (CRUD) pagine per il modello di film.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento scaffoldato.
Nella finestra di dialogo Aggiungi scaffolding selezionare Controller MVC con visualizzazioni, usando Entity Framework > Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe Model, selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Visualizzazioni e Nome controller: mantenere l'impostazione predefinita.
- Seleziona Aggiungi.
Se viene visualizzato un messaggio di errore, selezionare Aggiungi una seconda volta per riprovare.
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti ai pacchetti necessari nel file di progetto
MvcMovie.csproj. - Registra il contesto del database nel
Program.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
Lo scaffolding crea quanto segue:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine con scaffolding non possono essere ancora usate perché il database non esiste. L'esecuzione dell'app e la selezione del collegamento Movie App provoca un messaggio di errore Impossibile aprire il database oppure nessuna tabella chiamata: Movie.
Creare l'app
Compilazione dell'app. Il compilatore genera diversi avvisi sulla modalità null di gestione dei valori. Per altre informazioni, vedere < >questo problema di GitHub e
Per eliminare gli avvisi dai tipi di riferimento nullable, rimuovere la seguente riga dal file MvcMovie.csproj.
<Nullable>enable</Nullable>
Ci auguriamo di risolvere questo problema nella versione successiva.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere i comandi seguenti:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Il Update-Database comando genera l'avviso seguente:
No type was specified for the decimal column 'Price' on entity type 'Movie'. (Nessun tipo specificato per la colonna decimale 'Price' nel tipo di entità 'Movie'). I valori saranno troncati automaticamente senza avviso se non rientrano nella precisione e scala predefinite. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Specificare in modo esplicito il tipo di colonna di SQL Server che può supportare tutti i valori usando 'HasColumnType()').
Ignorare l'avviso precedente, che viene risolto in un'esercitazione successiva.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Esegui l'app e seleziona il collegamento App Film.
Se si ottiene un'eccezione simile alla seguente, è possibile che il passaggio delle migrazioni sia stato saltato:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
Iniezione delle dipendenze
ASP.NET Core viene compilato con dependency injection (DI). I servizi, come il contesto del database, vengono registrati con DI in Program.cs. Questi servizi vengono forniti ai componenti che li richiedono tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave ConnectionString dal file appsettings.json.
Classe InitialCreate
Esaminare il file di Migrations/{timestamp}_InitialCreate.cs migrazione:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Iniezione delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore usa l'iniezione delle dipendenze per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico per gestire associazioni ritardate al fine di trasmettere informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella classe MoviesController e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:5001/movies/details/1 imposta:
- Il controller al
moviescontroller, il primo segmento di URL. - Azione su
details, il secondo segmento di URL. - Il
idpassa a 1, l'ultimo segmento dell'URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:5001/movies/details?id=1
Il id parametro è definito come tipo nullable (int?) nei casi in cui il valore id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati di route o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella vista Details.cshtml, il codice passa ogni campo del film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. Le viste e i metodi Create e Edit passano anche un oggetto modello Movie.
Esaminare la vista Index.cshtml e il metodo Index nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La direttiva @model consente l'accesso all'elenco di film che il controller ha passato alla vista utilizzando un oggetto Model fortemente tipizzato. Ad esempio, nella visualizzazione Index.cshtml, il codice scorre attraverso i film con un'istruzione foreach sull'oggetto fortemente tipizzato Model.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Models/Movie.cs file con il codice seguente:
using System;
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models
{
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- L'utente non deve immettere le informazioni sull'ora nel campo data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Aggiungere i pacchetti NuGet
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console (PMC).
Nella console di Gestione pacchetti (PMC) eseguire il comando seguente:
Install-Package Microsoft.EntityFrameworkCore.Design
I comandi precedenti aggiungono:
- Il provider EF Core SQL Server. Il pacchetto del provider installa il EF Core pacchetto come dipendenza.
- Le utilità utilizzate dai pacchetti installati automaticamente nel passaggio di scaffolding saranno usate successivamente nel tutorial.
Compilare il progetto per controllare se sono presenti errori del compilatore.
Generare le pagine film
Usare lo strumento di scaffolding per produrre Create, Read, Update, e Delete (CRUD) per il modello del film.
In Esplora soluzioni fare clic con il tasto destro del mouse sulla cartella Controllers e selezionare Aggiungi > Nuovo elemento impalcato.
Nella finestra di dialogo Aggiungi scaffold selezionare Controller MVC con viste, utilizzando Entity Framework > Aggiungi.
Completare la finestra di dialogo Aggiungi controller MVC con visualizzazioni usando Entity Framework :
- Nell'elenco a discesa Classe modello, selezionare Movie (MvcMovie.Models).
- Nella riga Classe contesto di dati selezionare il segno più +.
- Nella finestra di dialogo Aggiungi contesto dati viene generato il nome della classe MvcMovie.Data.MvcMovieContext.
- Seleziona Aggiungi.
- Visualizzazioni e Nome Controller: Mantieni l'impostazione predefinita.
- Seleziona Aggiungi.
Lo scaffolding aggiorna quanto segue:
- Inserisce i riferimenti necessari al pacchetto nel file
MvcMovie.csprojdel progetto. - Registra il contesto del
Startup.ConfigureServicesdatabase nelStartup.csfile. - Aggiunge un stringa di connessione di database al file
appsettings.json.
Lo scaffolding genera il seguente:
- Un controller di film:
Controllers/MoviesController.cs -
Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index :
Views/Movies/*.cshtml - Classe di contesto del database:
Data/MvcMovieContext.cs
La creazione automatica di questi file e aggiornamenti dei file è nota come scaffolding.
Le pagine scaffoldate non possono essere ancora usate perché il database non esiste. L'esecuzione dell'applicazione e la selezione del collegamento Movie App generano il messaggio di errore Impossibile aprire il database o nessuna tabella: Movie.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Le migrazioni sono un set di strumenti che creano e aggiornano un database in modo che corrispondano al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console .
Nella console Gestione pacchetti immettere i comandi seguenti:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.
Il Update-Database comando genera l'avviso seguente:
No type was specified for the decimal column 'Price' on entity type 'Movie'. (Nessun tipo specificato per la colonna decimale 'Price' nel tipo di entità 'Movie'). Ciò causerà il troncamento silenzioso dei valori se non si adattano alla precisione e all'intervallo predefiniti. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Specificare in modo esplicito il tipo di colonna di SQL Server che può supportare tutti i valori usando 'HasColumnType()').
Ignorare l'avviso precedente, che viene risolto in un'esercitazione successiva.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Testare l'app
Esegui l'app e seleziona il collegamento App Film.
Se si ottiene un'eccezione simile alla seguente, è possibile che il passaggio delle migrazioni sia stato saltato:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.
Esaminare la classe di contesto del database generata e la registrazione
Con EF Core, l'accesso ai dati viene eseguito usando un modello. Un modello è costituito da classi di entità e da un contesto dell'oggetto che rappresenta una sessione con il database L'oggetto contesto consente di eseguire query e salvare i dati. Il contesto del database deriva da Microsoft. EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Lo scaffolding crea la classe di contesto del Data/MvcMovieContext.cs database:
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<Movie> Movie { get; set; }
}
}
Il codice precedente crea una proprietà DbSet<Movie> che rappresenta i film nel database.
ASP.NET Core viene compilato con dependency injection (DI). I servizi, ad esempio il contesto del database, devono essere registrati con l'inserimento delle dipendenze in Startup. I componenti che richiedono questi servizi vengono forniti tramite parametri del costruttore.
Nel Controllers/MoviesController.cs file, il costruttore usa l'Dependency Injection per passare il contesto del MvcMovieContext database nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Lo scaffolding ha generato il seguente codice evidenziato in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}
Il sistema di configurazione ASP.NET Core legge il stringa di connessione del database "MvcMovieContext".
Esaminare il stringa di connessione del database generato
Lo scaffolding ha aggiunto un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Per lo sviluppo locale, il sistema di configurazione ASP.NET Core legge la chiave ConnectionString dal file appsettings.json.
Classe InitialCreate
Esaminare il file di Migrations/{timestamp}_InitialCreate.cs migrazione:
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
Nel codice precedente:
-
InitialCreate.Upcrea la tabella Movie e configuraIdcome chiave primaria. -
InitialCreate.Downripristina le modifiche dello schema apportate dallaUpmigrazione.
Inserimento delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore utilizza la Dependency Injection per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Testare la pagina Create. Immettere e inviare i dati.
Testare le pagine Edit, Details e Delete.
Modelli fortemente tipizzati e direttiva @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico ad associazione tardiva per passare informazioni a una vista.
MVC consente di passare oggetti modello fortemente tipizzati a una visualizzazione. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Il meccanismo di scaffolding ha passato un modello fortemente tipizzato nella MoviesController classe e nelle viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio, https://localhost:5001/movies/details/1 imposta:
- Il controller al controller
movies, rappresenta il primo segmento dell'URL. - Azione su
details, il secondo segmento di URL. - Da
ida 1, ultimo segmento di URL.
L'oggetto id può essere passato con una stringa di query, come nell'esempio seguente:
https://localhost:5001/movies/details?id=1
Il parametro id è definito come un tipo nullable (int?) nei casi in cui id non viene specificato.
Un'espressione lambda viene passata al FirstOrDefaultAsync metodo per selezionare le entità film che corrispondono ai dati del percorso o al valore della stringa di query.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella visualizzazione Details.cshtml, ad esempio, il codice passa ciascun campo relativo al film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. I metodi Create e Edit e le viste passano anche un oggetto modello Movie.
Esaminare la Index.cshtml visualizzazione e il Index metodo nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La @model direttiva consente l'accesso all'elenco di film passati dal controller alla visualizzazione usando un Model oggetto fortemente tipizzato. Ad esempio, nella Index.cshtml visualizzazione il codice scorre i filmati con un'istruzione foreach sull'oggetto fortemente tipizzato Model :
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato come IEnumerable<Movie> oggetto , ogni elemento del ciclo viene tipizzato come Movie. Tra gli altri vantaggi, il compilatore convalida i tipi usati nel codice.
Registrazione SQL di Entity Framework Core
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 file appsettings.Development.json:
{
"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 Visual Studio.
Per altre informazioni, vedere le risorse seguenti:
- Registrazione in .NET e ASP.NET Core
-
Il modello di ASP.NET disabilita la registrazione EF Core SQL per impostazione predefinita (
dotnet/aspnetcore#32977)
Risorse aggiuntive
In questa esercitazione vengono aggiunte classi per la gestione dei film in un database. Queste classi sono la parte "Model" dell'app MVC.
Queste classi di modello vengono usate con Entity Framework Core (EF Core) per lavorare con un database. EF Core è un framework ORM (Object-Relational Mapping) che semplifica il codice di accesso ai dati da scrivere.
Le classi di modello create sono note come classi POCO, da Plain Old CLR Objects. Le classi POCO non hanno alcuna dipendenza da EF Core. Definiscono solo le proprietà dei dati da archiviare nel database.
In questa esercitazione vengono create prima le classi di modelli e EF Core viene creato il database.
Aggiungere una classe del modello di dati
Fare clic con il tasto destro del mouse sulla cartella Modelli>Aggiungi>Classe. Denominare il file Movie.cs.
Aggiornare il Movie.cs file con il codice seguente:
using System;
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models
{
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
La classe Movie contiene un campo Id, richiesto dal database per la chiave primaria.
L'attributo DataType su ReleaseDate specifica il tipo di dati (Date). Con questo attributo:
- l'utente non deve immettere le informazioni temporali nel campo della data.
- Viene visualizzata solo la data, non le informazioni temporali.
L'attributo DataAnnotations viene analizzato in un'esercitazione successiva.
Aggiungere i pacchetti NuGet
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console (PMC).
Nella console di Gestione pacchetti eseguire il comando seguente:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Il comando precedente aggiunge il provider EF Core SQL Server. Il pacchetto del provider installa il EF Core pacchetto come dipendenza. I pacchetti aggiuntivi vengono installati automaticamente nella fase di scaffolding più avanti nel tutorial.
Creare una classe di contesto di database
Per coordinare la funzionalità EF Core (Create, Read, Update, Delete) per il modello, è necessaria una classe di contesto del Movie database. Il contesto del database deriva da Microsoft.EntityFrameworkCore.DbContext e specifica le entità da includere nel modello di dati.
Creare una cartella Data.
Aggiungere un Data/MvcMovieContext.cs file con il codice seguente:
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<Movie> Movie { get; set; }
}
}
Il codice precedente crea una proprietà DbSet<Movie> per il set di entità. Nella terminologia di Entity Framework, un set di entità corrisponde in genere alla tabella di un database. Un'entità corrisponde a una riga nella tabella.
Registrare il contesto del database
ASP.NET Core viene compilato con dependency injection (DI). I servizi (come il contesto di database EF Core) devono essere registrati con DI 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. In questa sezione viene registrato il contesto di database nel contenitore DI.
Aggiungere le istruzioni seguenti using all'inizio di Startup.cs:
using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;
Aggiungere il codice evidenziato seguente in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}
Il nome del stringa di connessione viene passato al contesto chiamando un metodo su un oggetto DbContextOptions. Per lo sviluppo locale, il sistema di configurazione di ASP.NET Core legge la stringa di connessione dal file.
Esaminare il stringa di connessione del database
Aggiungere un stringa di connessione al file appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Compilare il progetto per controllare se sono presenti errori del compilatore.
Eseguire la generazione delle pagine dei film
Usare lo strumento di scaffolding per generare le pagine per le operazioni CRUD (creazione, lettura, aggiornamento ed eliminazione) per il modello Movie.
In Esplora soluzioni, clicca con il tasto destro sulla cartella Controllers e seleziona > Aggiungi > Nuovo Elemento Impalcato.
Nella finestra di dialogo Aggiungi scaffolding selezionare Controller MVC con visualizzazioni, usando Entity Framework > Aggiungi.
Completare la finestra di dialogo Aggiungi controller:
- Classe modello:Movie (MvcMovie.Models)
- Classe contesto dati:MvcMovieContext (MvcMovie.Data)
- Viste: mantenere il valore predefinito di ogni opzione selezionata
- Nome del controller: mantenere il valore predefinito MoviesController
- Seleziona Aggiungi
Visual Studio crea:
- Un controller di film (
Controllers/MoviesController.cs) - Razor visualizzare i file per le pagine Create, Delete, Details, Edit e Index (*Views/Movies/'.cshtml')
La creazione automatica di questi file è nota come scaffolding.
Non puoi ancora utilizzare le pagine generate automaticamente perché il database non esiste. Se si esegue l'app e si fa clic sul collegamento Movie App, si otterrà un messaggio di errore impossibile aprire il database o nessuna tabella denominata Movie.
Migrazione iniziale
Usare la EF Corefunzionalità Migrazioni per creare il database. Migrazioni è un set di strumenti che consentono di creare e aggiornare un database in modo che corrisponda al modello di dati.
Dal menu Tools selezionare NuGet Gestione pacchetti>Gestione pacchetti Console (PMC).
Nella Console di Gestione pacchetti (PMC) immettere i comandi seguenti:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: genera unMigrations/{timestamp}_InitialCreate.csfile di migrazione. L'argomentoInitialCreateè il nome della migrazione. È possibile usare qualsiasi nome, ma per convenzione viene selezionato un nome che descrive la migrazione. Trattandosi della prima migrazione, la classe generata contiene il codice per creare lo schema del database. Lo schema del database si basa sul modello specificato nella classeMvcMovieContext.Update-Database: aggiorna il database alla migrazione più recente, creata dal comando precedente. Questo comando esegue ilUpmetodo nelMigrations/{time-stamp}_InitialCreate.csfile , che crea il database.Il comando database update genera l'avviso seguente:
No type was specified for the decimal column 'Price' on entity type 'Movie'. (Nessun tipo specificato per la colonna decimale 'Price' nel tipo di entità 'Movie'). I valori saranno troncati automaticamente senza avviso se non rientrano nella precisione e scala predefinite. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Specificare in modo esplicito il tipo di colonna di SQL Server che può supportare tutti i valori usando 'HasColumnType()').
È possibile ignorare tale avviso. Verrà risolto in un'esercitazione successiva.
Per altre informazioni sugli strumenti PMC per EF Core, vedere EF Core tools reference - PMC in Visual Studio.
Classe InitialCreate
Esaminare il file di Migrations/{timestamp}_InitialCreate.cs migrazione:
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn),
Title = table.Column<string>(nullable: true),
ReleaseDate = table.Column<DateTime>(nullable: false),
Genre = table.Column<string>(nullable: true),
Price = table.Column<decimal>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
Il metodo Up crea la tabella Movie e configura Id come chiave primaria. Il metodo Down annulla le modifiche dello schema apportate dalla migrazione Up.
Testare l'app
Eseguire l'app e fare clic sul collegamento Movie App.
Se si ottiene un'eccezione simile a una delle seguenti:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Probabilmente hai saltato il passaggio delle migrazioni.
Testare la pagina Create. Immettere e inviare i dati.
Note
Potrebbe non essere possibile immettere virgole decimali nel campo
Price. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese (Stati Uniti), è necessario localizzare l'app. Per istruzioni sulla globalizzazione, vedere questo problema di GitHub.Testare le pagine Edit, Details e Delete.
Iniezione delle dipendenze nel controller
Aprire il Controllers/MoviesController.cs file ed esaminare il costruttore:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Il costruttore usa l'iniezione delle dipendenze per iniettare il contesto del database (MvcMovieContext) nel controller. Il contesto di database viene usato in ognuno dei metodi CRUD nel controller.
Modelli fortemente tipizzati e parola chiave @model
In un passaggio precedente di questa esercitazione è stato esaminato come un controller può passare oggetti o dati a una vista usando il dizionario ViewData. Il dizionario ViewData è un oggetto dinamico che fornisce un modo pratico per gestire associazioni ritardate al fine di trasmettere informazioni a una vista.
MVC consente anche di passare oggetti modello fortemente tipizzati a una vista. Questo approccio fortemente tipizzato consente il controllo del codice in fase di compilazione. Nel meccanismo di scaffolding è stato utilizzato questo approccio, ovvero il passaggio di un modello fortemente tipizzato, con la classe MoviesController e le viste.
Esaminare il metodo generato Details nel Controllers/MoviesController.cs file:
// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
In genere il parametro id viene passato come dati di route. Ad esempio https://localhost:5001/movies/details/1 imposta:
- Il controller verso il controller
movies(il primo segmento dell'URL). - L'azione su
details(secondo segmento di URL). - L'ID a 1 (l'ultimo segmento dell'URL).
È possibile anche passare id con una stringa di query nel modo seguente:
https://localhost:5001/movies/details?id=1
Il parametro id viene definito come tipo nullable (int?) nel caso in cui non venga fornito un valore ID.
Un'espressione lambda viene passata a FirstOrDefaultAsync per selezionare le entità film che corrispondono al valore della stringa di query o dei dati di route.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Se viene trovato un film, viene passata un'istanza del modello Movie alla vista Details:
return View(movie);
Esaminare il contenuto del Views/Movies/Details.cshtml file:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Movie</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
L'istruzione @model all'inizio del file di vista specifica il tipo di oggetto previsto dalla vista. Quando è stato creato il controller dei film, è stata inclusa l'istruzione @model seguente:
@model MvcMovie.Models.Movie
Questa direttiva @model consente di accedere al film che il controller ha passato alla vista. L'oggetto Model è fortemente tipizzato. Nella vista Details.cshtml, il codice passa ogni campo del film agli helper HTML DisplayNameFor e DisplayFor con l'oggetto fortemente tipizzato Model. Le viste e i metodi Create e Edit passano anche un oggetto modello Movie.
Esaminare la vista Index.cshtml e il metodo Index nel controller Movies. Si noti che il codice crea un oggetto List quando chiama il metodo View. Il codice passa questo elenco Movies dal metodo di azione Index alla vista:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Quando è stato creato il controller movies, lo scaffolding includeva l'istruzione seguente @model all'inizio del Index.cshtml file:
@model IEnumerable<MvcMovie.Models.Movie>
La direttiva @model consente di accedere all'elenco di film che il controller ha passato alla vista usando un oggetto Model fortemente tipizzato. Ad esempio, nella visualizzazione Index.cshtml, il codice scorre attraverso i film con un'istruzione foreach sull'oggetto fortemente tipizzato Model.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Poiché l'oggetto Model è fortemente tipizzato (come un oggetto IEnumerable<Movie>), ogni elemento nel ciclo viene tipizzato come Movie. Tra gli altri vantaggi, si ottiene un controllo del codice in fase di compilazione.