Start here. This is the direct spoken answer to practice first.
Why this question matters
Duplicate checks are a classic source of race conditions. EF Core code often looks correct until two requests hit the same path at the same time.
I would keep the database unique constraint as the final authority. The API can check first to return a nicer message, but two requests can pass that check concurrently. When SaveChanges hits the unique constraint, EF Core raises a database update exception. I would catch the specific constraint violation, map it to a clear conflict or validation response, and avoid returning a generic 500.