Поделиться через


Часть 4. Добавление модели в приложение MVC ASP.NET Core

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 9 этой статьи.

Авторы: Рик Андерсон (Rick Anderson) и Джон П. Смит (Jon P Smith)

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Models/Movie.cs Обновите файл со следующим кодом:

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; }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • Пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Знак вопроса после string указывает, что свойство допускает значение NULL. Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.

Добавление пакетов NuGet

Visual Studio автоматически устанавливает необходимые пакеты.

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне добавления нового шаблона элемента:

  • В левой области выберите "Установить>common>MVC".
  • Выберите контроллер MVC с представлениями с помощью Entity Framework.
  • Выберите Добавить.

Диалоговое окно

В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.

  • В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
  • В строке Класс контекста данных щелкните знак плюса (+).
    • В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.PagesMovieContext.
    • Выберите Добавить.
  • В раскрывающемся списке поставщика базы данных выберите SQL Server.
  • Представления и Имя контроллера: оставьте значения по умолчанию.
  • Выберите Добавить.

Добавление контекста данных: использование значений по умолчанию

Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.

Шаблон добавляет следующие пакеты:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

При формировании шаблонов выполняется создание таких объектов:

  • Контроллер фильмов: Controllers/MoviesController.cs
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index: Views/Movies/*.cshtml;
  • класс контекста для базы данных: Data/MvcMovieContext.cs.

При формировании шаблонов выполняются такие действия:

  • Вставляет необходимые MvcMovie.csproj ссылки на пакеты в файл проекта.
  • Регистрирует контекст базы данных в Program.cs файле.
  • добавление строки подключения к базе данных в файл appsettings.json.

Автоматическое создание этих файлов и их обновление называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).

Создайте приложение, чтобы убедиться, что ошибки отсутствуют.

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

В консоли диспетчер пакетов (PMC) введите следующую команду:

Add-Migration InitialCreate

  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

Появится следующее предупреждение, которое будет устранено на следующем шаге:

Тип хранилища не указан для десятичного свойства Price для типа сущности "Movie". Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. Явным образом укажите тип столбца SQL Server, который может содержать все значения в OnModelCreating с помощью HasColumnType, укажите точность и масштабирование с помощью HasPrecision или настройте преобразователь значений с помощью HasConversion.

В PMC введите следующую команду:

Update-Database

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Тестирование приложения

Запустите приложение и выберите ссылку Movie App.

Если вы получите исключение, аналогичное следующему, возможно, вы пропустили Update-Database команду на шаге миграции:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

Изучение созданного класса контекста базы данных, а также регистрации

При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.

Создание шаблонов создает класс контекста Data/MvcMovieContext.cs базы данных:

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!;
    }
}

Приведенный выше код создает свойство DbSet<Movie>, представляющее фильмы в базе данных.

Внедрение зависимостей

ASP.NET Core поддерживает внедрение зависимостей. Службы, такие как контекст базы данных, регистрируются в di Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.

В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

При формировании шаблонов был создан следующий выделенный код в 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.")));

Система конфигурации ASP.NET Core считывает строку подключения к базе данных MvcMovieContext.

Проверка созданной строки подключения к базе данных

При формировании шаблонов в файл 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"
  }
}

При локальной разработке система конфигурации ASP.NET Core считывает ключ ConnectionString из файла appsettings.json.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    /// <inheritdoc />
    public partial class InitialCreate : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

В предыдущем коде:

  • метод InitialCreate.Up создает таблицу Movie и настраивает Id в качестве первичного ключа;
  • метод InitialCreate.Down отменяет изменения схемы, внесенные при миграции Up.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Протестируйте страницу создания. Введите и отправьте данные.

Протестируйте страницы редактирования, сведений и удаления.

Строго типизированные модели и директива @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. Механизм формирования шаблонов передал строго типизированную модель в класс MoviesController и представления.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • контроллер к контроллеру movies первый сегмент URL-адреса;
  • действие для details, второй сегмент URL-адреса;
  • значение 1 для id, последний сегмент URL-адреса.

Параметр id можно передать с помощью строки запроса, как показано в следующем примере:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение id.

Лямбда-выражение передается в метод FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Код возвращает сведения о проблеме, если Movie свойство контекста данных имеет значение NULL.

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Так как объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.

Дополнительные ресурсы

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Models/Movie.cs Обновите файл со следующим кодом:

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; }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • Пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Знак вопроса после string указывает, что свойство допускает значение NULL. Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.

Добавление пакетов NuGet

Visual Studio автоматически устанавливает необходимые пакеты.

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне добавления нового шаблона элемента:

  • В левой области выберите "Установить>common>MVC".
  • Выберите контроллер MVC с представлениями с помощью Entity Framework.
  • Выберите Добавить.

Диалоговое окно

В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.

  • В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
  • В строке Класс контекста данных щелкните знак плюса (+).
    • В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.PagesMovieContext.
    • Выберите Добавить.
  • В раскрывающемся списке поставщика базы данных выберите SQL Server.
  • Представления и Имя контроллера: оставьте значения по умолчанию.
  • Выберите Добавить.

Добавление контекста данных: использование значений по умолчанию

Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.

Шаблон добавляет следующие пакеты:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

При формировании шаблонов выполняется создание таких объектов:

  • Контроллер фильмов: Controllers/MoviesController.cs
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index: Views/Movies/*.cshtml;
  • класс контекста для базы данных: Data/MvcMovieContext.cs.

При формировании шаблонов выполняются такие действия:

  • Вставляет необходимые MvcMovie.csproj ссылки на пакеты в файл проекта.
  • Регистрирует контекст базы данных в Program.cs файле.
  • добавление строки подключения к базе данных в файл appsettings.json.

Автоматическое создание этих файлов и их обновление называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).

Создайте приложение, чтобы убедиться, что ошибки отсутствуют.

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

Введите в консоли диспетчера пакетов (PMC) следующие команды:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

Команда Update-Database выдает следующее предупреждение:

Тип хранилища не указан для десятичного свойства Price для типа сущности "Movie". Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. Явным образом укажите тип столбца SQL Server, который может содержать все значения в OnModelCreating с помощью HasColumnType, укажите точность и масштабирование с помощью HasPrecision или настройте преобразователь значений с помощью HasConversion.

Игнорируйте предыдущее предупреждение, оно уже исправлено в следующем учебнике.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Тестирование приложения

Запустите приложение и выберите ссылку Movie App.

Если вы получите исключение, аналогичное следующему, возможно, вы пропустили Update-Database команду на шаге миграции:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

Изучение созданного класса контекста базы данных, а также регистрации

При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.

Создание шаблонов создает класс контекста Data/MvcMovieContext.cs базы данных:

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; }
    }
}

Приведенный выше код создает свойство DbSet<Movie>, представляющее фильмы в базе данных.

Внедрение зависимостей

ASP.NET Core поддерживает внедрение зависимостей. Службы, такие как контекст базы данных, регистрируются в di Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.

В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

При формировании шаблонов был создан следующий выделенный код в Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Система конфигурации ASP.NET Core считывает строку подключения к базе данных MvcMovieContext.

Проверка созданной строки подключения к базе данных

При формировании шаблонов в файл appsettings.json была добавлена строка подключения:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

При локальной разработке система конфигурации ASP.NET Core считывает ключ ConnectionString из файла appsettings.json.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

В предыдущем коде:

  • метод InitialCreate.Up создает таблицу Movie и настраивает Id в качестве первичного ключа;
  • метод InitialCreate.Down отменяет изменения схемы, внесенные при миграции Up.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Протестируйте страницу создания. Введите и отправьте данные.

Протестируйте страницы редактирования, сведений и удаления.

Строго типизированные модели и директива @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. Механизм формирования шаблонов передал строго типизированную модель в класс MoviesController и представления.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • контроллер к контроллеру movies первый сегмент URL-адреса;
  • действие для details, второй сегмент URL-адреса;
  • значение 1 для id, последний сегмент URL-адреса.

Параметр id можно передать с помощью строки запроса, как показано в следующем примере:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение id.

Лямбда-выражение передается в метод FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Код возвращает сведения о проблеме, если Movie свойство контекста данных имеет значение NULL.

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Так как объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.

Дополнительные ресурсы

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Models/Movie.cs Обновите файл со следующим кодом:

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; }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • Пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Знак вопроса после string указывает, что свойство допускает значение NULL. Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.

Добавление пакетов NuGet

Visual Studio автоматически устанавливает необходимые пакеты.

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне добавления нового шаблона элемента:

  • В левой области выберите "Установить>common>MVC".
  • Выберите контроллер MVC с представлениями с помощью Entity Framework.
  • Выберите Добавить.

Диалоговое окно

В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.

  • В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
  • В строке Класс контекста данных щелкните знак плюса (+).
    • В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.PagesMovieContext.
    • Выберите Добавить.
  • В раскрывающемся списке поставщика базы данных выберите SQL Server.
  • Представления и Имя контроллера: оставьте значения по умолчанию.
  • Выберите Добавить.

Добавление контекста данных: использование значений по умолчанию Если появится сообщение об ошибке, нажмите кнопку "Добавить второй раз", чтобы повторить попытку.

Шаблон добавляет следующие пакеты:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

При формировании шаблонов выполняется создание таких объектов:

  • Контроллер фильмов: Controllers/MoviesController.cs
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index: Views/Movies/*.cshtml;
  • класс контекста для базы данных: Data/MvcMovieContext.cs.

При формировании шаблонов выполняются такие действия:

  • Вставляет необходимые MvcMovie.csproj ссылки на пакеты в файл проекта.
  • Регистрирует контекст базы данных в Program.cs файле.
  • добавление строки подключения к базе данных в файл appsettings.json.

Автоматическое создание этих файлов и их обновление называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).

Создайте приложение, чтобы убедиться, что ошибки отсутствуют.

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

Введите в консоли диспетчера пакетов (PMC) следующие команды:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

Команда Update-Database выдает следующее предупреждение:

"Для десятичного столбца Price в типе сущности Movie не указан тип. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".

Игнорируйте предыдущее предупреждение, оно уже исправлено в следующем учебнике.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Тестирование приложения

Запустите приложение и выберите ссылку Movie App.

Если вы получите исключение, аналогичное следующему, возможно, вы пропустили Update-Database команду на шаге миграции:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

Изучение созданного класса контекста базы данных, а также регистрации

При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.

Создание шаблонов создает класс контекста Data/MvcMovieContext.cs базы данных:

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; }
    }
}

Приведенный выше код создает свойство DbSet<Movie>, представляющее фильмы в базе данных.

Внедрение зависимостей

ASP.NET Core поддерживает внедрение зависимостей. Службы, такие как контекст базы данных, регистрируются в di Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.

В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

При формировании шаблонов был создан следующий выделенный код в Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Система конфигурации ASP.NET Core считывает строку подключения к базе данных MvcMovieContext.

Проверка созданной строки подключения к базе данных

При формировании шаблонов в файл appsettings.json была добавлена строка подключения:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

При локальной разработке система конфигурации ASP.NET Core считывает ключ ConnectionString из файла appsettings.json.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

В предыдущем коде:

  • метод InitialCreate.Up создает таблицу Movie и настраивает Id в качестве первичного ключа;
  • метод InitialCreate.Down отменяет изменения схемы, внесенные при миграции Up.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Протестируйте страницу создания. Введите и отправьте данные.

Протестируйте страницы редактирования, сведений и удаления.

Строго типизированные модели и директива @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. Механизм формирования шаблонов передал строго типизированную модель в класс MoviesController и представления.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • контроллер к контроллеру movies первый сегмент URL-адреса;
  • действие для details, второй сегмент URL-адреса;
  • значение 1 для id, последний сегмент URL-адреса.

Параметр id можно передать с помощью строки запроса, как показано в следующем примере:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение id.

Лямбда-выражение передается в метод FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Код возвращает сведения о проблеме, если Movie свойство контекста данных имеет значение NULL.

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Так как объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.

Дополнительные ресурсы

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Models/Movie.cs Обновите файл со следующим кодом:

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; }
    }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • Пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Знак вопроса после string указывает, что свойство допускает значение NULL. Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.

Добавление пакетов NuGet

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов (PMC).

Меню PMC

В PMC выполните следующую команду:

Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Приведенные выше команды добавляют следующие компоненты:

  • EF Core Поставщик SQL Server. Пакет поставщика устанавливает EF Core пакет в качестве зависимости.
  • Программы, используемые пакетами, устанавливаются автоматически на этапе формирования шаблонов далее в этом учебнике.

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.

Диалоговое окно

В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.

  • В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
  • В строке Класс контекста данных щелкните знак плюса (+).
    • В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.PagesMovieContext.
    • Выберите Добавить.
  • Представления и Имя контроллера: оставьте значения по умолчанию.
  • Выберите Добавить.

Добавление контекста данных: использование значений по умолчанию

Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.

При формировании шаблонов выполняются такие действия:

  • Вставляет необходимые MvcMovie.csproj ссылки на пакеты в файл проекта.
  • Регистрирует контекст базы данных в Program.cs файле.
  • добавление строки подключения к базе данных в файл appsettings.json.

При формировании шаблонов выполняется создание таких объектов:

  • Контроллер фильмов: Controllers/MoviesController.cs
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index: Views/Movies/*.cshtml;
  • класс контекста для базы данных: Data/MvcMovieContext.cs.

Автоматическое создание этих файлов и их обновление называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).

Создание приложения

сборка приложения. Компилятор создает несколько предупреждений о том, как обрабатываются значения null. Дополнительные сведения см. в этом описании проблемы на GitHub, а также в статье Ссылочные типы, допускающие значение null.

Чтобы исключить предупреждения из ссылочных типов, допускающих значения NULL, удалите следующую строку из файла MvcMovie.csproj:

<Nullable>enable</Nullable>

Мы постараемся исправить эту проблему в следующем выпуске.

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

Введите в консоли диспетчера пакетов (PMC) следующие команды:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

Команда Update-Database выдает следующее предупреждение:

"Для десятичного столбца Price в типе сущности Movie не указан тип. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".

Игнорируйте предыдущее предупреждение, оно уже исправлено в следующем учебнике.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Тестирование приложения

Запустите приложение и выберите ссылку Movie App.

Если вы получаете такое исключение, как показано ниже, возможно, вы пропустили шаг миграций:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

Изучение созданного класса контекста базы данных, а также регистрации

При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.

Создание шаблонов создает класс контекста Data/MvcMovieContext.cs базы данных:

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; }
    }
}

Приведенный выше код создает свойство DbSet<Movie>, представляющее фильмы в базе данных.

Внедрение зависимостей

ASP.NET Core поддерживает внедрение зависимостей. Службы, такие как контекст базы данных, регистрируются в di Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.

В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

При формировании шаблонов был создан следующий выделенный код в Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Система конфигурации ASP.NET Core считывает строку подключения к базе данных MvcMovieContext.

Проверка созданной строки подключения к базе данных

При формировании шаблонов в файл appsettings.json была добавлена строка подключения:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

При локальной разработке система конфигурации ASP.NET Core считывает ключ ConnectionString из файла appsettings.json.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

В предыдущем коде:

  • метод InitialCreate.Up создает таблицу Movie и настраивает Id в качестве первичного ключа;
  • метод InitialCreate.Down отменяет изменения схемы, внесенные при миграции Up.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Протестируйте страницу создания. Введите и отправьте данные.

Протестируйте страницы редактирования, сведений и удаления.

Строго типизированные модели и директива @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. Механизм формирования шаблонов передал строго типизированную модель в класс MoviesController и представления.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • контроллер к контроллеру movies первый сегмент URL-адреса;
  • действие для details, второй сегмент URL-адреса;
  • значение 1 для id, последний сегмент URL-адреса.

Параметр id можно передать с помощью строки запроса, как показано в следующем примере:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение id.

Лямбда-выражение передается в метод FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Так как объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.

Дополнительные ресурсы

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Models/Movie.cs Обновите файл со следующим кодом:

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; }
    }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • Пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Добавление пакетов NuGet

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов (PMC).

Меню PMC

В PMC выполните следующую команду:

Install-Package Microsoft.EntityFrameworkCore.Design

Приведенные выше команды добавляют следующие компоненты:

  • EF Core Поставщик SQL Server. Пакет поставщика устанавливает EF Core пакет в качестве зависимости.
  • Программы, используемые пакетами, устанавливаются автоматически на этапе формирования шаблонов далее в этом учебнике.

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.

Диалоговое окно

В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.

  • В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
  • В строке Класс контекста данных щелкните знак плюса (+).
    • В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.PagesMovieContext.
    • Выберите Добавить.
  • Представления и Имя контроллера: оставьте значения по умолчанию.
  • Выберите Добавить.

Добавление контекста данных: использование значений по умолчанию

При формировании шаблонов выполняются такие действия:

  • Вставляет необходимые MvcMovie.csproj ссылки на пакеты в файл проекта.
  • Регистрирует контекст базы данных в Startup.ConfigureServices файла Startup.cs.
  • добавление строки подключения к базе данных в файл appsettings.json.

При формировании шаблонов выполняется создание таких объектов:

  • Контроллер фильмов: Controllers/MoviesController.cs
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index: Views/Movies/*.cshtml;
  • класс контекста для базы данных: Data/MvcMovieContext.cs.

Автоматическое создание этих файлов и их обновление называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

Введите в консоли диспетчера пакетов (PMC) следующие команды:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

Команда Update-Database выдает следующее предупреждение:

"Для десятичного столбца Price в типе сущности Movie не указан тип. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".

Игнорируйте предыдущее предупреждение, оно уже исправлено в следующем учебнике.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Тестирование приложения

Запустите приложение и выберите ссылку Movie App.

Если вы получаете такое исключение, как показано ниже, возможно, вы пропустили шаг миграций:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

Изучение созданного класса контекста базы данных, а также регистрации

При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.

Создание шаблонов создает класс контекста Data/MvcMovieContext.cs базы данных:

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; }
    }
}

Приведенный выше код создает свойство DbSet<Movie>, представляющее фильмы в базе данных.

ASP.NET Core поддерживает внедрение зависимостей. Службы, такие как контекст базы данных, должны быть зарегистрированы с помощью DI в Startup. Компоненты, которые используют эти службы, предоставляются через параметры конструктора.

В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

При формировании шаблонов был создан следующий выделенный код в Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

Система конфигурации ASP.NET Core считывает строку подключения к базе данных MvcMovieContext.

Проверка созданной строки подключения к базе данных

При формировании шаблонов в файл 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"
  }
}

При локальной разработке система конфигурации ASP.NET Core считывает ключ ConnectionString из файла appsettings.json.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

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");
    }
}

В предыдущем коде:

  • метод InitialCreate.Up создает таблицу Movie и настраивает Id в качестве первичного ключа;
  • метод InitialCreate.Down отменяет изменения схемы, внесенные при миграции Up.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Протестируйте страницу создания. Введите и отправьте данные.

Протестируйте страницы редактирования, сведений и удаления.

Строго типизированные модели и директива @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. Механизм формирования шаблонов передал строго типизированную модель в класс MoviesController и представления.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • контроллер к контроллеру movies первый сегмент URL-адреса;
  • действие для details, второй сегмент URL-адреса;
  • значение 1 для id, последний сегмент URL-адреса.

Параметр id можно передать с помощью строки запроса, как показано в следующем примере:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение id.

Лямбда-выражение передается в метод FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Так как объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.

Ведение журнала SQL Entity Framework Core

Конфигурация ведения журналов обычно предоставляется разделом Logging в файлах appsettings.{Environment}.json. Чтобы регистрировать инструкции SQL, добавьте "Microsoft.EntityFrameworkCore.Database.Command": "Information" в файл 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": "*"
}

В приведенном выше коде JSON инструкции SQL отображаются в командной строке и в окне вывода Visual Studio.

Дополнительные сведения см. в статье Ведение журнала в ASP.NET Core и описании этой проблемы GitHub.

Дополнительные ресурсы

В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляет уровень модели в приложении MVC.

Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.

Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.

В этом руководстве классы моделей создаются сначала и EF Core создают базу данных.

Добавление класса модели данных

Щелкните правой кнопкой мыши папку Models и выберите Добавить>Class (Класс). Назовите файл Movie.cs.

Movie.cs Обновите файл со следующим кодом:

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; }
    }
}

Класс Movie содержит поле Id, которое требуется базе данных в качестве первичного ключа.

Атрибут DataType для ReleaseDate указывает тип данных (Date). С этим атрибутом:

  • пользователю не требуется вводить сведения о времени в поле даты.
  • Отображается только дата, а не время.

DataAnnotations рассматриваются в следующем руководстве.

Добавление пакетов NuGet

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов (PMC).

Меню PMC

В PMC выполните следующую команду:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Предыдущая команда добавляет EF Core поставщика SQL Server. Пакет поставщика устанавливает EF Core пакет в качестве зависимости. Дополнительные пакеты устанавливаются автоматически на этапе формирования шаблонов далее в этом руководстве.

Создание класса контекста для базы данных

Класс контекста базы данных необходим для координации EF Core функций (создание, чтение, обновление, удаление) для Movie модели. Контекст базы данных является производным от Microsoft.EntityFrameworkCore.DbContext сущностей, которые необходимо включить в модель данных.

Создайте папку Data.

Добавьте файл Data/MvcMovieContext.cs с помощью следующего кода:

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; }
    }
}

Представленный выше код создает свойство DbSet<Movie> для набора сущностей. В терминологии Entity Framework набор сущностей обычно соответствует таблице базы данных. Сущность соответствует строке в таблице.

Регистрация контекста базы данных

ASP.NET Core поддерживает внедрение зависимостей. Службы (например EF Core , контекст базы данных) должны быть зарегистрированы в di во время запуска приложения. Затем компоненты, которые используют эти службы (например, Razor Pages), предоставляются через параметры конструктора. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве. В этом разделе контекст базы данных регистрируется в контейнере внедрения зависимостей.

Добавьте следующие инструкции using в начало файла Startup.cs:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Добавьте выделенный ниже код в Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json.

Просмотр строки подключения к базе данных

Добавьте строку подключения в файл 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"
  }
}

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.

Формирования шаблона страниц фильмов

Используйте средство формирования шаблонов, чтобы создать страницы для операций создания, чтения, обновления и удаления (CRUD) для модели фильма.

В Обозревателе решений щелкните правой кнопкой мыши папку Controllers и выберите Добавить > New Scaffolded Item (Создать шаблонный элемент).

представление указанного выше шага

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.

Диалоговое окно

Выполните необходимые действия в диалоговом окне Добавление контроллера:

  • Класс модели: Movie (MvcMovie.Models)
  • Класс контекста данных: MvcMovieContext (MvcMovie.Data)

Добавление контекста данных

  • Представление: оставьте флажки, установленные по умолчанию.
  • Имя контроллера: оставьте имя по умолчанию (MoviesController).
  • Выберите Добавить

Visual Studio создаст следующие компоненты:

  • контроллер movies (Controllers/MoviesController.cs);
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index (*Views/Movies/.cshtml).

Автоматическое создание этих файлов называется формированием шаблонов.

Сформированные страницы пока использовать нельзя, так как база данных не существует. Если запустить приложение и щелкнуть ссылку "Приложение фильма", вы получите базу данных "Не удается открыть" или нет такой таблицы: сообщение об ошибке "Фильм".

Первоначальная миграция

EF CoreИспользуйте функцию миграции для создания базы данных. Миграции — это набор средств, позволяющих создавать и обновлять базы данных в соответствии с моделью данных.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet>Консоль диспетчера пакетов (PMC).

В PMC введите следующие команды:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: создает Migrations/{timestamp}_InitialCreate.cs файл миграции. Аргумент InitialCreate — это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.

  • Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускает Up метод в Migrations/{time-stamp}_InitialCreate.cs файле, который создает базу данных.

    Команда обновления базы данных выдает следующее предупреждающее сообщение:

    "Для десятичного столбца Price в типе сущности Movie не указан тип. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".

    Это предупреждение можно игнорировать. Оно будет устранено в следующем руководстве.

Дополнительные сведения о средствах PMC смEF CoreEF Core. в справочнике по средствам PMC в Visual Studio.

Класс InitialCreate

Изучите Migrations/{timestamp}_InitialCreate.cs файл миграции:

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");
    }
}

Метод Up создает таблицу Movie и настраивает Id в качестве первичного ключа. Метод Down отменяет изменения схемы, внесенные миграцией Up.

Тестирование приложения

  • Запустите приложение и щелкните ссылку Movie App.

    Если возникнет исключение наподобие следующего:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

возможно, вы пропустили шаг миграции.

  • Протестируйте страницу создания. Введите и отправьте данные.

    Примечание.

    В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения. Инструкции по глобализации см. на сайте GitHub.

  • Протестируйте страницы редактирования, сведений и удаления.

Внедрение зависимостей в контроллере

Controllers/MoviesController.cs Откройте файл и проверьте конструктор:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Строго типизированные модели и ключевое слово @model

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.

Модель MVC также поддерживает передачу строго типизированных объектов модели в представление. Такой подход обеспечивает проверку кода во время компиляции. В этом подходе используется механизм формирования шаблонов (то есть передачи строго типизированной модели) с представлениями и классом MoviesController.

Изучите созданный метод Details в файле Controllers/MoviesController.cs:

// 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);
}

Параметр id обычно передается в качестве данных маршрута. Например, https://localhost:5001/movies/details/1 задает:

  • Контроллер movies (первый сегмент URL-адреса).
  • Действие details (второй сегмент URL-адреса).
  • Идентификатор 1 (последний сегмент URL-адреса).

Также можно передать id с помощью строки запроса следующим образом:

https://localhost:5001/movies/details?id=1

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение идентификатора.

Лямбда-выражение передается в FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Если фильм найден, экземпляр модели Movie передается в представление Details:

return View(movie);

Изучите содержимое файла Views/Movies/Details.cshtml:

@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>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением. При создании контроллера movie был включен следующий оператор @model:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление. Объект Model является строго типизированным. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor иDisplayFor со строго типизированным объектом Model. Методы Create и Edit и представления также передают объект модели Movie.

Изучите представление Index.cshtml и метод Indexв контроллере Movies. Обратите внимание на то, как в коде создается объект List при вызове метода View. Код передает список Movies из метода действия Index в представление:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Когда контроллер фильмов был создан, шаблон включал следующую @model инструкцию в верхней части Index.cshtml файла:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта 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>

Поскольку объект Model является строго типизированным (как и объект IEnumerable<Movie>), каждый элемент в цикле получает тип Movie. Помимо прочих преимуществ, это означает, что выполняется проверка кода во время компиляции.

Дополнительные ресурсы