Skip to content

Add missing uniqueness constraints for Recipe, Ingredient, Unit, and UnitType#1059

Merged
dgee2 merged 3 commits into
mainfrom
dgee2/issue-979-add-missing-uniqueness-constraints-and-m-bff254
May 23, 2026
Merged

Add missing uniqueness constraints for Recipe, Ingredient, Unit, and UnitType#1059
dgee2 merged 3 commits into
mainfrom
dgee2/issue-979-add-missing-uniqueness-constraints-and-m-bff254

Conversation

@dgee2

@dgee2 dgee2 commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Hardens the database schema by adding unique constraints where business uniqueness is confirmed but was previously unenforced.

Changes

New unique indexes (migration: AddUniqueNameConstraints)

Table Column Index Name Notes
Recipe Name UX_Recipe_Name
Ingredient Name UX_Ingredient_Name
UnitType Name UX_UnitType_Name Seeded reference data - zero migration risk
Unit Name UX_Unit_Name Seeded reference data - zero migration risk
Unit Abbreviation UX_Unit_Abbreviation Filtered index (WHERE NOT NULL); seeded data - zero migration risk

Application-layer violation handling

  • Added DbUpdateExceptionExtensions.IsUniqueConstraintViolation() helpers (SQL Server error codes 2627/2601) for both DbUpdateException and SqlException
  • RecipeRepository.CreateRecipeAsync catches unique constraint violations and surfaces them as BusinessValidationException -> 422 Unprocessable Entity
  • RecipeRepository.UpdateRecipeAsync catches unique constraint violations and surfaces them as BusinessValidationException -> 422 Unprocessable Entity
  • IngredientRepository.CreateIngredientAsync uses a lookup-before-insert (idempotent) pattern from PR Prevent duplicate inserts for canonical/reference rows #978: duplicate ingredient creation returns 200 OK with the existing ingredient. A race-condition catch block detaches the failed entity and re-queries to return the existing row cleanly.

Integration tests

  • Duplicate Recipe.Name creation -> 422
  • Duplicate Recipe.Name on update (PUT) -> 422

Migration risk

  • Recipe.Name / Ingredient.Name: If any pre-existing rows share a name the migration will fail. A pre-flight THROW guard is included in the migration script to fail early with a clear, actionable message rather than an opaque SQL error.
  • UnitType, Unit.Name, Unit.Abbreviation: Seeded reference data with no duplicate risk.

Copilot AI review requested due to automatic review settings May 22, 2026 20:16
Comment thread backend/MenuApi.Integration.Tests/IngredientIntegrationTests.cs Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the backend data model by enforcing business-unique names at the database layer (via unique indexes) and translating unique-constraint insert failures into 422 validation responses for key write paths.

Changes:

  • Added unique indexes for Recipe.Name, Ingredient.Name, UnitType.Name, Unit.Name, and filtered unique Unit.Abbreviation.
  • Added SQL Server unique-violation detection (DbUpdateExceptionExtensions.IsUniqueConstraintViolation) and mapped duplicate create attempts to BusinessValidationException (422) for recipes/ingredients.
  • Added integration tests asserting 422 on duplicate Recipe.Name and Ingredient.Name creation.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
backend/MenuDB/Migrations/MenuDbContextModelSnapshot.cs Updates EF snapshot to reflect new unique indexes.
backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.Designer.cs Adds migration designer model including new unique indexes.
backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.cs Creates/drops the new unique indexes in the database.
backend/MenuDB/MenuDbContext.cs Defines unique indexes in the EF model configuration.
backend/MenuApi/Repositories/RecipeRepository.cs Catches unique-constraint insert failures and returns 422 via BusinessValidationException.
backend/MenuApi/Repositories/IngredientRepository.cs Same as above for ingredient creation.
backend/MenuApi/Exceptions/DbUpdateExceptionExtensions.cs Adds helper to detect SQL Server unique constraint/index violations.
backend/MenuApi.Integration.Tests/RecipeIntegrationTests.cs Adds integration test for duplicate recipe-name creation → 422.
backend/MenuApi.Integration.Tests/IngredientIntegrationTests.cs Adds integration test for duplicate ingredient-name creation → 422.
Files not reviewed (1)
  • backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/MenuApi/Repositories/RecipeRepository.cs
Comment thread backend/MenuApi.Integration.Tests/RecipeIntegrationTests.cs
Copilot AI review requested due to automatic review settings May 22, 2026 21:49
@dgee2 dgee2 force-pushed the dgee2/issue-979-add-missing-uniqueness-constraints-and-m-bff254 branch from 9ae5312 to 143aedc Compare May 22, 2026 21:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.Designer.cs: Language not supported

Comment thread backend/MenuApi/Repositories/RecipeRepository.cs
Comment thread backend/MenuApi/Repositories/IngredientRepository.cs
…e, Unit.Name, Unit.Abbreviation

- Add HasIndex(...).IsUnique() in MenuDbContext for all five business-unique columns
- Unit.Abbreviation uses a filtered index (WHERE NOT NULL) to allow multiple nulls
- Generate migration AddUniqueNameConstraints
- Handle DbUpdateException unique constraint violations in IngredientRepository
  and RecipeRepository, surfacing them as BusinessValidationException (422)
- Add DbUpdateExceptionExtensions.IsUniqueConstraintViolation() helper
- Add integration tests for duplicate Recipe.Name and Ingredient.Name creation

Closes #979

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dgee2 dgee2 force-pushed the dgee2/issue-979-add-missing-uniqueness-constraints-and-m-bff254 branch from 143aedc to f9e057d Compare May 22, 2026 21:55
@dgee2 dgee2 requested a review from Copilot May 22, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.Designer.cs: Language not supported

Comment thread backend/MenuApi/Repositories/IngredientRepository.cs
Comment thread backend/MenuApi/Repositories/IngredientRepository.cs
Comment thread backend/MenuApi/Repositories/RecipeRepository.cs
- Add pre-flight THROW guards in AddUniqueNameConstraints migration before
  creating UX_Recipe_Name and UX_Ingredient_Name indexes, so the migration
  fails with a clear actionable message if duplicate rows exist
- Handle unique constraint violations in UpdateRecipeAsync by catching
  SqlException (2627/2601) directly (ExecuteUpdateAsync bypasses DbUpdateException)
  and surfacing as BusinessValidationException -> 422
- Add Update_Recipe_To_Duplicate_Name_Returns_UnprocessableEntity integration test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • backend/MenuDB/Migrations/20260522201428_AddUniqueNameConstraints.Designer.cs: Language not supported

Comment thread backend/MenuApi/Repositories/RecipeRepository.cs Outdated
- IngredientRepository: detach entity and IngredientUnits before re-querying
  in race-condition catch block so the DbContext is left in a clean state
- DbUpdateExceptionExtensions: add IsUniqueConstraintViolation overload for
  SqlException so error codes (2627/2601) are defined in one place only
- RecipeRepository.UpdateRecipeAsync: use the new SqlException extension
  method instead of hardcoded error numbers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@dgee2 dgee2 merged commit 2a0d0d1 into main May 23, 2026
13 checks passed
@dgee2 dgee2 deleted the dgee2/issue-979-add-missing-uniqueness-constraints-and-m-bff254 branch May 23, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants