Having two classes:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
When assigning ChildId
to Parent
should I check first if it exists in the DB or wait for the DB to throw an exception?
For example (using Entity Framework Core):
NOTE these kinds of checks are ALL OVER THE INTERNET even on official Microsoft's docs: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application#modify-the-department-controller but there is additional exception handling for SaveChanges
also, note that the main intent of this check was to return friendly message and known HTTP status to the user of the API and not to completely ignore database exceptions. And the only place exception be thrown is inside SaveChanges
or SaveChangesAsync
call... so there won't be any exception when you call FindAsync
or Any
. So if child exists but was deleted before SaveChangesAsync
then concurrency exception will be thrown.
I did this due to a fact that foreign key violation
exception will be much harder to format to display "Child with id {parent.ChildId} could not be found."
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;
}
The second example in Postgres will throw 23503 foreign_key_violation
error: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
The downside of handling exceptions this way in ORM like EF is that it will work only with a specific database backend. If you ever wanted to switch to SQL server or something else this will not work anymore because the error code will change.
Not formatting the exception properly for the end-user could expose some things you don't want anybody but developers to see.
Related:
https://stackoverflow.com/questions/308905/should-there-be-a-transaction-for-read-queries
Child with id {parent.ChildId} could not be found.
. And formatting "Foreign key violation" I think is worse in this case.