Часть 4. Добавление модели в приложение MVC ASP.NET CorePart 4, add a model to an ASP.NET Core MVC app

Авторы: Рик Андерсон (Rick Anderson) и Том Дайкстра (Tom Dykstra)By Rick Anderson and Tom Dykstra

В этом разделе мы добавим классы для управления фильмами в базе данных.In this section, you add classes for managing movies in a database. Эти классы будут представлять уровень м одели в приложении M VC.These classes will be the "M odel" part of the M VC app.

Эти классы используются в Entity Framework Core (EF Core) для работы с базой данных.You use these classes with Entity Framework Core (EF Core) to work with a database. EF Core —это платформа объектно реляционного сопоставления (ORM), которая позволяет упростить необходимый код доступа к данным.EF Core is an object-relational mapping (ORM) framework that simplifies the data access code that you have to write.

Создаваемые вами классы моделей называются классами POCO (от P lain O ld C LR O — старые добрые объекты CLR), так как они не зависят от EF Core.The model classes you create are known as POCO classes (from P lain O ld C LR O bjects) because they don't have any dependency on EF Core. Эти классы просто определяют свойства данных, которые будут храниться в базе данных.They just define the properties of the data that will be stored in the database.

Работая с этим учебником, вы напишете классы моделей, а затем EF Core создаст базу данных.In this tutorial, you write the model classes first, and EF Core creates the database.

Добавление класса модели данныхAdd a data model class

Щелкните правой кнопкой мыши папку Models и выберите пункт Добавить > Класс.Right-click the Models folder > Add > Class. Назовите файл Movie.cs.Name the file Movie.cs.

Обновите файл Movie.cs, используя следующий код:Update the Movie.cs file with the following code:

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, которое требуется базе данных в качестве первичного ключа.The Movie class contains an Id field, which is required by the database for the primary key.

Атрибут DataType для ReleaseDate указывает тип данных (Date).The DataType attribute on ReleaseDate specifies the type of the data (Date). С этим атрибутом:With this attribute:

  • пользователю не требуется вводить сведения о времени в поле даты.The user is not required to enter time information in the date field.
  • Отображается только дата, а не время.Only the date is displayed, not time information.

DataAnnotations рассматриваются в следующем руководстве.DataAnnotations are covered in a later tutorial.

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

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet > Консоль диспетчера пакетов (PMC).From the Tools menu, select NuGet Package Manager > Package Manager Console (PMC).

Меню PMC

В PMC выполните следующую команду:In the PMC, run the following command:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Приведенная выше команда добавляет поставщик EF Core для SQL Server.The preceding command adds the EF Core SQL Server provider. Пакет поставщика устанавливает пакет EF Core в качестве зависимости.The provider package installs the EF Core package as a dependency. Дополнительные пакеты устанавливаются автоматически на этапе формирования шаблонов далее в этом руководстве.Additional packages are installed automatically in the scaffolding step later in the tutorial.

Создание класса контекста для базы данныхCreate a database context class

Класс контекста базы данных необходим в целях координации функциональных возможностей EF Core (создание, чтение, обновление, удаление) для модели Movie.A database context class is needed to coordinate EF Core functionality (Create, Read, Update, Delete) for the Movie model. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.The database context is derived from Microsoft.EntityFrameworkCore.DbContext and specifies the entities to include in the data model.

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

Добавьте файл Data/MvcMovieContext.cs со следующим кодом:Add a Data/MvcMovieContext.cs file with the following code:

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> для набора сущностей.The preceding code creates a DbSet<Movie> property for the entity set. В терминологии Entity Framework набор сущностей обычно соответствует таблице базы данных.In Entity Framework terminology, an entity set typically corresponds to a database table. Сущность соответствует строке в таблице.An entity corresponds to a row in the table.

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

ASP.NET Core поддерживает внедрение зависимостей.ASP.NET Core is built with dependency injection (DI). Службы (например, контекст базы данных EF Core) должны регистрироваться с помощью внедрения зависимостей во время запуска приложения.Services (such as the EF Core DB context) must be registered with DI during application startup. Затем компоненты, которые используют эти службы (например, Razor Pages), обращаются к ним через параметры конструктора.Components that require these services (such as Razor Pages) are provided these services via constructor parameters. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.The constructor code that gets a DB context instance is shown later in the tutorial. В этом разделе контекст базы данных регистрируется в контейнере внедрения зависимостей.In this section, you register the database context with the DI container.

Добавьте следующие инструкции using в начало файла Startup.cs.Add the following using statements at the top of Startup.cs:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Добавьте выделенный ниже код в Startup.ConfigureServices:Add the following highlighted code in Startup.ConfigureServices:

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

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

Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Добавление строки подключения базы данныхAdd a database connection string

Добавьте строку подключения в файл appsettings.json :Add a connection string to the appsettings.json file:

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

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.Build the project as a check for compiler errors.

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

