3.4 KiB
Fix Customer Email Duplicate Error
Problem
The unique index on Customers.Email was enforcing global uniqueness (across all companies), but in a multi-tenant system, different companies should be able to have customers with the same email address.
Solution
Change the unique index to be scoped to CompanyId, allowing the same email across different companies while still preventing duplicates within the same company.
Quick Fix (Run SQL Script)
Step 1: Run the SQL Script
- Open SQL Server Management Studio
- Connect to your testing server
- Open the file:
fix-customer-email-index.sql - Execute the script
This will:
- ✅ Drop the old global unique index
- ✅ Create new company-scoped unique index
- ✅ Show verification that it worked
Step 2: Test Seeding
- Go to your web app:
/SeedData - Click "Seed Company Data"
- Should work perfectly now! ✨
Alternative: Use EF Migration (For Production Deployment)
If you want to use EF migrations for a cleaner deployment:
From Web Project Directory:
cd src/PowderCoating.Web
# Apply the migration
dotnet ef database update --project ../PowderCoating.Infrastructure
This will apply the migration: FixCustomerEmailIndexForMultiTenancy
What Changed
Before (Old Index):
CREATE UNIQUE INDEX IX_Customers_Email ON Customers (Email)
WHERE [Email] IS NOT NULL
❌ Problem: Only ONE customer across ALL companies can have john.smith@acmemfg.com
After (New Index):
CREATE UNIQUE INDEX IX_Customers_Email ON Customers (CompanyId, Email)
WHERE [Email] IS NOT NULL AND [IsDeleted] = 0
✅ Solution: EACH company can have a customer with john.smith@acmemfg.com
Examples
Now This Works:
| CompanyId | Status | |
|---|---|---|
| 1 | john.smith@acmemfg.com | ✅ OK |
| 2 | john.smith@acmemfg.com | ✅ OK (different company) |
| 1 | jane.doe@example.com | ✅ OK |
This Still Prevents Duplicates:
| CompanyId | Status | |
|---|---|---|
| 1 | john.smith@acmemfg.com | ✅ First insert OK |
| 1 | john.smith@acmemfg.com | ❌ DUPLICATE (same company) |
Verification
After running the script, verify the index:
-- Check the new index definition
SELECT
i.name AS IndexName,
i.is_unique AS IsUnique,
STRING_AGG(COL_NAME(ic.object_id, ic.column_id), ', ') AS IndexColumns,
i.filter_definition AS Filter
FROM sys.indexes i
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE i.object_id = OBJECT_ID('Customers')
AND i.name = 'IX_Customers_Email'
GROUP BY i.name, i.is_unique, i.filter_definition
Expected Result:
- IndexName:
IX_Customers_Email - IsUnique:
1(true) - IndexColumns:
CompanyId, Email - Filter:
[Email] IS NOT NULL AND [IsDeleted] = 0
Build & Deploy
The migration is already in your code:
src/PowderCoating.Infrastructure/Migrations/
└─ 20260211160000_FixCustomerEmailIndexForMultiTenancy.cs
When you deploy to production:
dotnet ef database update --project ../PowderCoating.Infrastructure
Will automatically apply this migration.
Summary
✅ Index Fixed - Scoped to CompanyId ✅ Multi-Tenancy Safe - Same email OK across companies ✅ Duplicate Prevention - Still blocks duplicates within a company ✅ Soft Delete Aware - Ignores deleted records
You're ready to seed! 🎉