Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Note
Это не последняя версия этой статьи. См. текущий выпуск в версии .NET 10 этой статьи.
Warning
Эта версия ASP.NET Core больше не поддерживается. Для получения дополнительной информации см. Политику поддержки .NET и .NET Core. См. текущий выпуск в версии .NET 10 этой статьи.
В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляют уровень модели в приложении MVC.
Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.
Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости от EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.
В этом руководстве сначала создаются классы моделей, а затем EF Core создаёт базу данных.
Добавление класса модели данных
Щелкните правой кнопкой мыши папку Models, выберите >Добавить>Класс. Назовите файл 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 и выберите Add > New Scaffolded Item.
В диалоговом окне «Добавить новый элемент с шаблоном»:
- В левой области выберите Установленные>Общее>MVC.
- Выберите контроллер MVC с представлениями с помощью Entity Framework.
- Нажмите кнопку "Добавить".
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- В раскрывающемся списке Database provider выберите SQL Server.
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.
Шаблон добавляет следующие пакеты:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.Design
Скаффолдинг создает следующее:
- Контроллер фильмов:
Controllers/MoviesController.cs - файлы представления Razor для страниц Create, Delete, Details, Edit и Index:
Views/Movies/*.cshtml; - класс контекста для базы данных:
Data/MvcMovieContext.cs.
Модуль scaffold обновляет следующее:
- Вставляет необходимые
MvcMovie.csprojссылки на пакеты в файл проекта. - Регистрирует контекст базы данных в
Program.csфайле. - Добавляет строку подключения базы данных в файл
appsettings.json.
Автоматическое создание этих файлов и их обновление называется формированием шаблонов.
Сгенерированные страницы пока использовать нельзя, так как база данных не существует. Запуск приложения и выбор ссылки Movie App приводит к возникновению сообщения об ошибке Не удается открыть базу данных или no such table: Movie (Такая таблица отсутствует: Movie).
Создайте приложение, чтобы убедиться, что ошибки отсутствуют.
Начальная миграция
EF Core Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (PMC) введите следующую команду:
Add-Migration InitialCreate
-
Add-Migration InitialCreate: создаетMigrations/{timestamp}_InitialCreate.csфайл миграции. АргументInitialCreate— это имя миграции. Можно использовать любое имя, но по соглашению выбирается имя, которое описывает миграцию. Так как это первая миграция, созданный класс содержит код для создания схемы базы данных. Схема базы данных создается на основе модели, указанной в классеMvcMovieContext.
Появится следующее предупреждение, которое будет устранено на следующем шаге:
Тип хранилища не указан для десятичного свойства "Цена" для типа сущности "Фильм". Это приведет к тому, что значения будут усекаться автоматически, если они не помещаются в пределы точности и масштаба по умолчанию. Явным образом укажите тип столбца SQL Server, который может содержать все значения в OnModelCreating с помощью HasColumnType, укажите точность и масштабирование с помощью HasPrecision или настройте преобразователь значений с помощью HasConversion.
В PMC введите следующую команду:
Update-Database
-
Update-Database: обновляет базу данных до последней миграции, созданной предыдущей командой. Эта команда запускаетUpметод вMigrations/{time-stamp}_InitialCreate.csфайле, который создает базу данных.
Дополнительные сведения о средствах PMC для EF Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получите исключение, аналогичное следующему, возможно, вы пропустили команду Update-Database на шаге миграции.
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы поддерживать проверку jQuery для языков, использующих запятую (",") вместо десятичной точки, и для неамериканских английских форматов даты, необходимо выполнить глобализацию приложения. Инструкции по глобализации см. в этом выпуске GitHub.
Изучите созданный класс контекста базы данных и регистрацию
С EF Core доступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. 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). Службы, такие как контекст базы данных, регистрируются в DI Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
При формировании шаблонов был создан следующий выделенный код в 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".
Изучите сгенерированную строку подключения к базе данных
Процесс scaffolding добавил строку подключения в файл 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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:{PORT}/movies/details/1 задает:
- Контроллер, обращающийся к контроллеру
movies, первый сегмент URL-адреса. - Действие для
details— второго сегмента URL-адреса. - Присвоение
idзначения 1, последний сегмент URL-адреса.
Параметр id можно передать с помощью строки запроса, как показано в следующем примере:
https://localhost:{PORT}/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, выберите >, затем Класс. Назовите файл 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. Дополнительную информацию см. в разделе Nullable reference types.
Добавление пакетов NuGet
Visual Studio автоматически устанавливает необходимые пакеты.
Выполните сборку проекта, чтобы проверить его на ошибки компиляции.
Создание каркаса страниц фильмов
Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.
В Обозреватель решений щелкните правой кнопкой мыши папку Controllers и выберите Add > New Scaffolded Item.
В диалоговом окне «Добавить новую заготовку элемента»:
- В левой области выберите Установленное>Общее>MVC.
- Выберите контроллер MVC с представлениями с помощью Entity Framework.
- Нажмите кнопку "Добавить".
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных генерируется имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- В раскрывающемся списке Database provider выберите SQL Server.
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.
Скаффолдинг добавляет следующие пакеты:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.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 Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (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 Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получите исключение, аналогичное следующему, возможно, вы пропустили Update-Database команду на шаге миграции.
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы поддерживать проверку jQuery для неанглоязычных локалей, которые используют запятую (",") в качестве десятичного разделителя, и для неамериканских форматов даты, необходимо выполнить глобализацию приложения. Инструкции по глобализации см. в разделе данного вопроса GitHub.
Изучите созданный класс контекста базы данных и регистрацию.
С EF Core доступ к данным осуществляется с использованием модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. EntityFrameworkCore.DbContext и указывает сущности для включения в модель данных.
Scaffolding создает класс контекста 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). Службы, такие как контекст базы данных, регистрируются в DI Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
При формировании шаблонов был создан следующий выделенный код в 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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:{PORT}/movies/details/1 задает:
- Контроллер, обращающийся к контроллеру
movies, первый сегмент URL-адреса. - Действие для
details— второго сегмента URL-адреса. - Присвоение
idзначения 1, последний сегмент URL-адреса.
Параметр id можно передать с помощью строки запроса, как показано в следующем примере:
https://localhost:{PORT}/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, выберите >, затем Класс. Назовите файл 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 и выберите Add > New Scaffolded Item.
В диалоговом окне «Добавить новую заготовку элемента»:
- В левой области выберите Установленное>Общее>MVC.
- Выберите контроллер MVC с представлениями с помощью Entity Framework.
- Нажмите кнопку "Добавить".
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных генерируется имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- В раскрывающемся списке Database provider выберите SQL Server.
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.
Скаффолдинг добавляет следующие пакеты:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.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 Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (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 Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получите исключение, аналогичное ниже, возможно, вы пропустили команду Update-Database на этапе миграции.
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы поддерживать проверку jQuery для неанглоязычных локалей, которые используют запятую (",") в качестве десятичного разделителя, и для неамериканских форматов даты, необходимо выполнить глобализацию приложения. Инструкции по глобализации см. в разделе данного вопроса GitHub.
Изучите созданный класс контекста базы данных и регистрацию.
С EF Core доступ к данным осуществляется с использованием модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. EntityFrameworkCore.DbContext и указывает сущности для включения в модель данных.
Scaffolding создает класс контекста 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). Службы, такие как контекст базы данных, регистрируются в DI Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
При формировании шаблонов был создан следующий выделенный код в 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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-адреса; - Установите
idна 1 в соответствии с последним сегментом 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. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.
Дополнительные ресурсы
В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляют часть "Mодель" в приложении MVC.
Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.
Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости от EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.
В этом руководстве сначала создаются классы моделей, а затем EF Core создаёт базу данных.
Добавление класса модели данных
Щелкните правой кнопкой мыши папку Models, затем выберите >Добавить>Класс. Назовите файл 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 и выберите Add > New Scaffolded Item.
В диалоговом окне «Добавить новый элемент с шаблоном»:
- В левой области выберите Установленные>Общее>MVC.
- Выберите контроллер MVC с представлениями с помощью Entity Framework.
- Нажмите кнопку "Добавить".
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных создается имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- В раскрывающемся списке Database provider выберите SQL Server.
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Если появится сообщение об ошибке, нажмите Добавить второй раз, чтобы повторить попытку.
Шаблон добавляет следующие пакеты:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.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 Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (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 Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получите исключение, аналогичное следующему, возможно, вы пропустили Update-Database команду на шаге Миграции:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для языковых локалей, не использующих английский и в которых в качестве десятичного разделителя используется запятая, а также для форматов даты, отличных от американского английского, необходимо глобализовать приложение. Инструкции по глобализации см. в статье this GitHub проблема.
Изучите созданный класс контекста базы данных и регистрацию.
При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. 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). Службы, такие как контекст базы данных, регистрируются в DI Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Кодогенерация создала следующий выделенный код в 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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-адреса; - Установите
idна 1 в соответствии с последним сегментом 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. Помимо других преимуществ, компилятор проверяет типы, используемые в коде.
Дополнительные ресурсы
В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляют часть "Mодель" в приложении MVC.
Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.
Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости от EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.
В этом руководстве сначала создаются классы моделей, а затем EF Core создаёт базу данных.
Добавление класса модели данных
Щелкните правой кнопкой мыши папку Models, затем выберите >Добавить>Класс. Назовите файл 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
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console (PMC).
В PMC выполните следующую команду:
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Приведенные выше команды добавляют следующие компоненты:
- Провайдер EF Core SQL Server. Пакет поставщика устанавливает EF Core пакет в качестве зависимости.
- Программы, используемые пакетами, устанавливаются автоматически на этапе формирования шаблонов далее в этом учебнике.
Выполните сборку проекта, чтобы проверить его на ошибки компиляции.
Создание каркаса страниц фильмов
Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.
В Обозреватель решений щелкните правой кнопкой мыши папку Controllers и выберите Add > New Scaffolded Item.
В диалоговом окне Add Scaffold выберите Контроллер MVC с представлениями, использующий Entity Framework, и нажмите > Добавить.
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных генерируется имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Если отобразится сообщение об ошибке, выберите Добавить еще раз, чтобы повторить попытку.
Моделирование актуализирует следующие элементы:
- Вставляет необходимые
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, удалите следующую строку из файла MvcMovie.csproj:
<Nullable>enable</Nullable>
Мы постараемся исправить эту проблему в следующем выпуске.
Начальная миграция
EF Core Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (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 выдает следующее предупреждение:
Для десятичного столбца 'Цена' в типе сущности 'Фильм' не указан тип данных. Это приведет к тому, что значения будут автоматически усекаться, если они не соответствуют предустановленной точности и шкале. С помощью метода HasColumnType() явно укажите тип столбца SQL Server, который может вместить все значения".
Игнорируйте предыдущее предупреждение, оно уже исправлено в следующем учебнике.
Дополнительные сведения о средствах PMC для EF Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получаете такое исключение, как показано ниже, возможно, вы пропустили шаг миграций:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы поддерживать проверку jQuery для неанглийских языков, где запятая (",") используется в качестве десятичного разделителя, а также для форматов даты, отличных от форматов США, приложение должно быть глобализовано. Инструкции по глобализации см. в этом вопросе на GitHub.
Изучите созданный класс контекста базы данных и регистрацию.
С помощью EF Core доступ к данным осуществляется через модель. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. 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). Службы, такие как контекст базы данных, регистрируются в DI Program.cs. Эти службы предоставляются компонентам, которые запрашивают их через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Кодогенерация создала следующий выделенный код в Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));
Система конфигурации ASP.NET Core считывает строку подключения к базе данных "MvcMovieContext".
Проверьте сгенерированную строку подключения к базе данных
Функция Scaffolding добавила строку подключения в файл 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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-адреса; - Установите
idна 1 в соответствии с последним сегментом 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 и выберите >Добавить>Класс. Назовите файл 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
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console (PMC).
В PMC выполните следующую команду:
Install-Package Microsoft.EntityFrameworkCore.Design
Приведенные выше команды добавляют следующие компоненты:
- Поставщик EF Core SQL Server. Пакет поставщика устанавливает пакет EF Core как зависимость.
- Программы, используемые пакетами, устанавливаются автоматически на этапе создания каркаса, в дальней части учебного пособия.
Выполните сборку проекта, чтобы проверить его на ошибки компиляции.
Создание каркаса страниц фильмов
Используйте средство формирования шаблонов, чтобы создать страницы Create, Read, Update и Delete (CRUD) для модели фильма.
В Обозреватель решений щелкните правой кнопкой мыши папку Controllers и выберите Add > New Scaffolded Item.
В диалоговом окне Add Scaffold выберите Контроллер MVC с представлениями, использующий Entity Framework, и нажмите > Добавить.
В диалоговом окне добавления контроллера MVC с представлениями с использованием Entity Framework выполните следующее.
- В раскрывающемся списке Класс модели выберите Фильм (MvcMovie.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- В диалоговом окне Добавление контекста данных генерируется имя класса MvcMovie.Data.MvcMovieContext.
- Нажмите кнопку "Добавить".
- Представления и Имя контроллера: оставьте значения по умолчанию.
- Нажмите кнопку "Добавить".
Моделирование актуализирует следующие элементы:
- Вставляет необходимые
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 Используйте функцию миграции для создания базы данных. Миграции — это набор средств, с помощью которых можно создавать и обновлять базы данных в соответствии с моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console.
В консоли диспетчер пакетов (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 Core см. в справочнике по средствам PMC EF Core — PMC в Visual Studio.
Тестирование приложения
Запустите приложение и выберите ссылку Movie App.
Если вы получаете такое исключение, как показано ниже, возможно, вы пропустили шаг миграций:
SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.
Note
В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для языковых локалей, не использующих английский и в которых в качестве десятичного разделителя используется запятая, а также для форматов даты, отличных от американского английского, необходимо глобализовать приложение. Инструкции по глобализации см. в статье this GitHub проблема.
Изучите созданный класс контекста базы данных и регистрацию.
При этом EF Coreдоступ к данным выполняется с помощью модели. Модель состоит из классов сущностей и объекта контекста, который представляет сеанс взаимодействия с базой данных. Объект контекста позволяет выполнять запросы и сохранять данные. Контекст базы данных является производным от Майкрософт. 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). Службы, такие как контекст базы данных, должны быть зарегистрированы с помощью DI в Startup. Компоненты, которые используют эти службы, предоставляются через параметры конструктора.
В файле Controllers/MoviesController.cs этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных MvcMovieContext в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Кодогенерация создала следующий выделенный код в 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) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Протестируйте страницу создания. Введите и отправьте данные.
Протестируйте страницы редактирования, сведений и удаления.
Строго типизированные модели и директива @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-адреса; - Установите
idна 1 в соответствии с последним сегментом 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, добавьте "Майкрософт.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.
Дополнительные сведения см. в следующих ресурсах:
- Логирование в .NET и ASP.NET Core
- Шаблон ASP.NET по умолчанию отключает ведение журнала EF Core SQL (
dotnet/aspnetcore#32977)
Дополнительные ресурсы
В этом учебнике добавляются классы для управления фильмами в базе данных. Эти классы представляют часть "Mодель" в приложении MVC.
Эти классы модели используются с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это платформа сопоставления с реляционными объектами (ORM), которая упрощает код доступа к данным, который требуется написать.
Созданные классы модели известны как классы POCO (Plain Old CLR Objects — простые старые объекты CLR). Классы POCO не имеют никакой зависимости от EF Core. Эти классы только определяют свойства данных, которые хранятся в базе данных.
В этом руководстве сначала создаются классы моделей, а затем EF Core создаёт базу данных.
Добавление класса модели данных
Щелкните правой кнопкой мыши папку Models, затем выберите >Добавить>Класс. Назовите файл 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
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console (PMC).
В PMC выполните следующую команду:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Предыдущая команда добавляет поставщик EF Core SQL Server. Пакет поставщика устанавливает EF Core пакет в качестве зависимости. Дополнительные пакеты устанавливаются автоматически на этапе формирования шаблонов далее в этом руководстве.
Создание класса контекста для базы данных
Класс контекста базы данных необходим для координации EF Core функций (создание, чтение, обновление, удаление) для Movie модели. Контекст базы данных является производным от Майкрософт.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 построен с использованием внедрения зависимостей (DI). Службы, такие как 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 и выберите > Добавить > новый шаблонный элемент.
В диалоговом окне Добавление шаблона выберите Контроллер MVC с представлениями, использующий Entity Framework > Добавить.
Выполните необходимые действия в диалоговом окне Добавление контроллера:
- Класс модели:Movie (MvcMovie.Models)
- Класс контекста данных:MvcMovieContext (MvcMovie.Data)
- Просмотры: оставьте каждый параметр установленным по умолчанию.
- Имя контроллера: оставьте имя по умолчанию (MoviesController).
- Выберите Добавить
Visual Studio создает:
- контроллер фильмов (
Controllers/MoviesController.cs) - файлы представления Razor для страниц Create, Delete, Details, Edit и Index (*Views/Movies/.cshtml).
Автоматическое создание этих файлов называется генерацией каркасов.
Сгенерированные страницы пока нельзя использовать, так как база данных ещё не создана. Если запустить приложение и щелкнуть Movie App ссылку, вы получите сообщение об ошибке 'Не удается открыть базу данных' или 'нет такой таблицы: Movie'.
Начальная миграция
EF Core Используйте функцию миграции для создания базы данных. Миграции — это набор средств, позволяющих создавать и обновлять базу данных в соответствии с вашей моделью данных.
В меню Tools выберите NuGet диспетчер пакетов>диспетчер пакетов Console (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 Core см. в справочнике по средствам PMC EF 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.
возможно, вы пропустили шаг миграции.
Протестируйте страницу создания. Введите и отправьте данные.
Note
В поле
Priceнельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для языковых локалей, не использующих английский и в которых в качестве десятичного разделителя используется запятая, а также для форматов даты, отличных от американского английского, необходимо глобализовать приложение. Инструкции по глобализации см. в статье this GitHub проблема.Протестируйте страницы редактирования, сведений и удаления.
Внедрение зависимостей в контроллере
Controllers/MoviesController.cs Откройте файл и проверьте конструктор:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
Этот конструктор применяет внедрение зависимостей для внедрения контекста базы данных (MvcMovieContext) в контроллер. Контекст базы данных используется в каждом методе CRUD в контроллере.
Строго типизированные модели и ключевое слово @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. Помимо прочих преимуществ, это означает, что выполняется проверка кода во время компиляции.
Дополнительные ресурсы
ASP.NET Core