Используйте средство формирования шаблонов, чтобы создать страницы для операций создания, чтения, обновления и удаления (CRUD) для модели фильма.Use the scaffolding tool to produce Create, Read, Update, and Delete (CRUD) pages for the movie model.

В Обозревателе решений щелкните правой кнопкой мыши папку Контроллеры и выберите Добавить > Создать шаблонный элемент.In Solution Explorer, right-click the Controllers folder > Add > New Scaffolded Item.

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

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.In the Add Scaffold dialog, select MVC Controller with views, using Entity Framework > Add.

Диалоговое окно "Добавление шаблона"

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

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

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

  • Представления: оставьте флажки, установленные по умолчаниюViews: Keep the default of each option checked
  • Имя контроллера: оставьте имя по умолчанию (MoviesController)Controller name: Keep the default MoviesController
  • Нажмите ДобавитьSelect Add

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

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

Автоматическое создание этих файлов называется формированием шаблонов.The automatic creation of these files is known as scaffolding.

Сформированные страницы пока использовать нельзя, так как база данных не существует.You can't use the scaffolded pages yet because the database doesn't exist. Если запустить приложение и щелкнуть ссылку Movie App, появится сообщение об ошибке Невозможно открыть базу данных или отсутствует таблица: Movie.If you run the app and click on the Movie App link, you get a Cannot open database or no such table: Movie error message.

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

Создайте базу данных с помощью функции миграций EF Core.Use the EF Core Migrations feature to create the database. Миграции — это набор средств, позволяющих создавать и обновлять базы данных в соответствии с моделью данных.Migrations is a set of tools that let you create and update a database to match your data model.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet > Консоль диспетчера пакетов (PMC).From the Tools menu, select NuGet Package Manager > Package Manager Console (PMC).

В PMC введите следующие команды:In the PMC, enter the following commands:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate. Создает файл миграции Migrations/{метка_времени}_InitialCreate.cs.Add-Migration InitialCreate: Generates a Migrations/{timestamp}_InitialCreate.cs migration file. Аргумент InitialCreate — это имя миграции.The InitialCreate argument is the migration name. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию.Any name can be used, but by convention, a name is selected that describes the migration. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных.Because this is the first migration, the generated class contains code to create the database schema. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.The database schema is based on the model specified in the MvcMovieContext class.

  • Update-Database. Обновляет базу данных до последней миграции, созданной предыдущей командой.Update-Database: Updates the database to the latest migration, which the previous command created. Эта команда выполняет метод Up в файле Migrations/{метка_времени}_InitialCreate.cs, который создает базу данных.This command runs the Up method in the Migrations/{time-stamp}_InitialCreate.cs file, which creates the database.

    Команда обновления базы данных выдает следующее предупреждающее сообщение:The database update command generates the following warning:

    "Для десятичного столбца Price в типе сущности Movie не указан тип.No type was specified for the decimal column 'Price' on entity type 'Movie'. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию.This will cause values to be silently truncated if they do not fit in the default precision and scale. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'.

    Это предупреждение можно игнорировать. Оно будет устранено в следующем руководстве.You can ignore that warning, it will be fixed in a later tutorial.

Дополнительные сведения о средствах PMC для EF Core см. в справочнике по средствам EF Core — PMC в Visual Studio.For more information on the PMC tools for EF Core, see EF Core tools reference - PMC in Visual Studio.

Класс InitialCreateThe InitialCreate class

Изучите файл миграции Migrations/{метка_времени}_InitialCreate.cs.Examine the Migrations/{timestamp}_InitialCreate.cs migration file:

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 в качестве первичного ключа.The Up method creates the Movie table and configures Id as the primary key. Метод Down отменяет изменения схемы, внесенные миграцией Up.The Down method reverts the schema changes made by the Up migration.

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

  • Запустите приложение и щелкните ссылку Movie App.Run the app and click the Movie App link.

    Если возникнет исключение наподобие следующего:If you get an exception similar to one of the following:

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

возможно, вы пропустили шаг миграции.You probably missed the migrations step.

  • Протестируйте страницу создания.Test the Create page. Введите и отправьте данные.Enter and submit data.

    Примечание

    В поле Price нельзя вводить десятичные запятые.You may not be able to enter decimal commas in the Price field. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения.To support jQuery validation for non-English locales that use a comma (",") for a decimal point and for non US-English date formats, the app must be globalized. Инструкции по глобализации см. на сайте GitHub.For globalization instructions, see this GitHub issue.

  • Протестируйте страницы редактирования, сведений и удаления.Test the Edit, Details, and Delete pages.

Внедрение зависимостей в контроллереDependency injection in the controller

Откройте файл Controllers/MoviesController.cs и изучите его конструктор:Open the Controllers/MoviesController.cs file and examine the constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер.The constructor uses Dependency Injection to inject the database context (MvcMovieContext) into the controller. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.The database context is used in each of the CRUD methods in the controller.

