2010-05-30 16 views
7

Sto osservando la progettazione di schemi di database multi-tenancy per un concetto SaaS. Sarà ASP.NET MVC -> EF, ma non è così importante.Entity Framework e progettazione di database multi-tenancy

Di seguito è possibile vedere uno schema di database di esempio (il titolare è la società). CompanyId viene replicato in tutto lo schema e la chiave primaria è stata posizionata sia sulla chiave naturale, sia sull'ID tenant.

Inserendo questo schema in Entity Framework dà i seguenti errori quando aggiungo le tabelle nel file Entity Model (Model1.edmx):

  • Il rapporto 'FK_Order_Customer' utilizza il set di chiavi esterne '{CustomerId, CompanyId}' che sono parzialmente contenuti nel set di chiavi primarie '{OrderId, CompanyId}' della tabella 'Ordine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuto nel set di chiavi primarie da mappare su un modello.
  • La relazione 'FK_OrderLine_Customer' utilizza l'insieme di chiavi esterne '{CustomerId, CompanyId}' che sono parzialmente contenute nel set di chiavi primarie '{OrderLineId, CompanyId}' della tabella 'OrderLine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuto nel set di chiavi primarie da mappare su un modello.
  • La relazione 'FK_OrderLine_Order' utilizza il set di chiavi esterne '{OrderId, CompanyId}' che sono parzialmente contenute nel set di chiavi primarie '{OrderLineId, CompanyId}' della tabella 'OrderLine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuti nella serie di chiavi primarie essere associato a un modello.
  • Il rapporto 'FK_Order_Customer' utilizza il set di chiavi esterne '{CustomerId, CompanyID}' che sono parzialmente contenute nel set di chiavi primarie '{OrderId, CompanyID}' del 'ordine' tavolo. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuto nel set di chiavi primarie da mappare su un modello.
  • La relazione 'FK_OrderLine_Customer' utilizza l'insieme di chiavi esterne '{CustomerId, CompanyId}' che sono parzialmente contenute nel set di chiavi primarie '{OrderLineId, CompanyId}' della tabella 'OrderLine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuti nella serie di chiavi primarie essere associato a un modello.
  • La relazione 'FK_OrderLine_Order' utilizza il set di chiavi esterne '{OrderId, CompanyId}' che sono parzialmente contenute nel set di chiavi primarie '{OrderLineId, CompanyId}' della tabella 'OrderLine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuto nel set di chiavi primarie da mappare su un modello.
  • La relazione 'FK_OrderLine_Product' utilizza il set di chiavi esterne '{ProductId, CompanyId}' che sono parzialmente contenute nel set di chiavi primarie '{OrderLineId, CompanyId}' della tabella 'OrderLine'. Il set di chiavi esterne deve essere completamente contenuto nel set di chiavi primarie, o completamente non contenuti nella serie di chiavi primarie essere associato a un modello.

La domanda è in due parti:

  1. È il mio progettazione di database non è corretto? Dovrei astenermi da queste chiavi primarie composte? Sto mettendo in discussione la mia sanità mentale per quanto riguarda il disegno dello schema fondamentale (sindrome del cervello frazzled). Non esitate a suggerire lo schema 'idealizzato'.
  2. In alternativa, se la struttura del database è corretta, EF non è in grado di far corrispondere le chiavi perché percepisce queste chiavi esterne come un potenziale rapporto errato 1: 1 (non corretto)? In tal caso, si tratta di un bug EF e come posso aggirarlo?

Multi-tenancy database schema http://i46.tinypic.com/23si52u.png

+0

Se rimuovo le chiavi primarie composite e uso solo le chiavi naturali (ProductId, OrderId, CustomerId, OrderLineId), l'errore EF scompare. Tuttavia, non sono sicuro se questo è solo spalare la merda sotto il tappeto! – Junto

+0

Una chiave primaria deve soddisfare due requisiti. Innanzitutto, deve essere unico. Secondo, per la normalizzazione, tutti gli elementi non chiave devono essere completamente dipendenti dalla chiave primaria. Alcuni dei tasti composti interrompono la normalizzazione in modo piuttosto grave, perché sembra che un componente della chiave composta dipenda dall'altra parte della chiave composta. È un grosso rischio con le chiavi composte. Quindi, per rispondere alla tua preoccupazione, no, non è * solo * spalare merda sotto il tappeto! –

risposta

4

Su una rapida scansione dei messaggi di errore di EF, chiaramente non piace il modo in cui si sta impostando chiavi composte, e penso che sia probabilmente spingendo nella giusta direzione. Rifletti su ciò che rende le tue chiavi primarie uniche. Il OrderID da solo non è univoco, senza un CompanyID? Un ID prodotto non è univoco, senza un ID azienda? Una OrderLine certamente dovrebbe essere unica senza un CompanyID, dal momento che un OrderLine dovrebbe essere associato solo a un singolo Ordine.

Se hai veramente bisogno di CompanyID per tutti questi, il che probabilmente significa che l'azienda in questione ti fornisce ProductID e OrderID, allora potresti voler andare in una direzione diversa e generare le tue chiavi primarie che non sono intrinseco ai dati. È sufficiente impostare una colonna di incremento automatico per la chiave primaria e lasciare che siano l'OrderID interno, OrderLineID, ProductID, CompanyID, ecc. A quel punto, OrderLine non avrà bisogno del OrderID o del CompanyID del cliente; il riferimento chiave esterna all'Ordine sarebbe il suo punto di partenza. (E CustomerID non dovrebbe mai essere un attributo di una riga di ordine, è un attributo dell'ordine, non la riga dell'ordine.)

