Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Note
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 10.
Warning
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 10.
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Znak zapytania po string wskazuje, że właściwość może mieć wartość null. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null.
Dodawanie pakietów NuGet
Visual Studio automatycznie instaluje wymagane pakiety.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodaj nowy szablonowany element:
- W okienku po lewej stronie wybierz pozycję Zainstalowane>Common>MVC.
- Wybierz kontroler MVC z widokami przy użyciu Entity Framework.
- Wybierz Dodaj.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Na liście rozwijanej Dostawca bazy danych wybierz SQL Server.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Jeśli zostanie wyświetlony komunikat o błędzie, wybierz pozycję Dodaj po raz drugi, aby spróbować ponownie.
Skafold dodaje następujące pakiety:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Program.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane szkieletowaniem.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Skompiluj aplikację, aby sprawdzić, czy nie ma żadnych błędów.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenie:
Add-Migration InitialCreate
-
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.
Zostanie wyświetlone następujące ostrzeżenie, które zostało rozwiązane w późniejszym kroku:
Nie określono typu przechowywania dla właściwości dziesiętnej „Price” w typie jednostki „Movie”. Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny programu SQL Server, który może pomieścić wszystkie wartości w elemencie "OnModelCreating" przy użyciu polecenia "HasColumnType", określić precyzję i skalę przy użyciu polecenia "HasPrecision" lub skonfigurować konwerter wartości przy użyciu polecenia "HasConversion".
W usłudze PMC wprowadź następujące polecenie:
Update-Database
-
Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto Update-Database polecenie w kroku migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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!;
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
Wstrzykiwanie zależności
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, są rejestrowane przy użyciu DI w Program.cs. Te usługi są udostępniane składnikom, które wymagają ich za pośrednictwem parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku 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.")));
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku 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"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:{PORT}/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:{PORT}/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Kod zwraca szczegóły problemu, jeśli Movie właściwość kontekstu danych ma wartość null.
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Znak zapytania po string wskazuje, że właściwość może mieć wartość null. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null.
Dodawanie pakietów NuGet
Visual Studio automatycznie instaluje wymagane pakiety.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodaj nowy szablonowany element:
- W okienku po lewej stronie wybierz pozycję Zainstalowane>Common>MVC.
- Wybierz kontroler MVC z widokami przy użyciu Entity Framework.
- Wybierz Dodaj.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Na liście rozwijanej Dostawca bazy danych wybierz SQL Server.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Jeśli zostanie wyświetlony komunikat o błędzie, wybierz pozycję Dodaj po raz drugi, aby spróbować ponownie.
Skafold dodaje następujące pakiety:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Program.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane szkieletowaniem.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Skompiluj aplikację, aby sprawdzić, czy nie ma żadnych błędów.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenie:
Add-Migration InitialCreate
-
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.
Zostanie wyświetlone następujące ostrzeżenie, które zostało rozwiązane w późniejszym kroku:
Nie określono typu przechowywania dla właściwości dziesiętnej „Price” w typie jednostki „Movie”. Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny programu SQL Server, który może pomieścić wszystkie wartości w elemencie "OnModelCreating" przy użyciu polecenia "HasColumnType", określić precyzję i skalę przy użyciu polecenia "HasPrecision" lub skonfigurować konwerter wartości przy użyciu polecenia "HasConversion".
W usłudze PMC wprowadź następujące polecenie:
Update-Database
-
Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto Update-Database polecenie w kroku migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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!;
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
Wstrzykiwanie zależności
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, są rejestrowane przy użyciu DI w Program.cs. Te usługi są udostępniane składnikom, które wymagają ich za pośrednictwem parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku 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.")));
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku 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"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:{PORT}/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:{PORT}/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Kod zwraca szczegóły problemu, jeśli Movie właściwość kontekstu danych ma wartość null.
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Znak zapytania po string wskazuje, że właściwość może mieć wartość null. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null.
Dodawanie pakietów NuGet
Visual Studio automatycznie instaluje wymagane pakiety.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodaj nowy szablonowany element:
- W okienku po lewej stronie wybierz pozycję Zainstalowane>Common>MVC.
- Wybierz kontroler MVC z widokami przy użyciu Entity Framework.
- Wybierz Dodaj.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Na liście rozwijanej Dostawca bazy danych wybierz SQL Server.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Jeśli zostanie wyświetlony komunikat o błędzie, wybierz pozycję Dodaj po raz drugi, aby spróbować ponownie.
Skafold dodaje następujące pakiety:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Program.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane szkieletowaniem.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Skompiluj aplikację, aby sprawdzić, czy nie ma żadnych błędów.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenia:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Polecenie Update-Database generuje następujące ostrzeżenie:
Nie określono typu przechowywania dla właściwości dziesiętnej „Price” w typie jednostki „Movie”. Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny programu SQL Server, który może pomieścić wszystkie wartości w elemencie "OnModelCreating" przy użyciu polecenia "HasColumnType", określić precyzję i skalę przy użyciu polecenia "HasPrecision" lub skonfigurować konwerter wartości przy użyciu polecenia "HasConversion".
Zignoruj poprzednie ostrzeżenie, zostało naprawione w późniejszym samouczku.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto Update-Database polecenie w kroku migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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; }
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
Wstrzykiwanie zależności
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, są rejestrowane przy użyciu DI w Program.cs. Te usługi są udostępniane składnikom, które wymagają ich za pośrednictwem parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:5001/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:5001/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Kod zwraca szczegóły problemu, jeśli Movie właściwość kontekstu danych ma wartość null.
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Znak zapytania po string wskazuje, że właściwość może mieć wartość null. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null.
Dodawanie pakietów NuGet
Visual Studio automatycznie instaluje wymagane pakiety.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodaj nowy szablonowany element:
- W okienku po lewej stronie wybierz pozycję Zainstalowane>Common>MVC.
- Wybierz kontroler MVC z widokami przy użyciu Entity Framework.
- Wybierz Dodaj.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Na liście rozwijanej Dostawca bazy danych wybierz SQL Server.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Jeśli zostanie wyświetlony komunikat o błędzie, wybierz pozycję Dodaj po raz drugi, aby spróbować ponownie.
Skafold dodaje następujące pakiety:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Program.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane szkieletowaniem.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Skompiluj aplikację, aby sprawdzić, czy nie ma żadnych błędów.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenia:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Polecenie Update-Database generuje następujące ostrzeżenie:
Nie określono typu dla kolumny dziesiętnej "Price" w typie jednostki "Movie". Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny serwera SQL, który może pomieścić wszystkie wartości przy użyciu polecenia "HasColumnType()".
Zignoruj poprzednie ostrzeżenie, zostało naprawione w późniejszym samouczku.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto Update-Database polecenie w kroku migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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; }
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
Wstrzykiwanie zależności
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, są rejestrowane przy użyciu DI w Program.cs. Te usługi są udostępniane składnikom, które wymagają ich za pośrednictwem parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:5001/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:5001/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Kod zwraca szczegóły problemu, jeśli Movie właściwość kontekstu danych ma wartość null.
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Znak zapytania po string wskazuje, że właściwość może mieć wartość null. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null.
Dodawanie pakietów NuGet
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console (PMC).
W PMC uruchom następujące polecenie:
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Powyższe polecenia dodają:
- Dostawca EF Core SQL Server. Pakiet dostawczy instaluje pakiet EF Core jako zależność.
- Narzędzia używane przez pakiety zainstalowane automatycznie w kroku tworzenia szkieletu w dalszej części tego samouczka.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodawanie szkieletu wybierz pozycję Kontroler MVC z widokami, używając polecenia Entity Framework > Add.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Jeśli zostanie wyświetlony komunikat o błędzie, wybierz pozycję Dodaj po raz drugi, aby spróbować ponownie.
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Program.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane szkieletowaniem.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Tworzenie aplikacji
Kompilowanie aplikacji. Kompilator wygeneruje kilka ostrzeżeń dotyczących obsługiwania wartości null. Aby uzyskać więcej informacji, zobacz to zgłoszenie na GitHubie i Nullable reference types.
Aby wyeliminować ostrzeżenia z typów odwołań dopuszczanych do wartości null, usuń następujący wiersz z MvcMovie.csproj pliku:
<Nullable>enable</Nullable>
Mamy nadzieję rozwiązać ten problem w następnej wersji.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenia:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Polecenie Update-Database generuje następujące ostrzeżenie:
Nie określono typu dla kolumny dziesiętnej "Price" w typie jednostki "Movie". Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny serwera SQL, który może pomieścić wszystkie wartości przy użyciu polecenia "HasColumnType()".
Zignoruj poprzednie ostrzeżenie, zostało naprawione w późniejszym samouczku.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto krok migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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; }
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
Wstrzykiwanie zależności
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, są rejestrowane przy użyciu DI w Program.cs. Te usługi są udostępniane składnikom, które wymagają ich za pośrednictwem parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:5001/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:5001/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Models/Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Dodawanie pakietów NuGet
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console (PMC).
W PMC uruchom następujące polecenie:
Install-Package Microsoft.EntityFrameworkCore.Design
Powyższe polecenia dodają:
- Dostawca EF Core SQL Server. Pakiet dostawczy instaluje pakiet EF Core jako zależność.
- Narzędzia używane przez pakiety zainstalowane automatycznie w kroku tworzenia szkieletu w dalszej części tego samouczka.
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Narzędzie do generowania szablonów umożliwia wygenerowanie stron Create, Read, Update i Delete (CRUD) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodawanie szkieletu wybierz pozycję Kontroler MVC z widokami, używając polecenia Entity Framework > Add.
Uzupełnij okno dialogowe Dodaj kontroler MVC z widokami, używając Entity Framework:
- Z listy rozwijanej Klasa modelu wybierz pozycję Movie (MvcMovie.Models).
- W wierszu Klasy kontekstu danych wybierz znak + (plus).
- W oknie dialogowym Dodaj Kontekst Danych generowana jest nazwa klasy MvcMovie.Data.MvcMovieContext.
- Wybierz Dodaj.
- Widoki i Nazwa kontrolera: zachowaj wartość domyślną.
- Wybierz Dodaj.
Szkieletowanie aktualizuje następujące:
- Wstawia wymagane odwołania do pakietu w
MvcMovie.csprojpliku projektu. - Rejestruje kontekst bazy danych w
Startup.ConfigureServicesStartup.cspliku. - Dodaje łańcuch połączenia bazy danych do pliku
appsettings.json.
Tworzenie szkieletu powoduje następujące elementy:
- Kontroler filmów:
Controllers/MoviesController.cs -
Razor wyświetl pliki dla stron Tworzenie, Usuwanie, Szczegóły, Edytowanie i Indeks :
Views/Movies/*.cshtml - Klasa kontekstu bazy danych:
Data/MvcMovieContext.cs
Automatyczne tworzenie tych plików i aktualizacji plików jest nazywane generowaniem szkieletów.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Uruchomienie aplikacji i wybranie linku Movie App powoduje komunikat o błędzie Nie można otworzyć bazy danych lub brak takiej tabeli: Movie.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które tworzą i aktualizują bazę danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console .
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenia:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.
Polecenie Update-Database generuje następujące ostrzeżenie:
Nie określono typu dla kolumny dziesiętnej "Price" w typie jednostki "Movie". Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny serwera SQL, który może pomieścić wszystkie wartości przy użyciu polecenia "HasColumnType()".
Zignoruj poprzednie ostrzeżenie, zostało naprawione w późniejszym samouczku.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Testowanie aplikacji
Uruchom aplikację i wybierz link Aplikacja filmowa.
Jeśli wystąpi wyjątek podobny do poniższego, być może pominięto krok migracji:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych Price . Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.
Sprawdzanie wygenerowanej klasy kontekstu bazy danych i rejestracji
W EF Core dostęp do danych odbywa się przy użyciu modelu. Model składa się z klas jednostek i obiektu kontekstu, który reprezentuje sesję z bazą danych. Obiekt kontekstu umożliwia wykonywanie zapytań i zapisywanie danych. Kontekst bazy danych pochodzi z Microsoft. EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Tworzenie szkieletu Data/MvcMovieContext.cs powoduje utworzenie klasy kontekstu bazy danych:
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; }
}
}
Powyższy kod tworzy właściwość DbSet<Movie> reprezentującą filmy w bazie danych.
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi, takie jak kontekst bazy danych, muszą być zarejestrowane przy użyciu DI w Startup. Składniki, które wymagają tych usług, są dostarczane za pomocą parametrów konstruktora.
W pliku
Tworzenie szkieletu wygenerowało następujący wyróżniony kod w pliku Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}
System konfiguracji ASP.NET Core odczytuje ciąg połączenia bazy danych "MvcMovieContext".
Sprawdzenie wygenerowanego ciągu połączenia z bazą danych
Tworzenie szkieletu dodało ciąg połączenia do pliku 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"
}
}
W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje klucz ConnectionString z pliku appsettings.json.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
W przedstawionym wyżej kodzie:
-
InitialCreate.UpTworzy tabelę Movie i konfigurujeIdjako klucz podstawowy. -
InitialCreate.DownPrzywraca zmiany schematu wprowadzone przez migracjęUp.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Silnie typizowane modele i @model dyrektywa
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC umożliwia przekazywanie silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów przekazał silnie typizowany model w klasie MoviesController i widokach.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład https://localhost:5001/movies/details/1 ustawia:
- Kontroler do kontrolera
movies, pierwszy segment adresu URL. - Działanie dla
details, drugiego segmentu adresu URL. -
iddo 1, ostatni segment adresu URL.
Element id można przekazać za pomocą ciągu zapytania, jak w poniższym przykładzie:
https://localhost:5001/movies/details?id=1
Parametr id jest definiowany jako typ dopuszczający wartość null (int?) w przypadkach, gdy wartość id nie jest podana.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync metody, aby wybrać jednostki filmu zgodne z danymi trasy lub wartością ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów przekazywanych przez kontroler do widoku przy użyciu silnie typizowanego Model obiektu. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany jako obiekt IEnumerable<Movie>, każdy element w pętli ma typ Movie. Między innymi kompilator weryfikuje typy używane w kodzie.
Logowanie SQL w Entity Framework Core
Konfiguracja rejestrowania jest często dostarczana za pomocą sekcji Logging plików appsettings.{Environment}.json. Aby zarejestrować instrukcje SQL, dodaj "Microsoft.EntityFrameworkCore.Database.Command": "Information" do pliku 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": "*"
}
Z użyciem poprzedniego JSON instrukcje SQL są wyświetlane w wierszu poleceń i w oknie wyjściowym Visual Studio.
Aby uzyskać więcej informacji, zobacz następujące zasoby:
- Rejestrowanie w .NET i ASP.NET Core
- szablon ASP.NET wyłącza rejestrowanie EF Core SQL domyślnie (
dotnet/aspnetcore#32977)
Dodatkowe zasoby
W tym samouczku dodawane są klasy do zarządzania filmami w bazie danych. Te klasy są częścią "Model" aplikacji MVC.
Te klasy modeli są używane z Entity Framework Core (EF Core) do pracy z bazą danych. EF Core to struktura mapowania relacyjnego (ORM, object-relational mapping), która upraszcza kod dostępu do danych, który trzeba napisać.
Utworzone klasy modeli są nazywane klasami POCO z Plain Old CLR Objects. Klasy POCO nie są zależne od EF Core. Definiują tylko właściwości danych, które mają być przechowywane w bazie danych.
W tym samouczku najpierw tworzone są klasy modeli, a następnie EF Core tworzy bazę danych.
Dodawanie klasy modelu danych
Kliknij prawym przyciskiem myszy folder Models, dodaj>>. Nazwij plik Movie.cs.
Movie.cs Zaktualizuj plik przy użyciu następującego kodu:
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; }
}
}
Klasa Movie zawiera Id pole wymagane przez bazę danych dla klucza podstawowego.
Atrybut DataType on ReleaseDate określa typ danych (Date). Za pomocą tego atrybutu:
- Użytkownik nie musi wprowadzać informacji o godzinie w polu daty.
- Wyświetlana jest tylko data, a nie informacje o godzinie.
Adnotacje danych zostały omówione w późniejszym samouczku.
Dodawanie pakietów NuGet
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console (PMC).
W PMC uruchom następujące polecenie:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Poprzednie polecenie dodaje dostawcę EF Core SQL Server. Pakiet dostawczy instaluje pakiet EF Core jako zależność. Dodatkowe pakiety są instalowane automatycznie w kroku tworzenia szkieletu w dalszej części samouczka.
Tworzenie klasy kontekstu bazy danych
Klasa kontekstu bazy danych jest wymagana do koordynowania EF Core funkcjonalności (Tworzenie, odczyt, aktualizacja, usuwanie) dla Movie modelu. Kontekst bazy danych pochodzi z Microsoft.EntityFrameworkCore.DbContext i określa jednostki do uwzględnienia w modelu danych.
Utwórz folder Dane.
Data/MvcMovieContext.cs Dodaj plik z następującym kodem:
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; }
}
}
Powyższy kod tworzy właściwość DbSet<Movie> dla zestawu jednostek. W terminologii platformy Entity Framework zestaw jednostek zwykle odpowiada tabeli bazy danych. Jednostka odpowiada wierszowi w tabeli.
Rejestrowanie kontekstu bazy danych
ASP.NET Core jest zbudowany z użyciem wstrzykiwania zależności (DI). Usługi (takie jak kontekst bazy danych) muszą być zarejestrowane z di podczas uruchamiania EF Core aplikacji. Składniki, które wymagają tych usług (takich jak Razor Pages), są udostępniane za pomocą parametrów konstruktora. Kod konstruktora, który pobiera wystąpienie kontekstu bazy danych, jest pokazany później w samouczku. W tej sekcji zarejestrujesz kontekst bazy danych w kontenerze DI.
Dodaj następujące using instrukcje na górze Startup.cs:
using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;
Dodaj następujący wyróżniony kod w Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}
Nazwa parametry połączenia jest przekazywana do kontekstu przez wywołanie metody w obiekcie DbContextOptions. W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje parametry połączenia z pliku appsettings.json.
Zbadaj ciąg połączenia bazy danych
Dodaj parametry połączenia do pliku 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"
}
}
Skompiluj projekt jako sprawdzenie błędów kompilatora.
Strony filmów szablonowych
Użyj narzędzia do tworzenia szkieletów, aby generować strony CRUD (tworzenia, odczytu, aktualizacji i usuwania) dla modelu filmu.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy Controllers folder > Dodaj > nowy element szkieletowy.
W oknie dialogowym Dodawanie szkieletu wybierz pozycję Kontroler MVC z widokami, używając polecenia Entity Framework > Add.
Wypełnij okno dialogu Dodawanie kontrolera:
- Klasa modelu:Movie (MvcMovie.Models)
- Klasa kontekstu danych:MvcFilmContext (MvcFilm.Data)
- Widoki: pozostaw zaznaczoną wartość domyślną każdej opcji
- Nazwa kontrolera: Zachowaj domyślny kontroler MoviesController
- Wybierz Dodaj
Visual Studio tworzy:
- Kontroler filmów (
Controllers/MoviesController.cs) - Razor wyświetl pliki dla stron Utwórz, Usuń, Szczegóły, Edytuj i Indeks (*Views/Movies/`.cshtml`)
Automatyczne tworzenie tych plików jest nazywane tworzeniem szkieletów.
Nie można jeszcze używać stron szkieletowych, ponieważ baza danych nie istnieje. Jeśli uruchomisz aplikację i klikniesz link aplikacja filmowa, otrzymasz komunikat o błędzie Nie można otworzyć bazy danych lub nie ma takiej tabeli: Movie.
Migracja początkowa
EF Core Użyj funkcji Migracje, aby utworzyć bazę danych. Migracje to zestaw narzędzi, które umożliwiają tworzenie i aktualizowanie bazy danych w celu dopasowania ich do modelu danych.
Z menu Tools wybierz pozycję NuGet Menedżer pakietów>Menedżer pakietów Console (PMC).
W usłudze PMC wprowadź następujące polecenia:
Add-Migration InitialCreate
Update-Database
Add-Migration InitialCreate: generujeMigrations/{timestamp}_InitialCreate.csplik migracji. ArgumentemInitialCreatejest nazwa migracji. Można użyć dowolnej nazwy, ale zgodnie z konwencją wybrana jest nazwa opisującą migrację. Ponieważ jest to pierwsza migracja, wygenerowana klasa zawiera kod umożliwiający utworzenie schematu bazy danych. Schemat bazy danych jest oparty na modelu określonym wMvcMovieContextklasie.Update-Database: aktualizuje bazę danych do najnowszej migracji, która została utworzona przez poprzednie polecenie. To polecenie uruchamia metodęUpwMigrations/{time-stamp}_InitialCreate.cspliku, która tworzy bazę danych.Polecenie aktualizacji bazy danych generuje następujące ostrzeżenie:
Nie określono typu dla kolumny dziesiętnej "Price" w typie jednostki "Movie". Spowoduje to dyskretne obcięcie wartości, jeśli nie mieszczą się w domyślnej precyzji i skali. Jawnie określ typ kolumny serwera SQL, który może pomieścić wszystkie wartości przy użyciu polecenia "HasColumnType()".
Można zignorować to ostrzeżenie, zostanie ono rozwiązane w kolejnych samouczkach.
Aby uzyskać więcej informacji na temat narzędzi PMC dla EF Core, zobacz EF Core - dokumentację narzędzi PMC w Visual Studio.
Klasa InitialCreate
Migrations/{timestamp}_InitialCreate.cs Sprawdź plik migracji:
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");
}
}
Metoda Up tworzy tabelę Movie i konfiguruje Id jako klucz podstawowy. Metoda Down przywraca zmiany schematu wprowadzone przez migrację Up .
Testowanie aplikacji
Uruchom aplikację i kliknij link Movie App.
Jeśli wystąpi wyjątek podobny do jednego z następujących:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Prawdopodobnie pominięto etap migracji.
Przetestuj stronę Tworzenia. Wprowadź i prześlij dane.
Note
W polu może nie być możliwe wprowadzenie przecinków dziesiętnych
Price. Aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż anglojęzyczne, które stosują przecinek (",") jako separator dziesiętny oraz dla formatów dat innych niż amerykańskie, aplikacja musi zostać zglobalizowana. Aby uzyskać instrukcje dotyczące globalizacji, zobacz this GitHub problem.Przetestuj stronę Edytuj, stronę Szczegóły i stronę Usuwanie.
Wstrzykiwanie zależności w kontrolerze
Otwórz plik Controllers/MoviesController.cs i przeanalizuj konstruktor:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Konstruktor używa Dependency Injection, aby wstrzyknąć kontekst bazy danych (MvcMovieContext) do kontrolera. Kontekst bazy danych jest używany w każdej z metod CRUD w kontrolerze.
Silnie typizowane modele i @model słowo kluczowe
Wcześniej w tym samouczku pokazano, jak kontroler może przekazywać dane lub obiekty do widoku, używając słownika ViewData. Słownik ViewData jest obiektem dynamicznym, który zapewnia wygodny, opóźniony sposób przekazywania informacji do widoku.
MVC zapewnia również możliwość przekazywania silnie typizowanych obiektów modelu do widoku. Silnie typizowane podejście umożliwia sprawdzanie kodu w czasie kompilacji. Mechanizm tworzenia szkieletów używał tego podejścia (czyli przekazywania silnie typizowanego MoviesController modelu) z klasą i widokami.
Sprawdź wygenerowaną Details metodę w Controllers/MoviesController.cs pliku:
// 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);
}
Parametr id jest zwykle przekazywany jako dane trasy. Na przykład zestawy https://localhost:5001/movies/details/1:
- Kontroler do
movieskontrolera (pierwszy segment adresu URL). - Akcja na
details(drugi segment adresu URL). - Identyfikator na 1 (ostatni segment adresu URL).
Możesz również przekazać element id za pomocą ciągu zapytania w następujący sposób:
https://localhost:5001/movies/details?id=1
Parametr id jest definiowany jako typ nullable (int?) w przypadku, gdy nie podano wartości identyfikatora.
Wyrażenie lambda jest przekazywane do FirstOrDefaultAsync, aby wybrać jednostki filmu, które pasują do danych trasy lub wartości ciągu zapytania.
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.Id == id);
Jeśli zostanie znaleziony film, wystąpienie Movie modelu zostanie przekazane do Details widoku:
return View(movie);
Sprawdź zawartość Views/Movies/Details.cshtml pliku:
@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>
Instrukcja @model w górnej części pliku widoku określa typ obiektu, którego oczekuje widok. Po utworzeniu kontrolera filmu dołączono następującą @model instrukcję:
@model MvcMovie.Models.Movie
Ta @model dyrektywa umożliwia dostęp do filmu, który kontroler przekazał widokowi. Obiekt Model jest silnie typizowany. Na przykład w Details.cshtml widoku kod przekazuje każde pole filmu do DisplayNameFor Helperów HTML z DisplayFor silnie typowanym Model obiektem. Metody Create i Edit oraz widoki również przekazują Movie obiekt modelu.
Sprawdź widok Index.cshtml i metodę Index w kontrolerze Filmy. Zwróć uwagę, że kod tworzy List obiekt, gdy wywołuje metodę View . Kod przekazuje tę Movies listę z Index metody akcji do widoku:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
Po utworzeniu kontrolera filmów szkielet zawiera następującą @model instrukcję w górnej Index.cshtml części pliku:
@model IEnumerable<MvcMovie.Models.Movie>
Dyrektywa @model umożliwia dostęp do listy filmów, które kontroler przekazał do widoku, przy użyciu silnie typizowanego obiektu Model. Na przykład w widoku Index.cshtml kod przechodzi przez filmy za pomocą instrukcji foreach dla silnie typizowanego obiektu 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>
Ponieważ obiekt Model jest silnie typowany (jako obiekt IEnumerable<Movie>), każdy element w pętli jest określony jako Movie. Między innymi oznacza to, że uzyskujesz sprawdzanie czasu kompilacji kodu.