Имея два класса:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
При назначении ChildId
на Parent
я должен проверить первый , если он существует в БД или ждать DB бросить исключение?
Например (с использованием Entity Framework Core):
ЗАМЕТЬТЕ, что эти виды проверок ВСЕ В ИНТЕРНЕТЕ, даже в официальных документах Microsoft: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using- mvc / processing-Concurrency-Concurrency-with-the-entity-framework-in-an-asp-net-mvc-application # modify-the-Department-controller, но есть дополнительная обработка исключенийSaveChanges
Также обратите внимание, что основной целью этой проверки было вернуть дружественное сообщение и известный статус HTTP пользователю API, а не полностью игнорировать исключения базы данных. И единственное место, которое будет выброшено - внутри SaveChanges
или по SaveChangesAsync
телефону ... так что не будет никакого исключения, когда вы звоните FindAsync
или Any
. Таким образом, если дочерний элемент существует, но был удален ранее, SaveChangesAsync
то будет выдано исключение для параллелизма.
Я сделал это из-за того, что foreign key violation
исключение будет гораздо сложнее отформатировать для отображения «Дочерний объект с id {parent.ChildId} не найден».
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
// is this code redundant?
// NOTE: its probably better to use Any isntead of FindAsync because FindAsync selects *, and Any selects 1
var child = await _db.Children.FindAsync(parent.ChildId);
if (child == null)
return NotFound($"Child with id {parent.ChildId} could not be found.");
_db.Parents.Add(parent);
await _db.SaveChangesAsync();
return parent;
}
против:
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
_db.Parents.Add(parent);
await _db.SaveChangesAsync(); // handle exception somewhere globally when child with the specified id doesn't exist...
return parent;
}
Второй пример в Postgres выдаст 23503 foreign_key_violation
ошибку: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
Недостаток обработки исключений таким способом в ORM, как EF, заключается в том, что он будет работать только с определенной базой данных базы данных. Если вы когда-либо хотели переключиться на SQL-сервер или что-то еще, это больше не будет работать, потому что код ошибки изменится.
Неправильное форматирование исключения для конечного пользователя может раскрыть некоторые вещи, которые вы не хотите, чтобы кто-либо кроме разработчиков видел.
Связанный:
https://stackoverflow.com/questions/308905/should-there-be-a-transaction-for-read-queries
источник
Child with id {parent.ChildId} could not be found.
. И форматирование «Нарушение внешнего ключа», я думаю, хуже в этом случае.Ответы:
Скорее запутанный вопрос, но ДА, вы должны сначала проверить, а не просто обработать исключение БД.
Прежде всего, в вашем примере вы находитесь на уровне данных, используя EF непосредственно в базе данных для запуска SQL. Ваш код эквивалентен запуску
Альтернатива, которую вы предлагаете:
Использование исключения для выполнения условной логики является медленным и неодобрительным.
У вас есть состояние гонки и вы должны использовать транзакцию. Но это может быть полностью сделано в коде.
Главное, спросить себя:
Если нет, то обязательно вставьте и выбросьте исключение. Но просто обработайте исключение, как любую другую ошибку, которая может произойти.
Если вы ожидаете, что это произойдет, то это НЕ исключение, и вам следует проверить, существует ли ребенок первым, и ответить соответствующим дружеским сообщением, если это не так.
Редактировать - Кажется, есть много споров по этому поводу. Перед тем как понизить голосование рассмотрим:
А. Что, если было два ограничения ФК. Вы бы выступили за разбор сообщения об исключении, чтобы выяснить, какой объект отсутствовал?
Б. Если вы пропустите, выполняется только один оператор SQL. Это только попадания, которые требуют дополнительных затрат на второй запрос.
C. Обычно Id - это суррогатный ключ. Трудно представить ситуацию, когда вы знаете ее, и вы не уверены, что она находится в БД. Проверка была бы странной. Но что, если это естественный ключ, введенный пользователем? Это может иметь высокий шанс не присутствовать
источник
Проверка на уникальность, а затем установка антипаттерна; всегда может случиться так, что идентификатор будет вставлен одновременно между временем проверки и временем записи. Базы данных оснащены для решения этой проблемы с помощью таких механизмов, как ограничения и транзакции; большинство языков программирования нет. Поэтому, если вы цените непротиворечивость данных, предоставьте их эксперту (базе данных), т.е. выполните вставку и поймайте исключение, если оно произойдет.
источник
Я думаю, что то, что вы называете «быстро провалиться», и то, что я называю это, не то же самое
Указание базе данных внести изменения и обработка ошибки, это быстро. Ваш путь сложный, медленный и не особо надежный.
Эта ваша техника не быстро проваливается, это «предполетная подготовка». Иногда есть веские причины, но не при использовании базы данных.
источник
Это началось как комментарий, но стало слишком большим.
Нет, как указывалось в других ответах, этот шаблон не должен использоваться. *
При работе с системами, использующими асинхронные компоненты, всегда будет условие гонки, при котором база данных (или файловая система, или другая асинхронная система) может измениться между проверкой и изменением. Проверка этого типа просто не является надежным способом предотвратить тип ошибки, которую вы не хотите обрабатывать.
Хуже, чем не достаточно, с первого взгляда создается впечатление, что это должно предотвратить ошибку дублирующихся записей, создавая ложное чувство безопасности.
Вам все равно нужна обработка ошибок.
В комментариях вы спросили, что если вам нужны данные из нескольких источников.
Все еще нет.
Фундаментальная проблема не исчезнет , если то , что вы хотите проверить , становится все более сложным.
Вам все равно нужна обработка ошибок в любом случае.
Даже если эта проверка была надежным способом предотвращения конкретной ошибки, от которой вы пытаетесь защититься, все равно могут возникать другие ошибки. Что произойдет, если вы потеряете соединение с базой данных, или ей не хватит места, или?
В любом случае, вам, вероятно, все равно потребуется обработка ошибок, связанных с другими базами данных. Обработка этой конкретной ошибки, вероятно, должна быть небольшой частью этого.
Если вам нужны данные, чтобы определить, что изменить, вам, очевидно, нужно будет их где-то собрать. (в зависимости от того, какие инструменты вы используете, возможно, есть более эффективные способы их сбора, чем отдельные запросы). Если, изучая собранные вами данные, вы решаете, что в конце концов вам не нужно вносить изменения, прекрасно, не делайте менять. Это определение полностью отделено от проблем обработки ошибок.
Вам все равно нужна обработка ошибок в любом случае.
Я знаю, что я повторяюсь, но я чувствую, что важно прояснить это. Я убирал этот беспорядок раньше.
Это потерпит неудачу в конце концов. Когда это не удастся, будет трудно и много времени, чтобы добраться до сути. Решать проблемы, возникающие в условиях гонки, сложно. Они не происходят последовательно, поэтому будет трудно или даже невозможно воспроизвести в изоляции. Вы не включили надлежащую обработку ошибок для начала, так что вам вряд ли придется что-либо делать: возможно, сообщение конечного пользователя о каком-то загадочном тексте (который вы пытались предотвратить с самого начала). Может быть, трассировка стека, указывающая на ту функцию, которая, когда вы смотрите на нее, явно отрицает, что ошибка должна быть возможной.
* Могут быть веские коммерческие причины для выполнения этих существующих проверок, например, для предотвращения дублирования приложением дорогостоящей работы, но это не является подходящей заменой для надлежащей обработки ошибок.
источник
Я думаю, что второстепенная вещь, которую следует здесь отметить, - одна из причин, по которой вы хотите, чтобы вы могли отформатировать сообщение об ошибке, чтобы его мог увидеть пользователь.
Я от всей души рекомендую вам:
а) показать конечному пользователю одно и то же общее сообщение об ошибке для каждой возникающей ошибки.
б) зарегистрировать фактическое исключение где-нибудь, к которому могут получить доступ только разработчики (если они находятся на сервере), или куда-то, что может быть отправлено вам с помощью инструментов сообщения об ошибках (если клиент развернут)
в) не пытайтесь форматировать подробности исключений ошибок, которые вы регистрируете, если вы не можете добавить более полезную информацию. Вы не хотите случайно «отформатировать» одну полезную информацию, которую вы могли бы использовать для отслеживания проблемы.
Вкратце - исключения полны очень полезной технической информации. Ничто из этого не должно быть для конечного пользователя, и вы потеряете эту информацию на свой страх и риск.
источник