Использование Razor Pages с Entity Framework Core в ASP.NET Core: руководство 1 из 8
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
Авторы: Том Дайкстра (Tom Dykstra), Джереми Ликнесс (Jeremy Likness) и Йон П. Смит (Jon P Smith)
Это первое руководство из серии, посвященной использованию Entity Framework (EF) Core в приложении ASP.NET Core Razor Pages. В учебниках создается веб-сайт для вымышленного университета Contoso. На сайте предусмотрены различные функции, в том числе прием учащихся, создание курсов и назначение преподавателей. В этом руководстве используется подход Code First. См. сведения о работе с этим руководством при использовании подхода Database First в этой проблеме GitHub.
Скачайте или просмотрите готовое приложение. Инструкции по скачиванию.
Необходимые компоненты
- Если у вас нет опыта работы с Razor Pages, ознакомьтесь с серией руководств Начало работы с Razor Pages, прежде чем приступать к изучению этого руководства.
- Visual Studio 2022 с рабочей нагрузкой ASP.NET и веб-разработка.
- Пакет SDK для .NET 6.0
Ядра СУБД
В инструкциях для Visual Studio используется SQL Server LocalDB, версия SQL Server Express, которая работает только в Windows.
Устранение неполадок
Если вы столкнулись с проблемой, которую не можете решить, сравните свой код с кодом готового проекта. Хорошим способом получения справки является размещение вопроса в StackOverflow.com с помощью тега ASP.NET Core или тегаEF Core.
Пример приложения
Приложение, создаваемое в этих руководствах, является простым веб-сайтом университета. Пользователи приложения могут просматривать и обновлять сведения об учащихся, курсах и преподавателях. Здесь приведено несколько экранов, создаваемых в руководстве.
Стиль пользовательского интерфейса для этого сайта основан на встроенных шаблонах проектов. В этом руководстве основное внимание уделяется использованию EF Core ASP.NET Core, а не настройке пользовательского интерфейса.
Необязательно: сборка примера для скачивания
Этот шаг необязательный. Создание готового приложения рекомендуется в случае, если возникли проблемы, которые не удается решить. Если вы столкнулись с проблемой, которую не можете решить, сравните свой код с кодом готового проекта. Указания по скачиванию.
Выберите ContosoUniversity.csproj
, чтобы открыть проект.
Выполните сборку проекта.
В консоли диспетчера пакетов (PMC) выполните следующую команду:
Update-Database
Запустите проект, чтобы заполнить базу данных.
Создание проекта веб-приложения
Запустите Visual Studio 2022 и нажмите Создать проект.
В диалоговом окне Создать проект выберите Веб-приложение ASP.NET Core и нажмите Далее.
В диалоговом окне Настроить новый проект введите
ContosoUniversity
в поле Имя проекта. Важно присвоить проекту имя ContosoUniversity, включая сопоставление регистра букв, чтобы пространство имени соответствовало при копировании и вставке примера кода.Выберите Далее.
В диалоговом окне Дополнительные сведения выберите .NET 6.0 (долгосрочная поддержка) и щелкните Создать.
Настройка стиля сайта
Скопируйте и вставьте в файл Pages/Shared/_Layout.cshtml
следующий код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/ContosoUniversity.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2021 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Файл макета задает заголовок, нижний колонтитул и меню для сайта. Приведенный выше код вносит следующие изменения:
- Заменяет все вхождения "ContosoUniversity" на "Contoso University". Таких элементов будет три.
- Пункты меню Home и Privacy будут удалены.
- Добавляются пункты меню About (Сведения), Students (Учащиеся), Courses (Курсы), Instructors (Преподаватели) и Departments (Кафедры).
Замените все содержимое файла Pages/Index.cshtml
следующим кодом:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
@* <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
*@ </p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
@* <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
*@ </p>
</div>
</div>
</div>
</div>
Приведенный выше код заменяет текст об ASP.NET Core текстом об этом приложении.
Запустите приложение, чтобы убедиться, что home откроется страница.
Модель данных
В следующих разделах создается модель данных.
Учащийся может зарегистрироваться в любом количестве курсов, а в отдельном курсе может быть зарегистрировано любое количество учащихся.
Сущность Student
- Создайте папку Models (Модели) в папке проекта.
- Создайте
Models/Student.cs
, используя следующий код:namespace ContosoUniversity.Models { public class Student { public int ID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public ICollection<Enrollment> Enrollments { get; set; } } }
Свойство ID
используется в качестве столбца первичного ключа в таблице базы данных, соответствующей этому классу. По умолчанию EF Core интерпретирует свойство, которое называется ID
или classnameID
является первичным ключом. Поэтому альтернативным автоматически распознаваемым именем для первичного ключа класса Student
является StudentID
. Дополнительные сведения см. в разделе EF Core "Ключи".
Свойство Enrollments
является свойством навигации. Свойства навигации содержат другие сущности, связанные с этой сущностью. В этом случае свойство Enrollments
сущности Student
содержит все сущности Enrollment
, которые связаны с этим учащимся. Например, если строка Student (Учащийся) в базе данных имеет две связанные строки Enrollment (Регистрация), свойство навигации Enrollments
содержит две эти сущности Enrollment.
В базе данных строка Enrollment связана со строкой Student, если ее столбец StudentID
содержит идентификатор учащегося. Например, предположим, что строка Student содержит идентификатор 1. Связанные строки Enrollment будут содержать значение StudentID
(идентификатор учащегося), равное 1. StudentID
— это внешний ключ в таблице Enrollment.
Свойство Enrollments
определено как ICollection<Enrollment>
, так как может быть несколько связанных сущностей Enrollment. Можно использовать и другие типы коллекций, например List<Enrollment>
или HashSet<Enrollment>
. При ICollection<Enrollment>
использовании EF Core создается HashSet<Enrollment>
коллекция по умолчанию.
Сущность Enrollment
Создайте Models/Enrollment.cs
, используя следующий код:
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
Свойство EnrollmentID
является первичным ключом. В этой сущности используется шаблон classnameID
вместо ID
. Для рабочей модели данных многие разработчики выбирают один шаблон и используют только его. В этом учебнике используются оба шаблона, чтобы проиллюстрировать их работу. Использование ID
без classname
упрощает внесение некоторых изменений в модель данных.
Свойство Grade
имеет тип enum
. Знак вопроса после объявления типа Grade
указывает, что свойство Grade
допускает значение NULL. Оценка со значением null отличается от нулевой оценки тем, что при таком значении оценка еще не известна или не назначена.
Свойство StudentID
представляет собой внешний ключ. Ему соответствует свойство навигации Student
. Сущность Enrollment
связана с одной сущностью Student
, поэтому свойство содержит отдельную сущность Student
.
Свойство CourseID
представляет собой внешний ключ. Ему соответствует свойство навигации Course
. Сущность Enrollment
связана с одной сущностью Course
.
EF Core интерпретирует свойство как внешний ключ, если он называется <navigation property name><primary key property name>
. Например, StudentID
является внешним ключом для свойства навигации Student
, так как сущность Student
имеет первичный ключ ID
. Свойства внешнего ключа также могут называться <primary key property name>
. Например, CourseID
, так как сущность Course
имеет первичный ключ CourseID
.
Сущность Course
Создайте Models/Course.cs
, используя следующий код:
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Свойство Enrollments
является свойством навигации. Сущность Course
может быть связана с любым числом сущностей Enrollment
.
Атрибут DatabaseGenerated
позволяет приложению указать первичный ключ, а не использовать созданный базой данных.
сборка приложения. Компилятор создает несколько предупреждений о том, как обрабатываются значения null
. Дополнительные сведения см. в сведениях об этой проблеме GitHub, а также в статьях Ссылочные типы, допускающие значение NULL и Руководство по более четкому выражению проектного замысла с помощью ссылочных типов, допускающих и не допускающих значение NULL.
Чтобы исключить предупреждения из ссылочных типов, допускающих значения NULL, удалите следующую строку из файла ContosoUniversity.csproj
:
<Nullable>enable</Nullable>
В настоящее время подсистема формирования шаблонов не поддерживает ссылочные типы, допускающие значения NULL, поэтому модели, используемые в формировании шаблонов, также не поддерживают их.
Удалите заметку о ссылочном типе ?
, допускающем значение NULL, из public string? RequestId { get; set; }
в файле Pages/Error.cshtml.cs
, чтобы сборка проекта выполнялась без предупреждений компилятора.
Формирование шаблона для страниц Student
В этом разделе для создания указанных ниже компонентов используется средство формирования шаблонов ASP.NET Core.
- Класс EF Core
DbContext
. Контекст —это основной класс, который координирует функциональные возможности Entity Framework для определенной модели данных. Он является производным от класса Microsoft.EntityFrameworkCore.DbContext. - Razor Pages с поддержкой операций создания, чтения, обновления и удаления (CRUD) для сущности
Student
.
- Создайте папку Pages/Students.
- В обозревателе решений щелкните правой кнопкой мыши папку Pages/Students и выберите пункты Добавить>Создать шаблонный элемент.
- В диалоговом окне добавления нового шаблона элемента:
- На вкладке слева выберите Установленные > Общие >Razor Pages
- Выберите Razor Pages на основе Entity Framework (CRUD)>Добавить.
- В диалоговом окне добавления Razor страниц с помощью Entity Framework (CRUD):
- В раскрывающемся списке Класс модели выберите Student (ContosoUniversity.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- Измените имя контекста данных так, чтобы оно заканчивалось на
SchoolContext
, а не наContosoUniversityContext
. Новое имя контекста:ContosoUniversity.Data.SchoolContext
. - Выберите Добавить, чтобы завершить добавление класса контекста данных.
- Выберите Добавить, чтобы закрыть диалоговое окно Добавление Razor Pages.
- Измените имя контекста данных так, чтобы оно заканчивалось на
Следующие пакеты устанавливаются автоматически:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Если предыдущий шаг завершился сбоем, выполните сборку проекта и повторите шаг формирования шаблона.
В процессе формирования шаблона выполняются следующие действия:
- Создает Razor страницы в папке Pages/Students :
Create.cshtml
иCreate.cshtml.cs
Delete.cshtml
иDelete.cshtml.cs
Details.cshtml
иDetails.cshtml.cs
Edit.cshtml
иEdit.cshtml.cs
Index.cshtml
иIndex.cshtml.cs
- Создает
Data/SchoolContext.cs
. - Добавляет контекст в внедрение
Program.cs
зависимостей. - добавляет строку подключения к базе данных в файл
appsettings.json
.
Строка подключения к базе данных
Средство формирования шаблонов создает строку подключения в файле appsettings.json
.
Строка подключения указывает базу данных SQL Server LocalDB.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
LocalDB — это упрощенная версия ядра СУБД SQL Server Express, предназначенная для разработки приложений и не ориентированная на использование в рабочей среде. По умолчанию LocalDB создает файлы MDF в каталоге C:/Users/<user>
.
Обновление класса контекста базы данных
Основной класс, который координирует EF Core функциональные возможности для данной модели данных, — это класс контекста базы данных. Контекст является производным от Microsoft.EntityFrameworkCore.DbContext. Контекст указывает сущности, которые включаются в модель данных. В этом проекте соответствующий класс называется SchoolContext
.
Обновите Data/SchoolContext.cs
, включив в него следующий код.
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Приведенный выше код изменяет форму слова DbSet<Student> Student
на множественную: DbSet<Student> Students
. Чтобы код Razor Pages соответствовал новому DBSet
имени, внесите глобальное изменение: _context.Student.
на _context.Students.
.
Всего это имя встречается 8 раз.
Так как набор сущностей содержит несколько сущностей, многие разработчики предпочитают имена свойств DBSet
во множественном числе.
Выделенный код:
- Создает свойство DbSet<TEntity> для каждого набора сущностей. В EF Core терминологии:
- Набор сущностей обычно соответствует таблице базы данных.
- Сущность соответствует строке в таблице.
- Вызывает OnModelCreating.
OnModelCreating
:- Вызывается при
SchoolContext
инициализации, но до защиты модели и используется для инициализации контекста. - является обязательным, так как далее в руководстве сущность
Student
будет содержать ссылки на другие сущности.
- Вызывается при
Мы планируем исправить эту проблему в будущем выпуске.
Program.cs
ASP.NET Core поддерживает внедрение зависимостей. С помощью внедрения зависимостей службы, например SchoolContext
, регистрируются во время запуска приложения. Затем компоненты, которые используют эти службы, например Razor Pages, обращаются к ним через параметры конструктора. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.
Средство формирования шаблонов автоматически зарегистрировало класс контекста в контейнере внедрения зависимостей.
Следующие выделенные строки были добавлены средством формирования шаблонов:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json
или appsettings.Development.json
.
Добавление фильтра исключений базы данных
Добавьте AddDatabaseDeveloperPageExceptionFilter и UseMigrationsEndPoint, как показано в следующем коде:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
Добавьте пакет NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.
Введите в консоли диспетчера пакетов следующие команды, чтобы добавить пакет NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Пакет NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
предоставляет ПО промежуточного слоя ASP.NET Core для страниц ошибок Entity Framework Core. Это ПО промежуточного слоя помогает обнаруживать и диагностировать ошибки с помощью миграций Entity Framework Core.
AddDatabaseDeveloperPageExceptionFilter
предоставляет полезные сведения об ошибках EF в среде разработки для их устранения.
Создание базы данных
Обновите Program.cs
, чтобы создать базу данных, если она не существует:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Метод EnsureCreated не принимает никаких действий, если база данных для контекста существует. Если база данных не существует, она создается вместе со схемой. EnsureCreated
обеспечивает описанный ниже рабочий процесс для обработки изменений модели данных.
- Удалите базу данных. Все существующие данные теряются.
- Модель данных изменяется. Например, добавляется поле
EmailAddress
. - Выполнить приложение.
- Метод
EnsureCreated
создает базу данных с новой схемой.
Этот рабочий процесс хорошо подходит для ранних стадий разработки, когда схема часто меняется, если данные сохранять не требуется. Однако если данные, введенные в базу данных, необходимо сохранять, ситуация будет иной. В таком случае используйте перенос.
Далее в этой серии учебников вы удалите базу данных, созданную методом EnsureCreated
, и используете вместо этого перенос. Созданную методом EnsureCreated
базу данных нельзя обновить, используя перенос.
Тестирование приложения
- Выполнить приложение.
- Щелкните ссылку Students и выберите Создать.
- Протестируйте ссылки Edit, Details и Delete.
Заполнение базы данных
Метод EnsureCreated
создает пустую базу данных. В этом разделе добавляется код, который заполняет базу данных тестовыми данными.
Создайте Data/DbInitializer.cs
, используя следующий код:
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Этот код проверяет наличие учащихся в базе данных. Если учащихся нет, в базу данных добавляются тестовые данные. Для повышения производительности тестовые данные создаются массивами, а не коллекциями List<T>
.
- В
Program.cs
удалите//
из строкиDbInitializer.Initialize
:
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
}
Завершите работу приложения, если оно запущено, и выполните следующую команду в консоли диспетчера пакетов (PMC):
Drop-Database -Confirm
Выберите
Y
, чтобы удалить базу данных.
- Перезапустите приложение.
- Выберите страницу учащихся, чтобы увидеть заполненные данные.
Просмотр базы данных
- Откройте обозреватель объектов SQL Server (SSOX) из меню Вид в Visual Studio.
- В SSOX щелкните (localdb)\MSSQLLocalDB > Базы данных > SchoolContext-{GUID}. Имя базы данных создается на основе имени контекста, которое вы указали ранее, а также включает дефис и GUID.
- Разверните узел Таблицы.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотр данных, чтобы просмотреть созданные столбцы и строки, вставленные в таблицу.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотреть код, чтобы увидеть, как модель
Student
соотносится со схемой таблицыStudent
.
Асинхронные методы EF в веб-приложениях ASP.NET Core
Асинхронное программирование — это режим по умолчанию для ASP.NET Core и EF Core.
Веб-сервер имеет ограниченное число потоков, поэтому при высокой загрузке могут использоваться все доступные потоки. В таких случаях сервер не может обрабатывать новые запросы до тех пор, пока не будут высвобождены потоки. В синхронном коде многие потоки могут быть заняты, не выполняя при этом какие-либо операции и ожидая завершения ввода-вывода. В асинхронном коде в то время, когда процесс ожидает завершения ввода-вывода, его поток высвобождается и может использоваться сервером для обработки других запросов. Таким образом, асинхронный код позволяет более эффективно использовать ресурсы сервера, который может обрабатывать больше трафика без задержек.
Во время выполнения асинхронный код использует немного больше служебных ресурсов. Однако при низком объеме трафика этим можно пренебречь. Тем не менее в случае большого объема трафика это дает существенный выигрыш в производительности.
В следующем коде для асинхронного выполнения используются ключевое слово async, возвращаемое значение Task
, ключевое слово await
и метод ToListAsync
.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- Ключевое
async
слово сообщает компилятору:- создавать обратные вызовы для частей тела метода;
- создавать возвращаемый объект Task.
- Тип возвращаемого значения
Task
представляет текущую операцию. - Ключевое слово
await
предписывает компилятору разделить метод на две части. Первая часть завершается операцией, которая запускается в асинхронном режиме. Вторая часть помещается в метод обратного вызова, который вызывается при завершении операции. ToListAsync
является асинхронной версией метода расширенияToList
.
Некоторые моменты, которые следует учитывать при написании асинхронного кода, использующего EF Core:
- Асинхронно выполняются только те инструкции, в результате которых в базу данных отправляются запросы или команды. К ним относятся
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
иSaveChangesAsync
. В их число не входят операторы, которые просто изменяютIQueryable
, такие какvar students = context.Students.Where(s => s.LastName == "Davolio")
. - Контекст EF Core не является потокобезопасным: не пытайтесь выполнять несколько операций параллельно.
- Чтобы воспользоваться преимуществами производительности асинхронного кода, убедитесь, что пакеты библиотеки (например, для разбиения по страницам) используют асинхронный режим, если они вызывают EF Core методы, отправляющие запросы в базу данных.
Дополнительные сведения об асинхронном программировании см. в разделах Обзор асинхронной модели и Асинхронное программирование с использованием ключевых слов Async и Await.
Предупреждение
Асинхронная реализация Microsoft.Data.SqlClient имеет некоторые известные проблемы (#593, #601 и другие). Если возникают непредвиденные проблемы с производительностью, попробуйте использовать выполнение команды синхронизации, особенно при работе с большим текстом или двоичными значениями.
Замечания, связанные с быстродействием
Как правило, веб-страница не должна загружать произвольное число строк. Запрос должен использовать разбиение на страницы или применять ограничение. Например, предыдущий запрос может использовать Take
для ограничения количества возвращаемых строк:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
Если в процессе перечисления большой таблицы в представлении возникло исключение базы данных, может быть возвращен ответ HTTP 200 с сообщением о частично сформированных данных.
Разбиение на страницы рассматривается далее в этом руководстве.
Дополнительные сведения см. в разделе Особенности производительности (EF).
Следующие шаги
Использование SQLite для разработки, SQL Server для рабочей среды
Это первое руководство из серии, посвященной использованию Entity Framework (EF) Core в приложении ASP.NET Core Razor Pages. В учебниках создается веб-сайт для вымышленного университета Contoso. На сайте предусмотрены различные функции, в том числе прием учащихся, создание курсов и назначение преподавателей. В этом руководстве используется подход Code First. См. сведения о работе с этим руководством при использовании подхода Database First в этой проблеме GitHub.
Скачайте или просмотрите готовое приложение. Инструкции по скачиванию.
Необходимые компоненты
- Если у вас нет опыта работы с Razor Pages, ознакомьтесь с серией руководств Начало работы с Razor Pages, прежде чем приступать к изучению этого руководства.
- Visual Studio 2019 16.8 или более поздней версии с рабочей нагрузкой ASP.NET и разработка веб-приложений
- Пакет SDK для .NET 5.0
Ядра СУБД
В инструкциях для Visual Studio используется SQL Server LocalDB, версия SQL Server Express, которая работает только в Windows.
Устранение неполадок
Если вы столкнулись с проблемой, которую не можете решить, сравните свой код с кодом готового проекта. Хорошим способом получения справки является размещение вопроса в StackOverflow.com с помощью тега ASP.NET Core или тегаEF Core.
Пример приложения
Приложение, создаваемое в этих руководствах, является простым веб-сайтом университета. Пользователи приложения могут просматривать и обновлять сведения об учащихся, курсах и преподавателях. Здесь приведено несколько экранов, создаваемых в руководстве.
Стиль пользовательского интерфейса для этого сайта основан на встроенных шаблонах проектов. В этом руководстве основное внимание уделяется использованию EF Core ASP.NET Core, а не настройке пользовательского интерфейса.
Необязательно: сборка примера для скачивания
Этот шаг необязательный. Создание готового приложения рекомендуется в случае, если возникли проблемы, которые не удается решить. Если вы столкнулись с проблемой, которую не можете решить, сравните свой код с кодом готового проекта. Указания по скачиванию.
Выберите ContosoUniversity.csproj
, чтобы открыть проект.
- Выполните сборку проекта.
- В консоли диспетчера пакетов (PMC) выполните следующую команду:
Update-Database
Запустите проект, чтобы заполнить базу данных.
Создание проекта веб-приложения
- Откройте Visual Studio и выберите Создать проект.
- В диалоговом окне Создать проект выберите Веб-приложение ASP.NET Core>Далее.
- В диалоговом окне Настроить новый проект введите
ContosoUniversity
в поле Имя проекта. Очень важно использовать именно такое имя с учетом регистра символов, чтобы пространства имен (namespace
) совпадали при копировании кода. - Нажмите кнопку создания.
- В диалоговом окне "Создание нового веб-приложения ASP.NET Core" выберите:
- В раскрывающихся списках выберите .NET Core и ASP.NET Core 5.0.
- Веб-приложение ASP.NET Core.
- Создание
Настройка стиля сайта
Скопируйте и вставьте в файл Pages/Shared/_Layout.cshtml
следующий код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2021 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
Файл макета задает заголовок, нижний колонтитул и меню для сайта. Приведенный выше код вносит следующие изменения:
- Заменяет все вхождения "ContosoUniversity" на "Contoso University". Таких элементов будет три.
- Пункты меню Home и Privacy будут удалены.
- Добавляются пункты меню About (Сведения), Students (Учащиеся), Courses (Курсы), Instructors (Преподаватели) и Departments (Кафедры).
Замените все содержимое файла Pages/Index.cshtml
следующим кодом:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
<a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
<a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
</p>
</div>
</div>
</div>
</div>
Приведенный выше код заменяет текст об ASP.NET Core текстом об этом приложении.
Запустите приложение, чтобы убедиться, что home откроется страница.
Модель данных
В следующих разделах создается модель данных.
Учащийся может зарегистрироваться в любом количестве курсов, а в отдельном курсе может быть зарегистрировано любое количество учащихся.
Сущность Student
Создайте папку Models (Модели) в папке проекта.
Создайте
Models/Student.cs
, используя следующий код:using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Student { public int ID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public ICollection<Enrollment> Enrollments { get; set; } } }
Свойство ID
используется в качестве столбца первичного ключа в таблице базы данных, соответствующей этому классу. По умолчанию EF Core интерпретирует свойство, которое называется ID
или classnameID
является первичным ключом. Поэтому альтернативным автоматически распознаваемым именем для первичного ключа класса Student
является StudentID
. Дополнительные сведения см. в разделе EF Core "Ключи".
Свойство Enrollments
является свойством навигации. Свойства навигации содержат другие сущности, связанные с этой сущностью. В этом случае свойство Enrollments
сущности Student
содержит все сущности Enrollment
, которые связаны с этим учащимся. Например, если строка Student (Учащийся) в базе данных имеет две связанные строки Enrollment (Регистрация), свойство навигации Enrollments
содержит две эти сущности Enrollment.
В базе данных строка Enrollment связана со строкой Student, если ее столбец StudentID
содержит идентификатор учащегося. Например, предположим, что строка Student содержит идентификатор 1. Связанные строки Enrollment будут содержать значение StudentID
(идентификатор учащегося), равное 1. StudentID
— это внешний ключ в таблице Enrollment.
Свойство Enrollments
определено как ICollection<Enrollment>
, так как может быть несколько связанных сущностей Enrollment. Можно использовать и другие типы коллекций, например List<Enrollment>
или HashSet<Enrollment>
. При ICollection<Enrollment>
использовании EF Core создается HashSet<Enrollment>
коллекция по умолчанию.
Сущность Enrollment
Создайте Models/Enrollment.cs
, используя следующий код:
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
Свойство EnrollmentID
является первичным ключом. В этой сущности используется шаблон classnameID
вместо ID
. Для рабочей модели данных многие разработчики выбирают один шаблон и используют только его. В этом учебнике используются оба шаблона, чтобы проиллюстрировать их работу. Использование ID
без classname
упрощает внесение некоторых изменений в модель данных.
Свойство Grade
имеет тип enum
. Знак вопроса после объявления типа Grade
указывает, что свойство Grade
допускает значение NULL. Оценка со значением null отличается от нулевой оценки тем, что при таком значении оценка еще не известна или не назначена.
Свойство StudentID
представляет собой внешний ключ. Ему соответствует свойство навигации Student
. Сущность Enrollment
связана с одной сущностью Student
, поэтому свойство содержит отдельную сущность Student
.
Свойство CourseID
представляет собой внешний ключ. Ему соответствует свойство навигации Course
. Сущность Enrollment
связана с одной сущностью Course
.
EF Core интерпретирует свойство как внешний ключ, если он называется <navigation property name><primary key property name>
. Например, StudentID
является внешним ключом для свойства навигации Student
, так как сущность Student
имеет первичный ключ ID
. Свойства внешнего ключа также могут называться <primary key property name>
. Например, CourseID
, так как сущность Course
имеет первичный ключ CourseID
.
Сущность Course
Создайте Models/Course.cs
, используя следующий код:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Свойство Enrollments
является свойством навигации. Сущность Course
может быть связана с любым числом сущностей Enrollment
.
Атрибут DatabaseGenerated
позволяет приложению указать первичный ключ, а не использовать созданный базой данных.
Выполните сборку проекта и убедитесь в отсутствии ошибок компилятора.
Формирование шаблона для страниц Student
В этом разделе для создания указанных ниже компонентов используется средство формирования шаблонов ASP.NET Core.
- Класс EF Core
DbContext
. Контекст —это основной класс, который координирует функциональные возможности Entity Framework для определенной модели данных. Он является производным от класса Microsoft.EntityFrameworkCore.DbContext. - Razor Pages с поддержкой операций создания, чтения, обновления и удаления (CRUD) для сущности
Student
.
- Создайте папку Pages/Students.
- В обозревателе решений щелкните правой кнопкой мыши папку Pages/Students и выберите пункты Добавить>Создать шаблонный элемент.
- В диалоговом окне добавления нового шаблона элемента:
- На вкладке слева выберите Установленные > Общие >Razor Pages
- Выберите Razor Pages на основе Entity Framework (CRUD)>Добавить.
- В диалоговом окне добавления Razor страниц с помощью Entity Framework (CRUD):
- В раскрывающемся списке Класс модели выберите Student (ContosoUniversity.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- Измените имя контекста данных так, чтобы оно заканчивалось на
SchoolContext
, а не наContosoUniversityContext
. Новое имя контекста:ContosoUniversity.Data.SchoolContext
. - Выберите Добавить, чтобы завершить добавление класса контекста данных.
- Выберите Добавить, чтобы закрыть диалоговое окно Добавление Razor Pages.
- Измените имя контекста данных так, чтобы оно заканчивалось на
Если формирование шаблонов завершится ошибкой 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.'
, снова запустите средство формирования шаблонов или обратитесь к описанию этой проблемы GitHub.
Следующие пакеты устанавливаются автоматически:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Если предыдущий шаг завершился сбоем, выполните сборку проекта и повторите шаг формирования шаблона.
В процессе формирования шаблона выполняются следующие действия:
- Создает Razor страницы в папке Pages/Students :
Create.cshtml
иCreate.cshtml.cs
Delete.cshtml
иDelete.cshtml.cs
Details.cshtml
иDetails.cshtml.cs
Edit.cshtml
иEdit.cshtml.cs
Index.cshtml
иIndex.cshtml.cs
- Создает
Data/SchoolContext.cs
. - Добавляет контекст в внедрение
Startup.cs
зависимостей. - добавляет строку подключения к базе данных в файл
appsettings.json
.
Строка подключения к базе данных
Средство формирования шаблонов создает строку подключения в файле appsettings.json
.
Строка подключения указывает базу данных SQL Server LocalDB.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=CU-1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
LocalDB — это упрощенная версия ядра СУБД SQL Server Express, предназначенная для разработки приложений и не ориентированная на использование в рабочей среде. По умолчанию LocalDB создает файлы MDF в каталоге C:/Users/<user>
.
Обновление класса контекста базы данных
Основной класс, который координирует EF Core функциональные возможности для данной модели данных, — это класс контекста базы данных. Контекст является производным от Microsoft.EntityFrameworkCore.DbContext. Контекст указывает сущности, которые включаются в модель данных. В этом проекте соответствующий класс называется SchoolContext
.
Обновите Data/SchoolContext.cs
, включив в него следующий код.
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Приведенный выше код изменяет форму слова DbSet<Student> Student
на множественную: DbSet<Student> Students
. Чтобы код Razor Pages соответствовал новому DBSet
имени, внесите глобальное изменение: _context.Student.
на _context.Students.
.
Всего это имя встречается 8 раз.
Так как набор сущностей содержит несколько сущностей, многие разработчики предпочитают имена свойств DBSet
во множественном числе.
Выделенный код:
- Создает свойство DbSet<TEntity> для каждого набора сущностей. В EF Core терминологии:
- Набор сущностей обычно соответствует таблице базы данных.
- Сущность соответствует строке в таблице.
- Вызывает OnModelCreating.
OnModelCreating
:- Вызывается при
SchoolContext
инициализации, но до защиты модели и используется для инициализации контекста. - является обязательным, так как далее в руководстве сущность
Student
будет содержать ссылки на другие сущности.
- Вызывается при
Выполните сборку проекта и убедитесь в отсутствии ошибок компилятора.
Startup.cs
ASP.NET Core поддерживает внедрение зависимостей. С помощью внедрения зависимостей службы, например SchoolContext
, регистрируются во время запуска приложения. Затем компоненты, которые используют эти службы, например Razor Pages, обращаются к ним через параметры конструктора. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.
Средство формирования шаблонов автоматически зарегистрировало класс контекста в контейнере внедрения зависимостей.
Следующие выделенные строки были добавлены средством формирования шаблонов:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}
Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json
.
Добавление фильтра исключений базы данных
Добавьте AddDatabaseDeveloperPageExceptionFilter и UseMigrationsEndPoint, как показано в следующем коде:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Добавьте пакет NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.
Введите в консоли диспетчера пакетов следующие команды, чтобы добавить пакет NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Пакет NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
предоставляет ПО промежуточного слоя ASP.NET Core для страниц ошибок Entity Framework Core. Это ПО промежуточного слоя помогает обнаруживать и диагностировать ошибки с помощью миграций Entity Framework Core.
AddDatabaseDeveloperPageExceptionFilter
предоставляет полезные сведения об ошибках EF в среде разработки для их устранения.
Создание базы данных
Обновите Program.cs
, чтобы создать базу данных, если она не существует:
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace ContosoUniversity
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
CreateDbIfNotExists(host);
host.Run();
}
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred creating the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Метод EnsureCreated не принимает никаких действий, если база данных для контекста существует. Если база данных не существует, она создается вместе со схемой. EnsureCreated
обеспечивает описанный ниже рабочий процесс для обработки изменений модели данных.
- Удалите базу данных. Все существующие данные теряются.
- Модель данных изменяется. Например, добавляется поле
EmailAddress
. - Выполнить приложение.
- Метод
EnsureCreated
создает базу данных с новой схемой.
Этот рабочий процесс хорошо подходит для ранних стадий разработки, когда схема часто меняется, если данные сохранять не требуется. Однако если данные, введенные в базу данных, необходимо сохранять, ситуация будет иной. В таком случае используйте перенос.
Далее в этой серии учебников вы удалите базу данных, созданную методом EnsureCreated
, и используете вместо этого перенос. Созданную методом EnsureCreated
базу данных нельзя обновить, используя перенос.
Тестирование приложения
- Выполнить приложение.
- Щелкните ссылку Students и выберите Создать.
- Протестируйте ссылки Edit, Details и Delete.
Заполнение базы данных
Метод EnsureCreated
создает пустую базу данных. В этом разделе добавляется код, который заполняет базу данных тестовыми данными.
Создайте Data/DbInitializer.cs
, используя следующий код:
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Этот код проверяет наличие учащихся в базе данных. Если учащихся нет, в базу данных добавляются тестовые данные. Для повышения производительности тестовые данные создаются массивами, а не коллекциями List<T>
.
В
Program.cs
удалите//
из строкиDbInitializer.Initialize
:context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Завершите работу приложения, если оно запущено, и выполните следующую команду в консоли диспетчера пакетов (PMC):
Drop-Database -Confirm
Выберите
Y
, чтобы удалить базу данных.
- Перезапустите приложение.
- Выберите страницу учащихся, чтобы увидеть заполненные данные.
Просмотр базы данных
- Откройте обозреватель объектов SQL Server (SSOX) из меню Вид в Visual Studio.
- В SSOX щелкните (localdb)\MSSQLLocalDB > Базы данных > SchoolContext-{GUID}. Имя базы данных создается на основе имени контекста, которое вы указали ранее, а также включает дефис и GUID.
- Разверните узел Таблицы.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотр данных, чтобы просмотреть созданные столбцы и строки, вставленные в таблицу.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотреть код, чтобы увидеть, как модель
Student
соотносится со схемой таблицыStudent
.
Асинхронный код
Асинхронное программирование — это режим по умолчанию для ASP.NET Core и EF Core.
Веб-сервер имеет ограниченное число потоков, поэтому при высокой загрузке могут использоваться все доступные потоки. В таких случаях сервер не может обрабатывать новые запросы до тех пор, пока не будут высвобождены потоки. В синхронном коде многие потоки могут быть заняты, не выполняя при этом какие-либо операции и ожидая завершения ввода-вывода. В асинхронном коде в то время, когда процесс ожидает завершения ввода-вывода, его поток высвобождается и может использоваться сервером для обработки других запросов. Таким образом, асинхронный код позволяет более эффективно использовать ресурсы сервера, который может обрабатывать больше трафика без задержек.
Во время выполнения асинхронный код использует немного больше служебных ресурсов. Однако при низком объеме трафика этим можно пренебречь. Тем не менее в случае большого объема трафика это дает существенный выигрыш в производительности.
В следующем коде для асинхронного выполнения используются ключевое слово async, возвращаемое значение Task
, ключевое слово await
и метод ToListAsync
.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- Ключевое
async
слово сообщает компилятору:- создавать обратные вызовы для частей тела метода;
- создавать возвращаемый объект Task.
- Тип возвращаемого значения
Task
представляет текущую операцию. - Ключевое слово
await
предписывает компилятору разделить метод на две части. Первая часть завершается операцией, которая запускается в асинхронном режиме. Вторая часть помещается в метод обратного вызова, который вызывается при завершении операции. ToListAsync
является асинхронной версией метода расширенияToList
.
Некоторые моменты, которые следует учитывать при написании асинхронного кода, использующего EF Core:
- Асинхронно выполняются только те инструкции, в результате которых в базу данных отправляются запросы или команды. К ним относятся
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
иSaveChangesAsync
. В их число не входят операторы, которые просто изменяютIQueryable
, такие какvar students = context.Students.Where(s => s.LastName == "Davolio")
. - Контекст EF Core не является потокобезопасным: не пытайтесь выполнять несколько операций параллельно.
- Чтобы воспользоваться преимуществами производительности асинхронного кода, убедитесь, что пакеты библиотеки (например, для разбиения по страницам) используют асинхронный режим, если они вызывают EF Core методы, отправляющие запросы в базу данных.
Дополнительные сведения об асинхронном программировании см. в разделах Обзор асинхронной модели и Асинхронное программирование с использованием ключевых слов Async и Await.
Замечания, связанные с быстродействием
Как правило, веб-страница не должна загружать произвольное число строк. Запрос должен использовать разбиение на страницы или применять ограничение. Например, предыдущий запрос может использовать Take
для ограничения количества возвращаемых строк:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
Если в процессе перечисления большой таблицы в представлении возникло исключение базы данных, может быть возвращен ответ HTTP 200 с сообщением о частично сформированных данных.
Значение MaxModelBindingCollectionSize по умолчанию равно 1024. Следующий код задает MaxModelBindingCollectionSize
:
public void ConfigureServices(IServiceCollection services)
{
var myMaxModelBindingCollectionSize = Convert.ToInt32(
Configuration["MyMaxModelBindingCollectionSize"] ?? "100");
services.Configure<MvcOptions>(options =>
options.MaxModelBindingCollectionSize = myMaxModelBindingCollectionSize);
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
Сведения о параметрах конфигурации, таких как MyMaxModelBindingCollectionSize
, см. в статье Конфигурация.
Разбиение на страницы рассматривается далее в этом руководстве.
Дополнительные сведения см. в разделе Особенности производительности (EF).
Ведение журнала SQL Entity Framework Core
Конфигурация ведения журналов обычно предоставляется разделом Logging
в файлах appsettings.{Environment}.json
. Чтобы регистрировать инструкции SQL, добавьте "Microsoft.EntityFrameworkCore.Database.Command": "Information"
в файл appsettings.Development.json
:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"AllowedHosts": "*"
}
В приведенном выше коде JSON инструкции SQL отображаются в командной строке и в окне вывода Visual Studio.
Дополнительные сведения см. в статье Ведение журнала в ASP.NET Core и описании этой проблемы GitHub.
Следующие шаги
Использование SQLite для разработки, SQL Server для рабочей среды
Это первое руководство из серии, посвященной использованию Entity Framework (EF) Core в приложении ASP.NET Core Razor Pages. В учебниках создается веб-сайт для вымышленного университета Contoso. На сайте предусмотрены различные функции, в том числе прием учащихся, создание курсов и назначение преподавателей. В этом руководстве используется подход Code First. См. сведения о работе с этим руководством при использовании подхода Database First в этой проблеме GitHub.
Скачайте или просмотрите готовое приложение. Инструкции по скачиванию.
Необходимые компоненты
- Если у вас нет опыта работы с Razor Pages, ознакомьтесь с серией руководств Начало работы с Razor Pages, прежде чем приступать к изучению этого руководства.
- Visual Studio 2019 с рабочей нагрузкой ASP.NET и веб-разработка.
- Пакет SDK для .NET Core 3.0
Ядра СУБД
В инструкциях для Visual Studio используется SQL Server LocalDB, версия SQL Server Express, которая работает только в Windows.
В инструкциях для Visual Studio Code используется SQLite, кроссплатформенное ядро СУБД.
Если вы решили использовать SQLite, скачайте и установите стороннее средство для управления базой данных SQLite и ее просмотра, например обозреватель базы данных для SQLite.
Устранение неполадок
Если вы столкнулись с проблемой, которую не можете решить, сравните свой код с кодом готового проекта. Хорошим способом получения справки является размещение вопроса в StackOverflow.com с помощью тега ASP.NET Core или тегаEF Core.
Пример приложения
Приложение, создаваемое в этих руководствах, является простым веб-сайтом университета. Пользователи приложения могут просматривать и обновлять сведения об учащихся, курсах и преподавателях. Здесь приведено несколько экранов, создаваемых в руководстве.
Стиль пользовательского интерфейса для этого сайта основан на встроенных шаблонах проектов. В этом руководстве основное внимание уделяется использованию EF Core, а не настройке пользовательского интерфейса.
Чтобы получить исходный код готового проекта, перейдите по ссылке в верхней части страницы. В папке cu30 содержится код для версии учебника по ASP.NET Core 3.0. Файлы, отражающие состояние кода для учебников 1–7, находятся в папке cu30snapshots.
Чтобы запустить приложение после скачивания готового проекта, выполните указанные ниже действия.
Выполните сборку проекта.
В консоли диспетчера пакетов (PMC) выполните следующую команду:
Update-Database
Запустите проект, чтобы заполнить базу данных.
Создание проекта веб-приложения
- В меню Файл Visual Studio откройте меню Создать>Проект.
- Выберите Веб-приложение ASP.NET Core.
- Назовите проект ContosoUniversity. Очень важно использовать именно такое имя с учетом регистра символов, чтобы пространства имен совпадали при копировании и вставке кода.
- Выберите в раскрывающихся списках пункты .NET Core и ASP.NET Core 3.0, а затем выберите Веб-приложение.
Настройка стиля сайта
Настройте верхний колонтитул сайта и меню, обновив:Pages/Shared/_Layout.cshtml
Замените все вхождения "ContosoUniversity" на "Contoso University". Таких элементов будет три.
Удалите пункты меню Home и Privacy. Добавьте пункты About (Сведения), Students (Учащиеся), Courses (Курсы), Instructors (Преподаватели) и Departments (Кафедры).
Изменения выделены.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Contoso University</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2019 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
Замените Pages/Index.cshtml
содержимое файла следующим кодом, чтобы заменить текст о ASP.NET Core текстом об этом приложении:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="row mb-auto">
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 mb-4 ">
<p class="card-text">
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core Razor Pages web app.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column position-static">
<p class="card-text mb-auto">
You can build the application by following the steps in a series of tutorials.
</p>
<p>
<a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="row no-gutters border mb-4">
<div class="col p-4 d-flex flex-column">
<p class="card-text mb-auto">
You can download the completed project from GitHub.
</p>
<p>
<a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
</p>
</div>
</div>
</div>
</div>
Запустите приложение, чтобы убедиться, что home откроется страница.
Модель данных
В следующих разделах создается модель данных.
Учащийся может зарегистрироваться в любом количестве курсов, а в отдельном курсе может быть зарегистрировано любое количество учащихся.
Сущность Student
Создайте папку Models (Модели) в папке проекта.
Создайте
Models/Student.cs
, используя следующий код:using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Student { public int ID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public ICollection<Enrollment> Enrollments { get; set; } } }
Свойство ID
используется в качестве столбца первичного ключа в таблице базы данных, соответствующей этому классу. По умолчанию EF Core интерпретирует свойство, которое называется ID
или classnameID
является первичным ключом. Поэтому альтернативным автоматически распознаваемым именем для первичного ключа класса Student
является StudentID
. Дополнительные сведения см. в разделе EF Core "Ключи".
Свойство Enrollments
является свойством навигации. Свойства навигации содержат другие сущности, связанные с этой сущностью. В этом случае свойство Enrollments
сущности Student
содержит все сущности Enrollment
, которые связаны с этим учащимся. Например, если строка Student (Учащийся) в базе данных имеет две связанные строки Enrollment (Регистрация), свойство навигации Enrollments
содержит две эти сущности Enrollment.
В базе данных строка Enrollment связана со строкой Student, если ее столбец StudentID содержит идентификатор учащегося. Например, предположим, что строка Student содержит идентификатор 1. Связанные строки Enrollment будут содержать значение StudentID (идентификатор учащегося), равное 1. StudentID — это внешний ключ в таблице Enrollment.
Свойство Enrollments
определено как ICollection<Enrollment>
, так как может быть несколько связанных сущностей Enrollment. Можно использовать и другие типы коллекций, например List<Enrollment>
или HashSet<Enrollment>
. При ICollection<Enrollment>
использовании EF Core создается HashSet<Enrollment>
коллекция по умолчанию.
Сущность Enrollment
Создайте Models/Enrollment.cs
, используя следующий код:
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
Свойство EnrollmentID
является первичным ключом. В этой сущности используется шаблон classnameID
вместо ID
. Для рабочей модели данных выберите один шаблон и используйте только его. В этом учебнике используются оба шаблона, чтобы проиллюстрировать их работу. Использование ID
без classname
упрощает внесение некоторых изменений в модель данных.
Свойство Grade
имеет тип enum
. Знак вопроса после объявления типа Grade
указывает, что свойство Grade
допускает значение NULL. Оценка со значением null отличается от нулевой оценки тем, что при таком значении оценка еще не известна или не назначена.
Свойство StudentID
представляет собой внешний ключ. Ему соответствует свойство навигации Student
. Сущность Enrollment
связана с одной сущностью Student
, поэтому свойство содержит отдельную сущность Student
.
Свойство CourseID
представляет собой внешний ключ. Ему соответствует свойство навигации Course
. Сущность Enrollment
связана с одной сущностью Course
.
EF Core интерпретирует свойство как внешний ключ, если он называется <navigation property name><primary key property name>
. Например, StudentID
является внешним ключом для свойства навигации Student
, так как сущность Student
имеет первичный ключ ID
. Свойства внешнего ключа также могут называться <primary key property name>
. Например, CourseID
, так как сущность Course
имеет первичный ключ CourseID
.
Сущность Course
Создайте Models/Course.cs
, используя следующий код:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Свойство Enrollments
является свойством навигации. Сущность Course
может быть связана с любым числом сущностей Enrollment
.
Атрибут DatabaseGenerated
позволяет приложению указать первичный ключ, а не использовать созданный базой данных.
Выполните сборку проекта и убедитесь в отсутствии ошибок компилятора.
Формирование шаблона для страниц Student
В этом разделе вы используете средство формирования шаблонов ASP.NET Core для создания указанных ниже компонентов.
- Класс контекстаEF Core. Контекст —это основной класс, который координирует функциональные возможности Entity Framework для определенной модели данных. Он является производным от класса
Microsoft.EntityFrameworkCore.DbContext
. - Razor Pages с поддержкой операций создания, чтения, обновления и удаления (CRUD) для сущности
Student
.
- В папке Pages создайте папку Students.
- В обозревателе решений щелкните правой кнопкой мыши папку Pages/Students и выберите пункты Добавить>Создать шаблонный элемент.
- В диалоговом окне Добавление шаблона щелкните Razor Pages на основе Entity Framework (CRUD)>Добавить.
- В диалоговом окне добавления Razor страниц с помощью Entity Framework (CRUD):
- В раскрывающемся списке Класс модели выберите Student (ContosoUniversity.Models).
- В строке Класс контекста данных щелкните знак плюса (+).
- Измените имя контекста данных с ContosoUniversity.Models.ContosoUniversityContext на ContosoUniversity.Data.SchoolContext.
- Выберите Добавить.
Следующие пакеты устанавливаются автоматически:
Microsoft.VisualStudio.Web.CodeGeneration.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.Extensions.Logging.Debug
Microsoft.EntityFrameworkCore.Tools
Если в предыдущем шаге возникает проблема, выполните сборку проекта и повторите шаг формирования шаблона.
В процессе формирования шаблона выполняются следующие действия:
- Создает Razor страницы в папке Pages/Students :
Create.cshtml
иCreate.cshtml.cs
Delete.cshtml
иDelete.cshtml.cs
Details.cshtml
иDetails.cshtml.cs
Edit.cshtml
иEdit.cshtml.cs
Index.cshtml
иIndex.cshtml.cs
- Создает
Data/SchoolContext.cs
. - Добавляет контекст в внедрение
Startup.cs
зависимостей. - добавляет строку подключения к базе данных в файл
appsettings.json
.
Строка подключения к базе данных
В файле appsettings.json
указывается строка подключения для SQL Server LocalDB.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext6;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
LocalDB — это упрощенная версия ядра СУБД SQL Server Express, предназначенная для разработки приложений и не ориентированная на использование в рабочей среде. По умолчанию LocalDB создает файлы MDF в каталоге C:/Users/<user>
.
Обновление класса контекста базы данных
Основной класс, который координирует EF Core функциональные возможности для данной модели данных, — это класс контекста базы данных. Контекст является производным от Microsoft.EntityFrameworkCore.DbContext. Контекст указывает сущности, которые включаются в модель данных. В этом проекте соответствующий класс называется SchoolContext
.
Обновите Data/SchoolContext.cs
, включив в него следующий код.
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Выделенный код создает свойство DbSet<TEntity> для каждого набора сущностей. В EF Core терминологии:
- Набор сущностей обычно соответствует таблице базы данных.
- Сущность соответствует строке в таблице.
Так как набор сущностей содержит несколько сущностей, свойства DBSet должны иметь имена во множественном числе. Так как средство формирования шаблонов создало DBSet Student
, в этом шаге его имя меняется на имя во множественном числе: Students
.
Чтобы код Razor Pages соответствовал новому имени DBSet, измените _context.Student
на _context.Students
во всем проекте. Всего это имя встречается 8 раз.
Выполните сборку проекта и убедитесь в отсутствии ошибок компилятора.
Startup.cs
ASP.NET Core поддерживает внедрение зависимостей. Службы (например EF Core , контекст базы данных) регистрируются с внедрением зависимостей во время запуска приложения. Затем компоненты, которые используют эти службы (например, Razor Pages), обращаются к ним через параметры конструктора. Код конструктора, который получает экземпляр контекста базы данных, приведен далее в этом руководстве.
Средство формирования шаблонов автоматически зарегистрировало класс контекста в контейнере внедрения зависимостей.
Выделенные строки в
ConfigureServices
были добавлены средством формирования шаблонов:public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddDbContext<SchoolContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SchoolContext"))); }
Имя строки подключения передается в контекст путем вызова метода для объекта DbContextOptions. При локальной разработке система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json
.
Создание базы данных
Обновите Program.cs
, чтобы создать базу данных, если она не существует:
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace ContosoUniversity
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
CreateDbIfNotExists(host);
host.Run();
}
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred creating the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Метод EnsureCreated не принимает никаких действий, если база данных для контекста существует. Если база данных не существует, она создается вместе со схемой. EnsureCreated
обеспечивает описанный ниже рабочий процесс для обработки изменений модели данных.
- Удалите базу данных. Все существующие данные теряются.
- Модель данных изменяется. Например, добавляется поле
EmailAddress
. - Выполнить приложение.
- Метод
EnsureCreated
создает базу данных с новой схемой.
Этот рабочий процесс хорошо подходит для ранних стадий разработки, когда схема часто меняется, если данные сохранять не требуется. Однако если данные, введенные в базу данных, необходимо сохранять, ситуация будет иной. В таком случае используйте перенос.
Далее в этой серии учебников вы удалите базу данных, созданную методом EnsureCreated
, и используете вместо этого перенос. Созданную методом EnsureCreated
базу данных нельзя обновить, используя перенос.
Тестирование приложения
- Выполнить приложение.
- Щелкните ссылку Students и выберите Создать.
- Протестируйте ссылки Edit, Details и Delete.
Заполнение базы данных
Метод EnsureCreated
создает пустую базу данных. В этом разделе добавляется код, который заполняет базу данных тестовыми данными.
Создайте Data/DbInitializer.cs
, используя следующий код:
using ContosoUniversity.Data;
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
};
context.Students.AddRange(students);
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
context.Courses.AddRange(courses);
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
context.Enrollments.AddRange(enrollments);
context.SaveChanges();
}
}
}
Этот код проверяет наличие учащихся в базе данных. Если учащихся нет, в базу данных добавляются тестовые данные. Для повышения производительности тестовые данные создаются массивами, а не коллекциями List<T>
.
В
Program.cs
, заменитеEnsureCreated
вызов вызовомDbInitializer.Initialize
:// context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Завершите работу приложения, если оно запущено, и выполните следующую команду в консоли диспетчера пакетов (PMC):
Drop-Database
Перезапустите приложение.
Выберите страницу учащихся, чтобы увидеть заполненные данные.
Просмотр базы данных
- Откройте обозреватель объектов SQL Server (SSOX) из меню Вид в Visual Studio.
- В SSOX щелкните (localdb)\MSSQLLocalDB > Базы данных > SchoolContext-{GUID}. Имя базы данных создается на основе имени контекста, которое вы указали ранее, а также включает дефис и GUID.
- Разверните узел Таблицы.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотр данных, чтобы просмотреть созданные столбцы и строки, вставленные в таблицу.
- Щелкните правой кнопкой мыши таблицу Student (Учащийся) и выберите пункт Просмотреть код, чтобы увидеть, как модель
Student
соотносится со схемой таблицыStudent
.
Асинхронный код
Асинхронное программирование — это режим по умолчанию для ASP.NET Core и EF Core.
Веб-сервер имеет ограниченное число потоков, поэтому при высокой загрузке могут использоваться все доступные потоки. В таких случаях сервер не может обрабатывать новые запросы до тех пор, пока не будут высвобождены потоки. В синхронном коде многие потоки могут быть заняты, не выполняя при этом какие-либо операции и ожидая завершения ввода-вывода. В асинхронном коде в то время, когда процесс ожидает завершения ввода-вывода, его поток высвобождается и может использоваться сервером для обработки других запросов. Таким образом, асинхронный код позволяет более эффективно использовать ресурсы сервера, который может обрабатывать больше трафика без задержек.
Во время выполнения асинхронный код использует немного больше служебных ресурсов. Однако при низком объеме трафика этим можно пренебречь. Тем не менее в случае большого объема трафика это дает существенный выигрыш в производительности.
В следующем коде для асинхронного выполнения используются ключевое слово async, возвращаемое значение Task<T>
, ключевое слово await
и метод ToListAsync
.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- Ключевое
async
слово сообщает компилятору:- создавать обратные вызовы для частей тела метода;
- создавать возвращаемый объект Task.
- Тип возвращаемого значения
Task<T>
представляет текущую операцию. - Ключевое слово
await
предписывает компилятору разделить метод на две части. Первая часть завершается операцией, которая запускается в асинхронном режиме. Вторая часть помещается в метод обратного вызова, который вызывается при завершении операции. ToListAsync
является асинхронной версией метода расширенияToList
.
Некоторые моменты, которые следует учитывать при написании асинхронного кода, использующего EF Core:
- Асинхронно выполняются только те инструкции, в результате которых в базу данных отправляются запросы или команды. К ним относятся
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
иSaveChangesAsync
. В их число не входят операторы, которые просто изменяютIQueryable
, такие какvar students = context.Students.Where(s => s.LastName == "Davolio")
. - Контекст EF Core не является потокобезопасным: не пытайтесь выполнять несколько операций параллельно.
- Чтобы воспользоваться преимуществами производительности асинхронного кода, убедитесь, что пакеты библиотеки (например, для разбиения по страницам) используют асинхронный режим, если они вызывают EF Core методы, отправляющие запросы в базу данных.
Дополнительные сведения об асинхронном программировании см. в разделах Обзор асинхронной модели и Асинхронное программирование с использованием ключевых слов Async и Await.
Следующие шаги
ASP.NET Core