Строго типизированные модели и ключевое слово @modelStrongly typed models and the @model keyword

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData.Earlier in this tutorial, you saw how a controller can pass data or objects to a view using the ViewData dictionary. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.The ViewData dictionary is a dynamic object that provides a convenient late-bound way to pass information to a view.

Модель MVC также поддерживает передачу строго типизированных объектов модели в представление.MVC also provides the ability to pass strongly typed model objects to a view. Такой подход обеспечивает проверку кода во время компиляции.This strongly typed approach enables compile time code checking. В этом подходе используется механизм формирования шаблонов (то есть передачи строго типизированной модели) с представлениями и классом MoviesController.The scaffolding mechanism used this approach (that is, passing a strongly typed model) with the MoviesController class and views.

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

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Параметр id обычно передается в качестве данных маршрута.The id parameter is generally passed as route data. Например, https://localhost:5001/movies/details/1 задает:For example https://localhost:5001/movies/details/1 sets:

  • Контроллер movies (первый сегмент URL-адреса).The controller to the movies controller (the first URL segment).
  • Действие details (второй сегмент URL-адреса).The action to details (the second URL segment).
  • Идентификатор 1 (последний сегмент URL-адреса).The id to 1 (the last URL segment).

Также можно передать id с помощью строки запроса следующим образом:You can also pass in the id with a query string as follows:

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

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение идентификатора.The id parameter is defined as a nullable type (int?) in case an ID value isn't provided.

Лямбда-выражение передается в FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.A lambda expression is passed in to FirstOrDefaultAsync to select movie entities that match the route data or query string value.

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

Если фильм найден, экземпляр модели Movie передается в представление Details:If a movie is found, an instance of the Movie model is passed to the Details view:

return View(movie);

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

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением.The @model statement at the top of the view file specifies the type of object that the view expects. При создании контроллера movie был включен следующий оператор @model:When the movie controller was created, the following @model statement was included:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление.This @model directive allows access to the movie that the controller passed to the view. Объект Model является строго типизированным.The Model object is strongly typed. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor и DisplayFor со строго типизированным объектом Model.For example, in the Details.cshtml view, the code passes each movie field to the DisplayNameFor and DisplayFor HTML Helpers with the strongly typed Model object. Методы Create и Edit и представления также передают объект модели Movie.The Create and Edit methods and views also pass a Movie model object.

Изучите представление Index.cshtml и метод Index в контроллере Movies.Examine the Index.cshtml view and the Index method in the Movies controller. Обратите внимание на то, как в коде создается объект List при вызове метода View.Notice how the code creates a List object when it calls the View method. Код передает список Movies из метода действия Index в представление:The code passes this Movies list from the Index action method to the view:

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

При создании контроллера movies механизм формирования шаблонов включил следующий оператор @model в начало файла Index.cshtml:When the movies controller was created, scaffolding included the following @model statement at the top of the Index.cshtml file:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model.The @model directive allows you to access the list of movies that the controller passed to the view by using a Model object that's strongly typed. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта Model:For example, in the Index.cshtml view, the code loops through the movies with a foreach statement over the strongly typed Model object:

@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.Because the Model object is strongly typed (as an IEnumerable<Movie> object), each item in the loop is typed as Movie. Помимо прочих преимуществ, это означает, что выполняется проверка кода во время компиляции.Among other benefits, this means that you get compile time checking of the code.

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

Добавление класса модели данныхAdd a data model class

Щелкните правой кнопкой мыши папку Models и выберите пункт Добавить > Класс.Right-click the Models folder > Add > Class. Назовите файл Movie.cs.Name the file Movie.cs.

Обновите файл Movie.cs, используя следующий код:Update the Movie.cs file with the following code:

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, которое требуется базе данных в качестве первичного ключа.The Movie class contains an Id field, which is required by the database for the primary key.

Атрибут DataType для ReleaseDate указывает тип данных (Date).The DataType attribute on ReleaseDate specifies the type of the data (Date). С этим атрибутом:With this attribute:

  • пользователю не требуется вводить сведения о времени в поле даты.The user is not required to enter time information in the date field.
  • Отображается только дата, а не время.Only the date is displayed, not time information.

DataAnnotations рассматриваются в следующем руководстве.DataAnnotations are covered in a later tutorial.

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

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet > Консоль диспетчера пакетов (PMC).From the Tools menu, select NuGet Package Manager > Package Manager Console (PMC).

Меню PMC

В PMC выполните следующую команду:In the PMC, run the following command:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Приведенная выше команда добавляет поставщик EF Core для SQL Server.The preceding command adds the EF Core SQL Server provider. Пакет поставщика устанавливает пакет EF Core в качестве зависимости.The provider package installs the EF Core package as a dependency. Дополнительные пакеты устанавливаются автоматически на этапе формирования шаблонов далее в этом руководстве.Additional packages are installed automatically in the scaffolding step later in the tutorial.

