Files
PowderCoatingLogix/DEPLOYMENT_GUIDE.md
T
2026-04-23 21:38:24 -04:00

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 Companies table
  • Add CompanyId columns to all entities
  • Add CompanyRole to AspNetUsers
  • 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:

  1. SuperAdmin Login

  2. Company Admin Login

  3. Manager Login

Step 8: Verify Data Isolation

  1. As SuperAdmin, create a second company:

    • Navigate to Companies → Create New Company
    • Company Name: "Test Company"
    • Create admin user for Test Company
  2. Login as Test Company Admin

    • Verify you only see Test Company data
    • Create a test customer
  3. Login as Demo Company Admin

    • Verify you CANNOT see Test Company customer
    • Verify you only see Demo Company data
  4. 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:

  1. Is ITenantContext registered in DI?
  2. Is ApplicationDbContext receiving IHttpContextAccessor and IServiceProvider?
  3. Are query filters being applied in OnModelCreating?
  4. 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)