Associazioni eterogenee - Data Modeling quando un oggetto che deve essere correlato a una delle molte classi

6

Supponiamo di avere 3 classi: ClassA , ClassB e ClassC . Supponiamo ora di avere una classe Message . Vuoi una relazione one-to-many tra ciascuna di queste tre classi e il Messaggio.

L'obiettivo è che un Message deve "appartenere" a un ClassA , ClassB o a ClassC oggetto. Un oggetto ClassA / ClassB / ClassC può avere zero o molti Messages . La classe Message è la stessa per ognuna delle tre classi (cioè non dovrebbe esserci una diversa classe di Message per ognuna delle tre Classi A, B e C).

Considerando ora la modellazione dei dati su un DB relazionale (inoltre, sto usando ORM, in questo caso Entity Framework Code First ), mapperò ognuna di quelle 3 classi e il Message class a una tabella.

Ma poi, il problema sta cercando di applicare le relazioni che ho spiegato sopra. Vedo alcuni modi per farlo:

  • Aggiungi tre chiavi esterne nullable ( ClassAID , ClassBID , ClassCID ) alla tabella dei messaggi. per esempio. Se un messaggio appartiene a un oggetto ClassA , quindi ClassAID avrà l'ID di tale obetto come valore e ClassBID e ClassCID saranno nulli. Questo mi costringerà a scrivere un vincolo / trigger per assicurarmi che l'oggetto Message abbia solo un valore in uno di quegli FK. Inoltre rende un po 'brutto nei livelli Business Logic e Presentation ogni volta che voglio sapere a quale oggetto appartiene un messaggio: il MessageObject avrebbe un riferimento nullable a un oggetto ClassA, ClassB e ClassC e dovrei controllare prima di presentarlo nell'interfaccia utente (ad es. un link dal messaggio al suo oggetto).

  • Implemental'ereditarietàegeneralizzaClassA,ClassBeClassC.IlmessaggiohaunFKperl'IDdellaclassegeneralizzata.CiòaggiungecomplessitàeinrealtànonrisolveilproblemadicapirefacilmenteaqualeClasseA/B/CècorrelatounMessaggio.

  • Aggiungi una tabella ClassToMessage (simile a una tabella di collegamento) che collega le 3 tabelle a quella del messaggio, nuovamente, tramite FK nullable. Una specie di combinazione dei due precedenti, ma non ancora esattamente una buona soluzione per gli stessi motivi.

Mipiacerebbesaperesec'èunmodoeleganteperraggiungereloscenariodesiderato,preferibilmenteavendoinmentel'approcciodiEntiyFramework.Grazieinanticipo.

MODIFICA1:

ImmaginaglioggettiMessaggiocomecommenti(ofile)inunastrutturaadalberodioggetti(glioggettiClassApossonoavereClassBeClassBpossonoavereClassC's...).Sidesideracheglioggettiditutteleclassiabbianozeroopiùcommenti.Vuoilastessatabelladicommentipertuttiicommenti(nonimportaseappartengonoaClassAoClassB...istanza).Vuoianchechesiafaciletrovareicommentiperundatooggettool'oggettoacuiappartieneuncommento.

EDIT2:

Altreduealternative:

  • Implementalatabellapergerarchia(TPH)avendounClassAMessage,ClassBMessage...cheancoranonrisolveilproblema...

  • Il messaggio memorizza l'ID dell'oggetto che lo possiede e il Tipo oggetto (indipendentemente dal fatto che sia stato un ClassA, ClassB ...). Nessuna relazione effettiva: livello di servizio o logica aziendale fa il resto (ahi).

HovistoquestoproblemaaffrontatoinStackOverflow,manonriescoatrovareunabuonasoluzione:

EDIT 3:

Sto perseguendo l'alternativa n. 4 (Tabella per gerarchia / ereditarietà delle tabelle di classe) e l'ho evoluta in un S.O. specifico. domanda: Entity Framework 6 Code First - Commenti su una struttura ad albero

    
posta user1987392 28.10.2014 - 16:15
fonte

4 risposte

1

Quello che hai chiamato "Tabella per gerarchia" assomiglia a quello che Martin Fowler e altri hanno chiamato "Class Table Ereditarietà". Vedi schema . Chiamerei questo emulare l'ereditarietà piuttosto che implementare l'ereditarietà.

Una tecnica che può essere utilizzata in modo vantaggioso in alcuni casi di ereditarietà delle tabelle di classe è chiamata "chiave primaria condivisa". Vedi tag wiki in SE.

Il vantaggio se la chiave primaria condivisa è che un riferimento FK che fa riferimento alla chiave primaria condivisa può fare riferimento sia alla tabella della classe generica che alla voce nella tabella della classe specializzata appropriata. Questo risulta molto utile e può giustificare la programmazione aggiuntiva necessaria per implementare la chiave primaria condivisa.

    
risposta data 29.10.2014 - 12:52
fonte
1

Fa la differenza rispetto a ciò che ClassA e ClassB veramente rappresentano; i nomi generici non sono utili per prendere decisioni di progettazione corrette. Ad esempio, se queste sono cose che possono o dovrebbero essere modellate con una classe base comune, con una vera relazione "è-a", segui questa strada.

Se ClassA e ClassB non hanno nulla in comune tranne quel messaggio, l'ereditarietà probabilmente non è l'approccio migliore. In questo caso, le soluzioni 1 e 3 sono entrambe possibili. Quale è più adeguato dipende da come "impermeabile" hai bisogno del tuo database, e che tipo di pattern di accesso e query ti aspetti.

    
risposta data 28.10.2014 - 17:53
fonte
1

Stai andando con quell'ultima opzione nella tua modifica ma modificandola un po '.

Inquestohaisottrattolemodificheallaclasseconcreta.

Neltuodatabase,tuttavia,staisalvandoilvalorediunaenumerazionechesalvailtipodiclasseconcretacuitistairiferendoechecos'èl'IDdellaclasseconcreta.Questeinformazionipossonoesserepassateallafabbrica,chedovrebbefornireunmetodocherestituisceunagenericaclassechepuòessereunaqualsiasidelleclassiconcretebasatesuenumeidprecedenti.

-Message+intId+intTypeEnum+intClassId-Class+intId+intTypeEnum

Class.TypeEnumrappresenterebbelarisorsa"libera" su Message.TypeEnum.

In questo modo non devi preoccuparti di dover aggiungere colonne per chiavi esterne nullable aggiuntive.

Come ho sottolineato: dovrai tenere conto che i vincoli del database (chiavi esterne) tra la classe Message e ClassA, ClassB, ... ClassX non possono essere utilizzati e dovrebbero essere gestiti manualmente.

    
risposta data 29.10.2014 - 11:41
fonte
-1

Sembra che tu stia avendo esattamente lo stesso tipo di problema che mi ha portato ad adottare un database di oggetti piuttosto che relazionale in uno dei miei progetti attuali.

Utilizzando un database di oggetti, potresti semplicemente avere Message con un riferimento non nullable su MessageClass oggetto, con ClassA , ClassB e ClassC come sottotipi di MessageClass . Anche se questo è fondamentalmente lo stesso di uno dei tuoi suggerimenti, il semplice fatto è che mentre questo è difficile da implementare usando SQL (e impossibile ottenere corretti limiti di integrità referenziale per, se le diverse classi richiedono tipi diversi di dati) è banale fare in un database di oggetti.

Sto utilizzando db4o e trovandolo abbastanza utile per questo tipo di attività.

    
risposta data 30.10.2014 - 11:59
fonte

Leggi altre domande sui tag