Создание класса контекста для базы данныхCreate a database context class

Класс контекста базы данных необходим в целях координации функциональных возможностей EF Core (создание, чтение, обновление, удаление) для модели Movie.A database context class is needed to coordinate EF Core functionality (Create, Read, Update, Delete) for the Movie model. Контекст базы данных наследуется от Microsoft.EntityFrameworkCore.DbContext и определяет сущности, которые необходимо включить в модель данных.The database context is derived from Microsoft.EntityFrameworkCore.DbContext and specifies the entities to include in the data model.

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

Добавьте файл Data/MvcMovieContext.cs со следующим кодом:Add a Data/MvcMovieContext.cs file with the following code:

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> для набора сущностей.The preceding code creates a DbSet<Movie> property for the entity set. В терминологии Entity Framework набор сущностей обычно соответствует таблице базы данных.In Entity Framework terminology, an entity set typically corresponds to a database table. Сущность соответствует строке в таблице.An entity corresponds to a row in the table.

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

ASP.NET Core поддерживает внедрение зависимостей.ASP.NET Core is built with dependency injection (DI). Службы (например, контекст базы данных EF Core) должны регистрироваться с помощью внедрения зависимостей во время запуска приложения.Services (such as the EF Core DB context) must be registered with DI during application startup. Затем компоненты, которые используют эти службы (например, Razor Pages), обращаются к ним через параметры конструктора.Components that require these services (such as Razor Pages) are provided these services via constructor parameters. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.The constructor code that gets a DB context instance is shown later in the tutorial. В этом разделе контекст базы данных регистрируется в контейнере внедрения зависимостей.In this section, you register the database context with the DI container.

Добавьте следующие инструкции using в начало файла Startup.cs.Add the following using statements at the top of Startup.cs:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Добавьте выделенный ниже код в Startup.ConfigureServices:Add the following highlighted code in Startup.ConfigureServices:

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

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

Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Добавление строки подключения базы данныхAdd a database connection string

Добавьте строку подключения в файл appsettings.json :Add a connection string to the appsettings.json file:

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

Выполните сборку проекта, чтобы проверить его на ошибки компиляции.Build the project as a check for compiler errors.

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

Используйте средство формирования шаблонов, чтобы создать страницы для операций создания, чтения, обновления и удаления (CRUD) для модели фильма.Use the scaffolding tool to produce Create, Read, Update, and Delete (CRUD) pages for the movie model.

В Обозревателе решений щелкните правой кнопкой мыши папку Контроллеры и выберите Добавить > Создать шаблонный элемент.In Solution Explorer, right-click the Controllers folder > Add > New Scaffolded Item.

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

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.In the Add Scaffold dialog, select MVC Controller with views, using Entity Framework > Add.

Диалоговое окно "Добавление шаблона"

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

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

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

  • Представления: оставьте флажки, установленные по умолчаниюViews: Keep the default of each option checked
  • Имя контроллера: оставьте имя по умолчанию (MoviesController)Controller name: Keep the default MoviesController
  • Нажмите ДобавитьSelect Add

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

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

Автоматическое создание этих файлов называется формированием шаблонов.The automatic creation of these files is known as scaffolding.

Сформированные страницы пока использовать нельзя, так как база данных не существует.You can't use the scaffolded pages yet because the database doesn't exist. Если запустить приложение и щелкнуть ссылку Movie App, появится сообщение об ошибке Невозможно открыть базу данных или отсутствует таблица: Movie.If you run the app and click on the Movie App link, you get a Cannot open database or no such table: Movie error message.

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

Создайте базу данных с помощью функции миграций EF Core.Use the EF Core Migrations feature to create the database. Миграции — это набор средств, позволяющих создавать и обновлять базы данных в соответствии с моделью данных.Migrations is a set of tools that let you create and update a database to match your data model.

В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet > Консоль диспетчера пакетов (PMC).From the Tools menu, select NuGet Package Manager > Package Manager Console (PMC).

В PMC введите следующие команды:In the PMC, enter the following commands:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate. Создает файл миграции Migrations/{метка_времени}_InitialCreate.cs.Add-Migration InitialCreate: Generates a Migrations/{timestamp}_InitialCreate.cs migration file. Аргумент InitialCreate — это имя миграции.The InitialCreate argument is the migration name. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию.Any name can be used, but by convention, a name is selected that describes the migration. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных.Because this is the first migration, the generated class contains code to create the database schema. Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.The database schema is based on the model specified in the MvcMovieContext class.

  • Update-Database. Обновляет базу данных до последней миграции, созданной предыдущей командой.Update-Database: Updates the database to the latest migration, which the previous command created. Эта команда выполняет метод Up в файле Migrations/{метка_времени}_InitialCreate.cs, который создает базу данных.This command runs the Up method in the Migrations/{time-stamp}_InitialCreate.cs file, which creates the database.

    Команда обновления базы данных выдает следующее предупреждающее сообщение:The database update command generates the following warning:

    "Для десятичного столбца Price в типе сущности Movie не указан тип.No type was specified for the decimal column 'Price' on entity type 'Movie'. Это приведет к тому, что значения будут усекаться без вмешательства пользователя, если они не помещаются в значения точности и масштаба по умолчанию.This will cause values to be silently truncated if they do not fit in the default precision and scale. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'.

    Это предупреждение можно игнорировать. Оно будет устранено в следующем руководстве.You can ignore that warning, it will be fixed in a later tutorial.