I tasti composti sono solo disordinati. Prova a progettare il modello senza di loro e vedi se semplifica le cose.

+0

Sono d'accordo sui tasti composti. Non sono nemmeno sicuro del motivo per cui li ho aggiunti in primo luogo! La programmazione a tarda notte non è mai una buona idea per me. – Junto

+0

Ho intenzione di dare la risposta a Cylon Cat piuttosto che EJB, principalmente perché ha innescato i miei processi mentali sul motivo per cui avevo aggiunto le chiavi composte in primo luogo (erroneamente). Grazie ad entrambi. – Junto

+0

Non sono d'accordo. La causa dell'errore sono i campi che Junto non ha usato quando ha creato le relazioni tra le tabelle. L'ID azienda in ogni tabella aiuta molto nei siti multi-tenant. –

2

Penso che memorizzare il numero di azienda in ciascuna tabella ti faccia male più che aiutare. Posso capire perché vuoi farlo (dato che il programmatore/dba puoi semplicemente andare in qualsiasi tabella e 'vedere' quali dati appartengono a chi è confortante), ma sta ostacolando l'impostazione del database dovrebbe essere.

Evita le chiavi composte e il tuo design diventa molto più semplice.

+0

concordato. Tasti composti bloccati. – Junto

2

Penso che l'errore non sia nel progetto. Non è nell'EF. È in relazione Sql Server.

Leggere il messaggio EF:

Il rapporto 'FK_Order_Customer' utilizza il set di chiavi esterne '{CustomerId, CompanyID}' che sono parzialmente contenute nel set di chiavi primarie '{ OrderId, CompanyId} 'di la tabella' Ordine '. Il set di chiavi estere deve essere completamente contenuto nel set di chiavi primarie o completamente non contenuto nel set di chiavi primarie da mappare su un modello.

ERRORE

actualy il rapporto betwen Ordine e uso del cliente solo un campo (probabilmente è stato trascinato con il mouse sul campo "CustomerId" da teh tabella Ordine al "Id" della tabella Clienti)

SOLUZIONE

clic destro sul filo che collega ordine e clienti e nel rapporto aggiunge anche la CompanyID


PS: Il design è corretto.

Inserire l'CompanyId in ogni tabella è la soluzione rith in architettura multi-tenant perché aiuta a ridimensionare (solitamente si desidera sempre selezionare solo i record dalla società registrata).

+0

non esiste una verità assoluta né nell'istruzione "Non usare chiavi composte" non in "Usa sempre per multitenancy". dipende dallo scopo del database e dall'utilizzo dello scenario. nei database DWH vorrei usare chiavi composte. in OLTP probabilmente creerei comunque la colonna CompanyID in ogni tabella che rappresenta un'entità root con un indice non cluster ma non vedo perché ho bisogno di farne una parte della chiave ... e potrebbe non essere aggiunta alle entità non-root come le righe di ordine –

0

Se è necessario aggiungere assolutamente colonna CompanyID a ogni tabella, aggiungerla come colonna normale e non come chiave composta. La chiave composita è usata principalmente quando devi implementare relazioni molte a molte.

Come qualcuno ha menzionato anche creare un indice non cluster su CompanyID in modo che i join per la tabella della società siano beneficiati.

Grazie!

0

Primo: come altri hanno detto, quando si fa riferimento a una chiave esterna, utilizzare l'intera chiave primaria nell'altra tabella (cioè entrambi i campi).

In secondo luogo, non riesco a immaginare di non utilizzare una colonna CompanyID nella maggior parte delle tabelle in un'applicazione seria. Orderdetail potrebbe forse essere un'eccezione in questo caso (anche tabelle di ricerca globali forse, a meno che non siano tenant dipendenti). Il fatto è che non puoi fare alcun tipo sicuro di ricerca in forma libera su una tabella senza aggiungere il CompanyID, o fare JOINs fino a quando non raggiungi una tabella che ha quella colonna. Quest'ultima ovviamente costa delle prestazioni. Forse in questo caso potresti fare un'eccezione per orderdetail e cercare solo nella versione unita (solo due tabelle). Poi di nuovo, non è davvero coerente.

Anche per quanto riguarda la creazione di una chiave composta o meno: è possibile, ma apre la possibilità che un bug scriva informazioni errate (in inesistenti o amministrazioni di altre persone) per la durata del bug. Prova a sistemarlo in produzione, per non parlare del fatto che spieghi ai clienti il ​​motivo per cui stanno vedendo gli ordini della concorrenza nel loro sistema.

+0

Dopo aver giocato con gli ORM server, sono giunto alla conclusione che quando si usano le tabelle di join in relazioni n: m, probabilmente queste potrebbero essere escluse dall'avere il tenantid (almeno per lo più). Gli ORM tendono a dare problemi con questo e, in realtà, qualsiasi query che coinvolge una tabella di join avrà almeno un'altra tabella contenente un tenantid. Molto raramente bisognerebbe interrogarli da soli, risolvendoli facilmente unendoli ad un tavolo adiacente. – user2921878