Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В предыдущем руководстве вы создали приложение MVC, которое хранит и отображает данные с помощью Entity Framework (EF) Core и локальной базы данных SQL Server. В этом упражнении вы просматриваете и настраиваете код CRUD (создание, чтение, обновление, удаление), который шаблон MVC автоматически создает для вас в контроллерах и представлениях.
Замечание
Чтобы создать уровень абстракции между контроллером и уровнем доступа к данным, часто реализуют шаблон репозитория. Чтобы сделать примеры простыми и ориентированными на демонстрацию использования самой Entity Framework, учебники не используют репозитории. Сведения о репозиториях с Entity Framework см. в последнем руководстве в этой серии.
Изучив это руководство, вы:
- Настройка страницы информации путем добавления данных регистрации с незамедлительной загрузкой
- Обновление страницы "Создание" с помощью защиты и обработки ошибок
- Обновите страницу редактирования, чтобы предотвратить избыточные изменения.
- Обновите страницу удаления, добавив отчет об ошибках
- Закрытие подключений к базе данных
Необходимые условия
- Выполните предыдущее руководство Начало работы с EF Core в ASP.NET MVC веб-приложении.
Настройка страницы сведений
Сгенерированный код для страницы индекса студентов пропустил свойство Enrollments, так как свойство содержит коллекцию. На странице сведений отображается содержимое коллекции в виде HTML-таблицы.
В файле Controllers/StudentsController.cs метод действия для представления сведений использует FirstOrDefaultAsync метод для получения одной Student сущности. Необходимо добавить код, вызывающий методы Include, ThenInclude и AsNoTracking, как показано в следующем выделенном коде.
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (student == null)
{
return NotFound();
}
return View(student);
}
Методы Include и ThenInclude заставляют контекст загружать свойство навигации Student.Enrollments и свойство навигации Enrollment.Course в рамках каждой записи. Дополнительные сведения об этих методах см. в руководстве по соответствующим данным .
Метод AsNoTracking повышает производительность в сценариях, когда возвращаемые сущности не обновляются в течение текущего времени существования контекста. Дополнительные сведения о AsNoTracking методе см. в конце этого руководства.
Настройка данных маршрута
Значение ключа, переданное методу Details , поступает из данных маршрута. Данные маршрута — это данные, которые связывание модели находит в сегменте URL-адреса. Например, маршрут по умолчанию задает сегменты controller, action, и id (идентификатор, ID):
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
В следующем URL-адресе маршрут по умолчанию сопоставляет инструктора как controller и индекс как action, и 1 как id. Эти значения составляют данные маршрута.
http://localhost:1230/Instructor/Index/1?courseID=2021
Последняя часть URL-адреса (?courseID=2021) — это строковое значение запроса. Привязыватель модели также передает значение идентификатора Index параметру метода id , если передать его в качестве значения строки запроса:
http://localhost:1230/Instructor/Index?id=1&CourseID=2021
На странице индекса URL-адреса гиперссылки создаются вспомогательными операторами тегов в представлении Razor . В следующем Razor коде параметр id соответствует стандартному маршруту, поэтому значение id добавляется в данные маршрута.
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a>
Это изменение создает следующий HTML-код, если item.ID значение равно 6:
<a href="/Students/Edit/6">Edit</a>
В следующем Razor коде studentID текст не соответствует параметру в маршруте по умолчанию, поэтому текст добавляется в виде строки запроса.
<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>
Это изменение создает следующий HTML-код, если item.ID значение равно 6:
<a href="/Students/Edit?studentID=6">Edit</a>
Дополнительные сведения о вспомогательных функциях тегов см. в разделе "Вспомогательные функции тегов" в ASP.NET Core.
Добавьте регистрации в вид сведений
Откройте файл Views/Students/Details.cshtml . Каждое поле отображается с помощью DisplayNameForDisplayFor вспомогательных элементов, как показано в следующем примере.
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.LastName)
</dd>
После последнего поля и непосредственно перед закрывающим </dl> тегом добавьте следующий код, чтобы отобразить список регистраций:
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Enrollments)
</dt>
<dd class="col-sm-10">
<table class="table">
<tr>
<th>Course Title</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Course.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
</dd>
Если отступ кода неправильно задан после вставки фрагмента кода, используйте сочетание клавиш CTRL+K+D , чтобы исправить его.
Новый код циклит по сущностям в свойстве Enrollments навигации. Для каждой регистрации код отображает название курса и оценку. Название курса извлекается из сущности Course, расположенной в навигационном свойстве Course сущности Enrollments.
Запустите приложение, перейдите на вкладку "Учащиеся " и выберите ссылку "Сведения " для учащегося. Откроется список курсов и оценок для выбранного учащегося:
Обновление страницы Create
В файле StudentsController.cs измените метод HttpPost Create , добавив try-catch блок и удалив ID свойство из атрибута Bind .
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
try
{
if (ModelState.IsValid)
{
_context.Add(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists " +
"see your system administrator.");
}
return View(student);
}
Этот код добавляет сущность Student, которую модельный привязчик ASP.NET Core MVC создал в набор сущностей Students, а затем сохраняет изменения в базе данных.
Привязка модели относится к функциям ASP.NET Core MVC, что упрощает работу с данными, отправленными формой. Привязыватель модели преобразует опубликованные значения форм в типы CLR и передает их методу в виде параметров. В этом случае диспетчер моделей создает для вас экземпляр Student сущности, используя значения свойств из коллекции Form.
Вы удалили свойство ID из атрибута Bind, потому что идентификатор является значением первичного ключа, которое SQL Server автоматически задает при вставке строки. Входные данные пользователя не задают значение идентификатора.
Кроме атрибута Bind, try-catch блок — это единственное изменение, внесенное в шаблонный код. Если при сохранении изменений возникает исключение, унаследованное от DbUpdateException, отображается универсальное сообщение об ошибке.
DbUpdateException Причина исключения может быть внешней для приложения, а не ошибки программирования, поэтому пользователю рекомендуется повторить попытку. Приложение производственного качества регистрировало бы такое исключение, но эта функция не реализована в данном примере. Дополнительные сведения см. в разделе "Ведение журнала" в .NET и ASP.NET Core.
Атрибут ValidateAntiForgeryToken помогает предотвратить атаки на подделку межсайтовых запросов (CSRF).
Вспомогательный метод тега формы автоматически внедряет маркер в представление. Токен также включается при отправке формы пользователем. Атрибут ValidateAntiForgeryToken проверяет маркер. Дополнительные сведения см. в разделе "Предотвращение атак межсайтовых запросов (XSRF/CSRF) в ASP.NET Core" и в разделе справки по FormTagHelper Class.
Защита от избыточной публикации
Атрибут Bind , который содержит шаблонный код в методе Create , является одним из способов защиты от перепоставки в сценариях создания. Например, предположим, что сущность Student включает в себя свойство Secret, которое не следует задавать на этой веб-странице.
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public string Secret { get; set; }
}
Даже если у вас нет Secret поля на веб-странице, хакер может использовать средство, например Fiddler, или написать код JavaScript для публикации Secret значения формы.
Bind При отсутствии ограничения атрибута, ограничивающего поля, используемые привязкой модели при создании экземпляра Student, привязка модели может принять это значение формы Secret и использовать его для создания экземпляра сущности Student. Затем любое значение, указанное хакером для поля формы Secret, обновляется в вашей базе данных. На следующем рисунке показано средство Fiddler, добавляющее поле Secret (со значением "OverPost") к отправленным значениям формы.
Затем значение "OverPost" успешно добавляется в Secret свойство вставленной строки, хотя вы и не предполагали, что веб-страница должна задавать это свойство.
Вы можете предотвратить избыточное обновление в сценариях редактирования, сначала считывая сущность из базы данных, а затем вызывая метод TryUpdateModel. Передайте явный список разрешенных свойств — таким образом используется метод в этих руководствах.
Альтернативный способ предотвращения избыточного постинга, предпочитаемый многими разработчиками, — использовать модели представлений, а не классы сущностей с привязкой модели. Включите только свойства, которые необходимо обновить в модели представления. После завершения работы связывателя модели MVC, скопируйте свойства модели представления в экземпляр сущности, при необходимости, воспользовавшись таким инструментом, как AutoMapper. Используйте параметр _context.Entry для экземпляра сущности, чтобы задать его состояние Unchanged, а затем установите параметр Property("PropertyName").IsModified в значение true для каждого свойства сущности, включенного в модель представления. Этот метод работает как в сценариях редактирования, так и в сценариях создания.
Проверка страницы создания
Код в файле Views/Students/Create.cshtml использует вспомогательные функции тегов label, input и span (для сообщений проверки) для каждого поля.
Запустите приложение, перейдите на вкладку "Учащиеся" и нажмите кнопку "Создать".
Введите имена и дату. Попробуйте ввести недопустимую дату, насколько это позволяет ваш браузер. (Некоторые браузеры заставляют использовать средство выбора дат.) Нажмите кнопку "Создать", чтобы просмотреть сообщение об ошибке.
Этот сценарий демонстрирует проверку на стороне сервера по умолчанию. В следующем руководстве показано, как добавить атрибуты, которые создают код для проверки на стороне клиента. В следующем выделенном коде показана проверка модели в методе Create .
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
try
{
if (ModelState.IsValid)
{
_context.Add(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists " +
"see your system administrator.");
}
return View(student);
}
Измените дату на допустимое значение и нажмите кнопку "Создать ", чтобы увидеть, что новый учащийся появится на странице "Индекс ".
Обновление страницы Edit
В файле StudentController.cs метод HttpGet Edit (один без HttpPost атрибута) использует FirstOrDefaultAsync метод для получения выбранной Student сущности, как описано ранее в методе Details . Изменять этот метод не нужно.
Используйте рекомендуемый код HttpPost Edit: чтение и обновление
Замените метод HttpPost Edit следующим кодом.
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
{
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View(studentToUpdate);
}
Эти изменения реализуют передовую практику безопасности, чтобы предотвратить избыточную отправку данных. Генератор шаблонов создал Bind атрибут и добавил сущность, созданную связывателем модели, в набор сущностей с флагом Modified. Этот код не рекомендуется для многих сценариев, так как Bind атрибут очищает все существующие данные в полях, не перечисленных в параметре Include .
Новый код считывает существующую сущность и вызывает TryUpdateModel метод для обновления полей в полученной сущности на основе входных данных пользователя в опубликованных данных формы. (Дополнительные сведения см. в разделе "Привязка модели" в ASP.NET Core.) Автоматическое отслеживание изменений Entity Framework задает Modified флаг для полей, которые обновляют входные данные формы. При вызове SaveChanges метода Entity Framework создает инструкции SQL для обновления строки базы данных. Конфликты параллелизма игнорируются, а только столбцы таблицы, обновленные пользователем, также обновляются в базе данных. (В одном из последующих руководств показано, как обрабатывать конфликты параллелизма.)
Рекомендуется для предотвращения избыточного обновления объявлять в параметрах TryUpdateModel, какие поля могут быть изменены на странице Изменение. (Пустая строка, предыдущая списку полей в списке параметров, предназначена для префикса, используемого с именами полей формы.) В настоящее время нет дополнительных полей, которые вы защищаете. Если вы перечислите поля, которые нужно привязать с помощью привязчика модели, можно предотвратить избыточное размещение полей, добавленных в будущем. Поля в списке автоматически защищаются, пока вы не добавите их явным образом.
В результате этих изменений сигнатура метода HttpPost Edit совпадает с методом HttpGet Edit . Изменения в основном переименовывают метод в EditPost.
Использование альтернативного кода HttpPost Edit: создание и присоединение
Рекомендуемый код HttpPost Edit обеспечивает обновление только измененных столбцов и сохраняет данные в свойствах, которые не должны включаться для привязки модели. Однако подход «сначала чтение» требует дополнительного чтения из базы данных и может привести к более сложному коду для обработки конфликтов конкурентности. Альтернативой является присоединение сущности, созданной привязкой модели к контексту Entity Framework, и пометить ее как измененную. (Не обновляйте проект с помощью следующего кода. Код иллюстрирует необязательный подход.)
public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
{
if (id != student.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View(student);
}
Этот подход можно использовать, если пользовательский интерфейс веб-страницы включает все поля в сущности и может обновлять любой из них.
Шаблонный код использует подход create-and-attach, но перехватывает только DbUpdateConcurrencyException исключения и возвращает коды ошибки 404. В примере показано перехватывание любого исключения при обновлении базы данных и отображение сообщения об ошибке.
Понимание состояний сущностей
Контекст базы данных отслеживает синхронизацию сущностей в памяти с соответствующими им строками в базе данных. Эта информация определяет, что происходит при вызове SaveChanges метода. Например, при передаче новой сущности методу Add, состояние этой сущности устанавливается в Added. При вызове SaveChanges метода контекст базы данных выдает команду SQL INSERT.
Сущность может находиться в одном из следующих состояний:
Added: сущность еще не существует в базе данных. МетодSaveChangesвыполняет инструкциюINSERT.Unchanged: ничего не нужно делать с этой сущностью методомSaveChanges. Когда сущность считывается из базы данных, она начинает с этого статуса.Modified: изменяются некоторые или все значения свойств сущности. МетодSaveChangesвыполняет инструкциюUPDATE.Deleted: сущность помечена для удаления. МетодSaveChangesвыполняет инструкциюDELETE.Detached: контекст базы данных не отслеживает сущность.
В классическом приложении изменения состояния обычно осуществляются автоматически. Вы читаете сущность и вносите изменения в некоторые из её значений свойств. Это поведение приводит к автоматическому изменению состояния сущности на Modified. При вызове метода Entity Framework создает инструкцию SaveChanges SQL UPDATE , которая обновляет только фактические свойства, которые вы изменили.
В веб-приложении объект, который изначально считывает сущность и отображает его данные для изменения, DbContext удаляется после отрисовки страницы. При вызове метода действия HttpPost Edit создается новый веб-запрос, и у вас есть новый экземпляр DbContext класса. При повторном чтении сущности в этом новом контексте вы имитируете обработку настольных компьютеров.
Если вы не хотите выполнять дополнительную операцию чтения, необходимо использовать объект сущности, созданный модельным привязчиком. Самый простой подход — задать состояние Modified сущности так же, как и в альтернативном коде HttpPost Edit , показанном ранее. При вызове SaveChanges метода Entity Framework обновляет все столбцы строки базы данных, так как контекст не имеет способа узнать, какие свойства вы изменили.
Если вы хотите избежать подхода чтения-сначала, но при этом хотите, чтобы оператор SQL UPDATE обновлял только те поля, которые изменены пользователем, код будет более сложным. Необходимо сохранить исходные значения каким-то образом, например с помощью скрытых полей. Значения должны быть доступны при вызове метода HttpPost Edit . Можно создать Student сущность с помощью исходных значений, вызвать Attach метод с исходной версией сущности, обновить значения сущности до новых значений, а затем вызвать SaveChanges метод.
Тестирование страницы редактирования
Запустите приложение, перейдите на вкладку "Учащиеся ", а затем щелкните гиперссылку "Изменить ".
Измените некоторые данные и нажмите кнопку "Сохранить". Откроется страница индекса , и вы увидите измененные данные.
Обновить страницу "Удаление"
В файле StudentController.cs код шаблона для метода HttpGet Delete использует FirstOrDefaultAsync метод для извлечения выбранной Student сущности, как показано в Details и Edit методах. Однако для реализации настраиваемого сообщения об ошибке при сбое вызова SaveChanges метода необходимо добавить некоторые функции в этот метод и соответствующее представление.
Как вы видели для операций обновления и создания, операции удаления требуют двух методов действия. Метод, вызываемая в ответ на запрос GET, отображает представление, которое дает пользователю возможность утвердить или отменить операцию удаления. Если пользователь утверждает, создается запрос POST. Затем вызывается метод HttpPost Delete , и этот метод фактически выполняет операцию удаления.
Необходимо добавить блок try-catch в метод HttpPost Delete для обработки любых ошибок, которые могут возникнуть при обновлении базы данных. Если возникает ошибка, метод HttpPost Delete вызывает метод HttpGet Delete , передав параметр, указывающий на ошибку. Метод HttpGet Delete снова отображает страницу подтверждения с сообщением об ошибке, давая пользователю возможность отменить или повторить попытку.
Замените метод действия HttpGet Delete следующим кодом, который управляет отчетами об ошибках.
public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (student == null)
{
return NotFound();
}
if (saveChangesError.GetValueOrDefault())
{
ViewData["ErrorMessage"] =
"Delete failed. Try again, and if the problem persists " +
"see your system administrator.";
}
return View(student);
}
Этот код принимает необязательный параметр, указывающий, был ли вызов метода выполнен после сбоя при сохранении изменений. Этот параметр имеет значение false, если метод HttpGet Delete вызывается без предыдущего сбоя. Если метод вызывается методом HttpPost Delete в ответ на ошибку обновления базы данных, параметр имеет значение true, а сообщение об ошибке передается в представление.
Используйте подход "read-first" для HTTP-запроса типа Delete
Замените метод действия HttpPost Delete (именованный DeleteConfirmed) следующим кодом. Этот код выполняет фактическую операцию удаления и перехватывает ошибки обновления базы данных.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return RedirectToAction(nameof(Index));
}
try
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
}
}
Код извлекает выбранную сущность, а затем вызывает Remove метод, чтобы задать состояние Deletedсущности. При вызове SaveChanges метода создается команда SQL DELETE.
Использование подхода create-and-attach к HttpPost Delete
Если повышение производительности в высокообъёмном приложении является приоритетом, вы можете избежать ненужных SQL-запросов, создавая экземпляр сущности Student, используя только значение первичного ключа и задав состояние сущности Deleted. Вся информация, необходимая Entity Framework для удаления сущности. (Не обновляйте проект с помощью следующего кода. Код иллюстрирует альтернативный подход.)
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
try
{
Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
}
}
Если сущность также содержит связанные данные для удаления, убедитесь, что каскадное удаление настроено в базе данных. При таком подходе к удалению сущностей Entity Framework может не осознать наличие связанных сущностей, которые необходимо удалить.
Обновите представление удаления
В файле Views/Student/Delete.cshtml добавьте сообщение об ошибке между заголовком h2 и заголовком h3, как показано в следующем примере:
<h2>Delete</h2>
<p class="text-danger">@ViewData["ErrorMessage"]</p>
<h3>Are you sure you want to delete this?</h3>
Запустите приложение, перейдите на вкладку "Учащиеся " и выберите гиперссылку "Удалить ":
Нажмите кнопку "Удалить". Страница индекса отображается без удаленного учащегося. (Вы видите пример кода обработки ошибок в действии в руководстве по параллелизму.)
Закрытие подключений к базе данных
Чтобы освободить ресурсы, которые занимает подключение к базе данных, экземпляр контекста должен быть ликвидирован как можно скорее по завершении работы. Встроенное в ASP.NET Core внедрение зависимостей отвечает за задачу очистки.
В файле Startup.cs вызывается метод расширения AddDbContext для создания DbContext класса в контейнере ASP.NET Core DI. Метод задает время существования Scoped службы по умолчанию.
Scoped означает, что время существования объекта контекста совпадает со временем жизни веб-запроса, а Dispose метод вызывается автоматически в конце веб-запроса.
Обработка транзакций
По умолчанию Entity Framework неявно реализует транзакции. В сценариях, когда вы вносите изменения в несколько строк или таблиц, а затем вызываете SaveChanges метод, Entity Framework автоматически гарантирует, что все изменения успешно выполнены или все они завершаются сбоем. Если некоторые изменения завершаются сначала, а затем возникает ошибка, завершенные изменения автоматически откатываются. В сценариях, где требуется больше управления, см. статью "Транзакции". В примерах описывается включение операций, выполненных за пределами Entity Framework в транзакцию.
Отключение отслеживания объектов сущностей (запросы без отслеживания)
Когда контекст базы данных извлекает строки таблицы и создает объекты сущностей, которые представляют их, он отслеживает, синхронизированы ли сущности в памяти с базой данных по умолчанию. При обновлении сущности данные в памяти выступают в роли кэша. Это кэширование часто не требуется в веб-приложении, так как экземпляры контекста обычно являются короткими (новый создается и удаляется для каждого запроса). Контекст, считывающий сущность, обычно удаляется перед использованием этой сущности снова.
Чтобы отключить отслеживание объектов сущностей в памяти, вызовите метод AsNoTracking. Ниже приведены некоторые распространенные сценарии для этого действия.
В течение времени существования контекста вам не нужно обновлять сущности, и не требуется Entity Framework автоматически загружать свойства навигации с сущностями, извлеченными отдельными запросами. Эти условия часто выполняются в методах действий HttpGet контроллера.
Выполняется запрос, который получает большой объем данных, и обновляется только небольшая часть возвращаемых данных. Более эффективно будет отключить отслеживание для крупного запроса, а позже выполнить запрос для нескольких сущностей, которым требуются обновления.
Вы хотите присоединить сущность для внесения обновлений, но ранее вы получили одну и ту же сущность для другой цели. Так как контекст базы данных уже отслеживает сущность, вы не можете подключить сущность, которую вы хотите изменить. Одним из способов обработки этой ситуации является вызов
AsNoTrackingметода в предыдущем запросе.
Дополнительные сведения см. в статье "Отслеживание и отсутствие отслеживания запросов".
Получение кода
Скачайте или просмотрите завершенное приложение,
Следующий шаг
ASP.NET Core