Дополнительные сведения о средствах PMC для EF Core см. в справочнике по средствам EF Core — PMC в Visual Studio.For more information on the PMC tools for EF Core, see EF Core tools reference - PMC in Visual Studio.

Класс InitialCreateThe InitialCreate class

Изучите файл миграции Migrations/{метка_времени}_InitialCreate.cs.Examine the Migrations/{timestamp}_InitialCreate.cs migration file:

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 в качестве первичного ключа.The Up method creates the Movie table and configures Id as the primary key. Метод Down отменяет изменения схемы, внесенные миграцией Up.The Down method reverts the schema changes made by the Up migration.

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

  • Запустите приложение и щелкните ссылку Movie App.Run the app and click the Movie App link.

    Если возникнет исключение наподобие следующего:If you get an exception similar to one of the following:

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

возможно, вы пропустили шаг миграции.You probably missed the migrations step.

  • Протестируйте страницу создания.Test the Create page. Введите и отправьте данные.Enter and submit data.

    Примечание

    В поле Price нельзя вводить десятичные запятые.You may not be able to enter decimal commas in the Price field. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения.To support jQuery validation for non-English locales that use a comma (",") for a decimal point and for non US-English date formats, the app must be globalized. Инструкции по глобализации см. на сайте GitHub.For globalization instructions, see this GitHub issue.

  • Протестируйте страницы редактирования, сведений и удаления.Test the Edit, Details, and Delete pages.

Внедрение зависимостей в контроллереDependency injection in the controller

Откройте файл Controllers/MoviesController.cs и изучите его конструктор:Open the Controllers/MoviesController.cs file and examine the constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер.The constructor uses Dependency Injection to inject the database context (MvcMovieContext) into the controller. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.The database context is used in each of the CRUD methods in the controller.

Строго типизированные модели и ключевое слово @modelStrongly typed models and the @model keyword

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData.Earlier in this tutorial, you saw how a controller can pass data or objects to a view using the ViewData dictionary. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.The ViewData dictionary is a dynamic object that provides a convenient late-bound way to pass information to a view.

Модель MVC также поддерживает передачу строго типизированных объектов модели в представление.MVC also provides the ability to pass strongly typed model objects to a view. Такой подход обеспечивает проверку кода во время компиляции.This strongly typed approach enables compile time code checking. В этом подходе используется механизм формирования шаблонов (то есть передачи строго типизированной модели) с представлениями и классом MoviesController.The scaffolding mechanism used this approach (that is, passing a strongly typed model) with the MoviesController class and views.

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

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Параметр id обычно передается в качестве данных маршрута.The id parameter is generally passed as route data. Например, https://localhost:5001/movies/details/1 задает:For example https://localhost:5001/movies/details/1 sets:

  • Контроллер movies (первый сегмент URL-адреса).The controller to the movies controller (the first URL segment).
  • Действие details (второй сегмент URL-адреса).The action to details (the second URL segment).
  • Идентификатор 1 (последний сегмент URL-адреса).The id to 1 (the last URL segment).

Также можно передать id с помощью строки запроса следующим образом:You can also pass in the id with a query string as follows:

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

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение идентификатора.The id parameter is defined as a nullable type (int?) in case an ID value isn't provided.

Лямбда-выражение передается в FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.A lambda expression is passed in to FirstOrDefaultAsync to select movie entities that match the route data or query string value.

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

Если фильм найден, экземпляр модели Movie передается в представление Details:If a movie is found, an instance of the Movie model is passed to the Details view:

return View(movie);

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

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Оператор @model в начале файла представления задает тип объекта, который будет ожидаться представлением.The @model statement at the top of the view file specifies the type of object that the view expects. При создании контроллера movie был включен следующий оператор @model:When the movie controller was created, the following @model statement was included:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление.This @model directive allows access to the movie that the controller passed to the view. Объект Model является строго типизированным.The Model object is strongly typed. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor и DisplayFor со строго типизированным объектом Model.For example, in the Details.cshtml view, the code passes each movie field to the DisplayNameFor and DisplayFor HTML Helpers with the strongly typed Model object. Методы Create и Edit и представления также передают объект модели Movie.The Create and Edit methods and views also pass a Movie model object.

