6.5 KiB
Multi-Tenancy Deployment Guide
Pre-Deployment Checklist
- Code review completed
- All tests passing
- Database backup created
- Rollback plan prepared
- Downtime window scheduled (if needed)
Deployment Steps
Step 1: Backup Database
# SQL Server backup example
BACKUP DATABASE PowderCoatingDb
TO DISK = 'C:\Backups\PowderCoatingDb_PreMultiTenancy.bak'
WITH FORMAT, COMPRESSION;
Step 2: Build Solution
cd Y:\PCC\PowderCoatingApp
dotnet build
Ensure no build errors.
Step 3: Review Migration
Check the migration file to understand what will happen:
# View the migration
cat src/PowderCoating.Infrastructure/Migrations/20260205220415_AddMultiTenancy.cs
The migration will:
- Create
Companiestable - Add
CompanyIdcolumns to all entities - Add
CompanyRoletoAspNetUsers - Create foreign keys and indexes
Step 4: Apply Migration
cd src/PowderCoating.Web
dotnet ef database update --project ../PowderCoating.Infrastructure
Expected output:
Applying migration '20260205220415_AddMultiTenancy'.
Done.
Step 5: Run Seed Data
The seed data will run automatically on application startup and will:
- Create default "Demo Company"
- Create SuperAdmin user
- Create CompanyAdmin user
- Create Manager user
Start the application:
dotnet run --project src/PowderCoating.Web
Watch the logs for:
Company 'Demo Company' created
User 'superadmin@powdercoating.com' created
User 'admin@demo.com' created
User 'manager@demo.com' created
Step 6: Verify Migration Success
Connect to the database and verify:
-- Check Companies table exists
SELECT * FROM Companies;
-- Check CompanyId added to entities
SELECT TOP 5 Id, CompanyId FROM Customers;
SELECT TOP 5 Id, CompanyId FROM Jobs;
-- Check ApplicationUser has CompanyId
SELECT TOP 5 Id, Email, CompanyId, CompanyRole FROM AspNetUsers;
-- Verify foreign keys
SELECT
fk.name AS ForeignKeyName,
OBJECT_NAME(fk.parent_object_id) AS TableName,
COL_NAME(fkc.parent_object_id, fkc.parent_column_id) AS ColumnName,
OBJECT_NAME (fk.referenced_object_id) AS ReferencedTableName
FROM
sys.foreign_keys AS fk
INNER JOIN sys.foreign_key_columns AS fkc
ON fk.object_id = fkc.constraint_object_id
WHERE
OBJECT_NAME(fk.referenced_object_id) = 'Companies';
Step 7: Test Login
Test each user account:
-
SuperAdmin Login
- URL: https://localhost:5001/Identity/Account/Login
- Email: superadmin@powdercoating.com
- Password: SuperAdmin123!
- Should see: "Companies" menu in Platform Management
-
Company Admin Login
- URL: https://localhost:5001/Identity/Account/Login
- Email: admin@demo.com
- Password: CompanyAdmin123!
- Should see: "Manage Users" menu in Company Settings
- Should see: "Demo Company" badge in header
-
Manager Login
- URL: https://localhost:5001/Identity/Account/Login
- Email: manager@demo.com
- Password: Manager123!
- Should see: "Demo Company" badge in header
- Should NOT see: "Manage Users" menu
Step 8: Verify Data Isolation
-
As SuperAdmin, create a second company:
- Navigate to Companies → Create New Company
- Company Name: "Test Company"
- Create admin user for Test Company
-
Login as Test Company Admin
- Verify you only see Test Company data
- Create a test customer
-
Login as Demo Company Admin
- Verify you CANNOT see Test Company customer
- Verify you only see Demo Company data
-
Login as SuperAdmin
- Navigate to Customers
- Should see customers from BOTH companies
Post-Deployment Verification
Functional Tests
- SuperAdmin can create new companies
- SuperAdmin can see all companies' data
- Company Admin can create users in their company
- Company Admin cannot see other companies' data
- Users can only see data from their own company
- New entities automatically get CompanyId assigned
- Query filters work correctly
Performance Tests
- Page load times are acceptable
- No N+1 query issues
- Indexes are being used (check query plans)
Security Tests
- Cannot access other company's data by changing IDs in URL
- Authorization policies enforce correctly
- Query filters cannot be bypassed by regular users
Troubleshooting
Issue: Migration Fails
Error: Foreign key constraint conflicts
This means existing data has CompanyId=0 which doesn't exist.
Solution:
-- Check if default company exists
SELECT * FROM Companies WHERE Id = 1;
-- If no company exists, seed data didn't run
-- Run application to trigger seed, OR manually:
INSERT INTO Companies (CompanyName, CompanyCode, PrimaryContactName, PrimaryContactEmail,
IsActive, SubscriptionStartDate, CreatedAt, CompanyId)
VALUES ('Demo Company', 'DEMO', 'Admin', 'admin@demo.com',
1, GETUTCDATE(), GETUTCDATE(), 0);
-- Update CompanyId to self-reference
UPDATE Companies SET CompanyId = Id WHERE Id = 1;
-- Update existing data
UPDATE Customers SET CompanyId = 1 WHERE CompanyId = 0;
UPDATE Jobs SET CompanyId = 1 WHERE CompanyId = 0;
-- ... repeat for all entities
Issue: Users Can't Login
Error: "Unable to determine your company"
This means the user's CompanyId is not set or CompanyId claim is missing.
Solution:
-- Check user's CompanyId
SELECT Id, Email, CompanyId FROM AspNetUsers WHERE Email = 'user@example.com';
-- Update if needed
UPDATE AspNetUsers SET CompanyId = 1 WHERE CompanyId = 0 OR CompanyId IS NULL;
Then ensure user re-logs in to get fresh claims.
Issue: Seeing Other Company's Data
This means query filters aren't working.
Check:
- Is
ITenantContextregistered in DI? - Is
ApplicationDbContextreceivingIHttpContextAccessorandIServiceProvider? - Are query filters being applied in
OnModelCreating? - Is the user authenticated and has CompanyId claim?
Rollback Procedure
If critical issues occur:
# Rollback migration
cd src/PowderCoating.Web
dotnet ef database update 20260205163837_InitialCreate --project ../PowderCoating.Infrastructure
# Restore database backup
RESTORE DATABASE PowderCoatingDb
FROM DISK = 'C:\Backups\PowderCoatingDb_PreMultiTenancy.bak'
WITH REPLACE;
# Revert code changes
git revert <commit-hash>
Support Contacts
- Lead Developer: [Your Name]
- Database Admin: [DBA Name]
- DevOps: [DevOps Contact]
Post-Deployment Tasks
- Monitor application logs for errors
- Monitor database performance
- Update documentation
- Train users on new multi-tenancy features
- Schedule follow-up review (1 week)