Tendo duas classes:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
Ao atribuir ChildId
a Parent
, devo verificar primeiro se ele existe no banco de dados ou aguardar que o banco de dados gere uma exceção?
Por exemplo (usando o Entity Framework Core):
OBSERVAÇÃO: esse tipo de verificação é POR TODA A INTERNET, mesmo nos documentos oficiais da Microsoft: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using- mvc / manipulação-simultaneidade-com-a-entidade-estrutura-em-um-asp-net-mvc-application # modify-the-department-controller mas há tratamento de exceção adicional paraSaveChanges
Além disso, observe que o principal objetivo dessa verificação era retornar mensagens amigáveis e status HTTP conhecido ao usuário da API e não ignorar completamente as exceções do banco de dados. E a única exceção lançada é dentro SaveChanges
ou SaveChangesAsync
chamada ... para que não haja nenhuma exceção quando você ligar FindAsync
ou Any
. Portanto, se o filho existir, mas foi excluído antes SaveChangesAsync
, a exceção de simultaneidade será lançada.
Fiz isso devido ao fato de que a foreign key violation
exceção será muito mais difícil de formatar para exibir "Não foi possível encontrar o filho com o 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;
}
versus:
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;
}
O segundo exemplo no Postgres gerará um 23503 foreign_key_violation
erro: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
A desvantagem de lidar com exceções dessa maneira no ORM, como o EF, é que ele funcionará apenas com um back-end de banco de dados específico. Se você quiser mudar para o servidor SQL ou algo mais, isso não funcionará mais porque o código de erro será alterado.
Não formatar a exceção corretamente para o usuário final pode expor algumas coisas que você não quer que ninguém, exceto os desenvolvedores, vejam.
Relacionado:
https://stackoverflow.com/questions/308905/should-there-be-a-transaction-for-read-queries
Child with id {parent.ChildId} could not be found.
. E formatação "Violação de chave estrangeira" eu acho que é pior neste caso.