Изучите представление Index.cshtml и метод Index в контроллере Movies.Examine the Index.cshtml view and the Index method in the Movies controller. Обратите внимание на то, как в коде создается объект List при вызове метода View.Notice how the code creates a List object when it calls the View method. Код передает список Movies из метода действия Index в представление:The code passes this Movies list from the Index action method to the view:

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

При создании контроллера movies механизм формирования шаблонов включил следующий оператор @model в начало файла Index.cshtml:When the movies controller was created, scaffolding included the following @model statement at the top of the Index.cshtml file:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model.The @model directive allows you to access the list of movies that the controller passed to the view by using a Model object that's strongly typed. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта Model:For example, in the Index.cshtml view, the code loops through the movies with a foreach statement over the strongly typed Model object:

@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.Because the Model object is strongly typed (as an IEnumerable<Movie> object), each item in the loop is typed as Movie. Помимо прочих преимуществ, это означает, что выполняется проверка кода во время компиляции.Among other benefits, this means that you get compile time checking of the code.

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

Добавление класса модели данныхAdd a data model class

Щелкните правой кнопкой мыши папку Models и выберите пункт Добавить > Класс.Right-click the Models folder > Add > Class. Присвойте классу имя Movie.Name the class Movie.

Добавьте в класс Movie следующие свойства:Add the following properties to the Movie class:

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 содержит:The Movie class contains:

  • Поле Id, которое является обязательным для первичного ключа базы данных.The Id field which is required by the database for the primary key.

  • [DataType(DataType.Date)]. Атрибут DataType указывает тип данных (Date).[DataType(DataType.Date)]: The DataType attribute specifies the type of the data (Date). С этим атрибутом:With this attribute:

    • пользователю не требуется вводить сведения о времени в поле даты.The user is not required to enter time information in the date field.
    • Отображается только дата, а не время.Only the date is displayed, not time information.

DataAnnotations рассматриваются в следующем руководстве.DataAnnotations are covered in a later tutorial.

Создание модели фильмаScaffold the movie model

В этом разделе создается модель фильма.In this section, the movie model is scaffolded. То есть средство формирования шаблонов создает страницы для операций создания, чтения, обновления и удаления для модели фильма.That is, the scaffolding tool produces pages for Create, Read, Update, and Delete (CRUD) operations for the movie model.

В Обозревателе решений щелкните правой кнопкой мыши папку Контроллеры и выберите Добавить > Создать шаблонный элемент.In Solution Explorer, right-click the Controllers folder > Add > New Scaffolded Item.

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

В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.In the Add Scaffold dialog, select MVC Controller with views, using Entity Framework > Add.

Диалоговое окно "Добавление шаблона"

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

  • Класс модели: Movie (MvcMovie.Models)Model class: Movie (MvcMovie.Models)
  • Класс контекста данных: щелкните значок + и добавьте класс MvcMovie.Models.MvcMovieContext по умолчанию.Data context class: Select the + icon and add the default MvcMovie.Models.MvcMovieContext

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

  • Представления: оставьте флажки, установленные по умолчаниюViews: Keep the default of each option checked
  • Имя контроллера: оставьте имя по умолчанию (MoviesController)Controller name: Keep the default MoviesController
  • Нажмите ДобавитьSelect Add

