Ho progettato un database a istanza singola per un'applicazione multi-tenant che utilizza le chiavi composite per imporre la separazione dei tenant al livello del database (evitando così relazioni errate di terzo grado).
Quindi lo schema assomiglia a questo:
TABLE Tenants (
    TenantId int           NOT NULL IDENTITY(1,1)
    Name     nvarchar(100) NOT NULL
    IsAdmin  bit           NOT NULL
    PRIMARY KEY( TenantId )
)
TABLE Users (
    TenantId int NOT NULL
    UserId   int NOT NULL IDENTITY(1,1)
    UserName nvarchar(100) NOT NULL
    PRIMARY KEY( TenantId, UserId )
    CONSTRAINT FK_Tenants_Users FOREIGN KEY ( TenantId ) REFERENCES Tenants ( TenantId )
)
TABLE Documents (
    TenantId   int NOT NULL
    DocumentId int NOT NULL IDENTITY(1,1)
    CreatedByUserId  int NOT NULL
    ModifiedByUserId int NOT NULL
    PRIMARY KEY ( TenantId, DocumentId )
    CONSTRAINT FK_Tenants_Documents   FOREIGN KEY ( TenantId )             REFERENCES Tenants ( TenantId )
    CONSTRAINT FK_Documents_Creators  FOREIGN KEY ( TenantId, CreatedBy )  REFERENCES Users   ( TenantId, CreatedBy )
    CONSTRAINT FK_Documents_Modifiers FOREIGN KEY ( TenantId, ModifiedBy ) REFERENCES Users   ( TenantId, ModifiedBy )
)
 Ci sono molte altre tabelle nel sistema, ma tutte condividono lo stesso concetto in cui se un'entità "appartiene" a un tenant, la chiave primaria di quell'entità è composta e include   TenantId   . 
 ... che aiuta a prevenire situazioni in cui   Document    di   ModifiedByUserId    potrebbe fare riferimento a   UserId    in un altro titolare. Dato che gli inquilini devono essere completamente separati, questa applicazione è l'ideale. 
 Tranne ... come dovrebbero accadere le azioni amministrative? Nota che in questo sistema un Tenant può essere contrassegnato come   IsAdmin    che ha lo scopo di dare loro la possibilità non solo di accedere alle risorse di ciascun Tenant, ma anche di modificare e creare risorse - il che è un problema perché una% di amministrazione diTenantId sarebbe diversa - quindi un utente amministratore non può creare una risorsa in un altro tenancy contrassegnato come proveniente da tale utente. 
 ... almeno per lo scenario in cui un amministratore modifica una risorsa esistente, il valore   ModifiedByUserId    potrebbe rimanere invariato. 
Quindi per abilitare questo scenario (creando nuove risorse in un altro tenant) ho alcune opzioni:
-  Dai a ogni inquilino una voce "Utente fantasma" che rappresenta le azioni amministrative. Il rovescio della medaglia è che confonde il set-set   Usersavendo un'entità che in realtà non rappresenta un utente umano (quindi cosa impostare per cose come nome, email, ecc.). Un lieve vantaggio di questo approccio rispetto all'opzione 2 (sotto) è che mantiene la segregazione completa degli inquilini, anche con i titolari amministrativi, in modo che una locazione possa essere ridotta a un'altra istanza DB senza conflitti di identità o riferimenti interrotti.
-  Rimuovi   TenantIddalle chiavi primarie composte diUser- lo svantaggio è che indebolisce la segregazione dei tenant.
- Ruba l'ID utente di un utente esistente nel tenant, forse l'account utente associato al titolare della proprietà, ma ciò comporterebbe una cattiva pista di controllo in quanto non sarebbe "loro" che ha apportato la modifica.
Ci sono altre opzioni per gestire questa situazione?
Mi sto appoggiando all'opzione 1 (utenti Ghost) ma non mi sembra giusto - e aggiunge anche complessità in quanto la logica del caso speciale dovrebbe impostare il valore della chiave esterna non per l'utente corrente, ma per il fantasma -utente, se l'utente corrente appartiene a un titolare dell'amministratore:
resource.CreatedByUserId = currentUser.TenantId == resource.TenantId ? currentUser.UserId ? getGhostUserId( resource.TenantId )