Диалоговое окно "Добавление контроллера"

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

  • класс контекста базы данных Entity Framework Core (Data/MvcMovieContext.cs);An Entity Framework Core database context class (Data/MvcMovieContext.cs)
  • контроллер фильмов (Controllers/MoviesController.cs);A movies controller (Controllers/MoviesController.cs)
  • файлы представления Razor для страниц Create, Delete, Details, Edit и Index (Views/Movies/*.cshtml).Razor view files for Create, Delete, Details, Edit, and Index pages (Views/Movies/*.cshtml)

Автоматическое создание контекста базы данных и методов и представлений действий CRUD (создание, чтение, обновление и удаление) называется формированием шаблонов.The automatic creation of the database context and CRUD (create, read, update, and delete) action methods and views is known as scaffolding.

Если запустить приложение и щелкнуть ссылку Mvc Movie, возникает ошибка наподобие следующей:If you run the app and click on the Mvc Movie link, you get an error similar to the following:

An unhandled exception occurred while processing the request.

SqlException: Cannot open database "MvcMovieContext-<GUID removed>" requested by the login. The login failed.
Login failed for user 'Rick'.

System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString

Вам необходимо создать базу данных. Для этого вы используете функцию миграций EF Core.You need to create the database, and you use the EF Core Migrations feature to do that. С помощью миграций можно создать базу данных, соответствующую модели данных, и обновлять схему базы данных при изменении модели.Migrations lets you create a database that matches your data model and update the database schema when your data model changes.

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

В этом разделе выполняются следующие задачи:In this section, the following tasks are completed:

  • Добавления первоначальной миграции.Add an initial migration.
  • Обновления базы данных с помощью первоначальной миграции.Update the database with the initial migration.
  1. В меню Сервис последовательно выберите пункты Диспетчер пакетов NuGet > Консоль диспетчера пакетов (PMC).From the Tools menu, select NuGet Package Manager > Package Manager Console (PMC).

    Меню PMC

  2. В PMC введите следующие команды:In the PMC, enter the following commands:

    Add-Migration Initial
    Update-Database
    

    Команда Add-Migration формирует код для создания схемы исходной базы данных.The Add-Migration command generates code to create the initial database schema.

    Схема базы данных создается на основе модели, указанной в классе MvcMovieContext.The database schema is based on the model specified in the MvcMovieContext class. Аргумент Initial — это имя миграции.The Initial argument is the migration name. Можно использовать любое имя, но по соглашению используется имя, которое описывает миграцию.Any name can be used, but by convention, a name that describes the migration is used. Для получения дополнительной информации см. Учебник. Часть 5. Применение миграций к примеру приложения университета Contoso.For more information, see Учебник. Часть 5. Применение миграций к примеру приложения университета Contoso.

    Команда Update-Database выполняет метод Up в файле Migrations/{time-stamp}_InitialCreate.cs, который создает базу данных.The Update-Database command runs the Up method in the Migrations/{time-stamp}_InitialCreate.cs file, which creates the database.

Проверка контекста, зарегистрированного с помощью внедрения зависимостейExamine the context registered with dependency injection

ASP.NET Core поддерживает внедрение зависимостей.ASP.NET Core is built with dependency injection (DI). С помощью внедрения зависимостей службы (например, контекст базы данных EF Core) регистрируются во время запуска приложения.Services (such as the EF Core DB context) are registered with DI during application startup. Затем компоненты, которые используют эти службы (например, Razor Pages), обращаются к ним через параметры конструктора.Components that require these services (such as Razor Pages) are provided these services via constructor parameters. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.The constructor code that gets a DB context instance is shown later in the tutorial.

Средство формирования шаблонов автоматически создает контекст базы данных и регистрирует его с использованием контейнера внедрения зависимостей.The scaffolding tool automatically created a DB context and registered it with the DI container.

Рассмотрим следующий метод Startup.ConfigureServices.Examine the following Startup.ConfigureServices method. Средством формирования шаблонов была добавлена выделенная строка:The highlighted line was added by the scaffolder:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies 
        // is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

MvcMovieContext координирует функции EF Core (Create, Read, Update, Delete и т. д.) для модели Movie.The MvcMovieContext coordinates EF Core functionality (Create, Read, Update, Delete, etc.) for the Movie model. Контекст данных (MvcMovieContext) получен из Microsoft.EntityFrameworkCore.DbContext.The data context (MvcMovieContext) is derived from Microsoft.EntityFrameworkCore.DbContext. Контекст данных указывает сущности, которые включаются в модель данных:The data context specifies which entities are included in the data model:

// Unused usings removed.
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;  // Enables public DbSet<Movie> Movie

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<Movie> Movie { get; set; }
    }
}

Представленный выше код создает свойство DbSet<Movie> для набора сущностей.The preceding code creates a DbSet<Movie> property for the entity set. В терминологии Entity Framework набор сущностей обычно соответствует таблице базы данных.In Entity Framework terminology, an entity set typically corresponds to a database table. Сущность соответствует строке в таблице.An entity corresponds to a row in the table.

Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

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

  • Запустите приложение и добавьте /Movies к URL-адресу в браузере (http://localhost:port/movies).Run the app and append /Movies to the URL in the browser (http://localhost:port/movies).

Если вы получите исключение базы данных, аналогичное следующему:If you get a database exception similar to the following:

SqlException: Cannot open database "MvcMovieContext-GUID" requested by the login. The login failed.
Login failed for user 'User-name'.

Вы пропустили шаг миграции.You missed the migrations step.

  • Протестируйте ссылку Создать.Test the Create link. Введите и отправьте данные.Enter and submit data.

    Примечание

    В поле Price нельзя вводить десятичные запятые.You may not be able to enter decimal commas in the Price field. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (,), а для отображения данных в форматах для других языков, кроме английского, выполните глобализацию приложения.To support jQuery validation for non-English locales that use a comma (",") for a decimal point and for non US-English date formats, the app must be globalized. Инструкции по глобализации см. на сайте GitHub.For globalization instructions, see this GitHub issue.

  • Протестируйте ссылки Изменить, Сведения и Удалить.Test the Edit, Details, and Delete links.

Проверьте класс Startup:Examine the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies 
        // is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

Выделенный выше код демонстрирует добавление контекста базы данных фильмов в контейнер внедрения зависимостей:The preceding highlighted code shows the movie database context being added to the Dependency Injection container:

  • services.AddDbContext<MvcMovieContext>(options => задает используемую базу данных и строку подключения.services.AddDbContext<MvcMovieContext>(options => specifies the database to use and the connection string.
  • => — это лямбда-оператор=> is a lambda operator

Откройте файл Controllers/MoviesController.cs и изучите его конструктор:Open the Controllers/MoviesController.cs file and examine the constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер.The constructor uses Dependency Injection to inject the database context (MvcMovieContext) into the controller. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.The database context is used in each of the CRUD methods in the controller.

Строго типизированные модели и ключевое слово @modelStrongly typed models and the @model keyword

Ранее в этом руководстве вы ознакомились с передачей данных или объектов из контроллера в представление с использованием словаря ViewData.Earlier in this tutorial, you saw how a controller can pass data or objects to a view using the ViewData dictionary. Словарь ViewData представляет собой динамический объект, который реализует удобный механизм позднего связывания для передачи информации в представление.The ViewData dictionary is a dynamic object that provides a convenient late-bound way to pass information to a view.

Модель MVC также поддерживает передачу строго типизированных объектов модели в представление.MVC also provides the ability to pass strongly typed model objects to a view. Такой подход обеспечивает более эффективную проверку кода во время компиляции.This strongly typed approach enables better compile time checking of your code. При создании методов и представлений в этом подходе используется механизм формирования шаблонов (то есть передачи строго типизированной модели) с представлениями и классами MoviesController.The scaffolding mechanism used this approach (that is, passing a strongly typed model) with the MoviesController class and views when it created the methods and views.

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

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Параметр id обычно передается в качестве данных маршрута.The id parameter is generally passed as route data. Например, https://localhost:5001/movies/details/1 задает:For example https://localhost:5001/movies/details/1 sets:

  • Контроллер movies (первый сегмент URL-адреса).The controller to the movies controller (the first URL segment).
  • Действие details (второй сегмент URL-адреса).The action to details (the second URL segment).
  • Идентификатор 1 (последний сегмент URL-адреса).The id to 1 (the last URL segment).

Также можно передать id с помощью строки запроса следующим образом:You can also pass in the id with a query string as follows:

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

Параметр id определяется как тип, допускающий значение NULL (int?), в случае, если не указано значение идентификатора.The id parameter is defined as a nullable type (int?) in case an ID value isn't provided.

Лямбда-выражение передается в FirstOrDefaultAsync для выбора записей фильмов, которые соответствуют данным маршрута или значению строки запроса.A lambda expression is passed in to FirstOrDefaultAsync to select movie entities that match the route data or query string value.

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

Если фильм найден, экземпляр модели Movie передается в представление Details:If a movie is found, an instance of the Movie model is passed to the Details view:

return View(movie);

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

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Указав оператор @model в начале файла представления, вы можете задать тип объекта, который будет ожидаться представлением.By including a @model statement at the top of the view file, you can specify the type of object that the view expects. При создании контроллера movie следующий оператор @model был автоматически добавлен в начало файла Details.cshtml:When you created the movie controller, the following @model statement was automatically included at the top of the Details.cshtml file:

@model MvcMovie.Models.Movie

Эта директива @model обеспечивает доступ к фильму, который контроллер передал в представление с использованием строго типизированного объекта Model.This @model directive allows you to access the movie that the controller passed to the view by using a Model object that's strongly typed. Например, в представлении Details.cshtml код передает каждое поле фильма во вспомогательные функции HTML DisplayNameFor и DisplayFor со строго типизированным объектом Model.For example, in the Details.cshtml view, the code passes each movie field to the DisplayNameFor and DisplayFor HTML Helpers with the strongly typed Model object. Методы Create и Edit и представления также передают объект модели Movie.The Create and Edit methods and views also pass a Movie model object.

Изучите представление Index.cshtml и метод Index в контроллере Movies.Examine the Index.cshtml view and the Index method in the Movies controller. Обратите внимание на то, как в коде создается объект List при вызове метода View.Notice how the code creates a List object when it calls the View method. Код передает список Movies из метода действия Index в представление:The code passes this Movies list from the Index action method to the view:

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

При создании контроллера movies механизм формирования шаблонов автоматически включает следующий оператор @model в начало файла Index.cshtml:When you created the movies controller, scaffolding automatically included the following @model statement at the top of the Index.cshtml file:

@model IEnumerable<MvcMovie.Models.Movie>

Эта директива @model обеспечивает доступ к списку фильмов, который контроллер передал в представление с использованием строго типизированного объекта Model.The @model directive allows you to access the list of movies that the controller passed to the view by using a Model object that's strongly typed. Например, в представлении Index.cshtml код циклически перебирает фильмы с использованием оператора foreach для строго типизированного объекта Model:For example, in the Index.cshtml view, the code loops through the movies with a foreach statement over the strongly typed Model object:

@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.Because the Model object is strongly typed (as an IEnumerable<Movie> object), each item in the loop is typed as Movie. Помимо прочих преимуществ, это означает, что выполняется проверка кода во время компиляции:Among other benefits, this means that you get compile time checking of the code:

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