Code First: associazioni indipendenti vs. associazioni di chiavi esterne?


102

Ho un dibattito mentale con me stesso ogni volta che inizio a lavorare su un nuovo progetto e sto progettando i miei POCO. Ho visto molti tutorial / esempi di codice che sembrano favorire le associazioni di chiavi esterne :

Associazione chiave esterna

public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; } // <-- Customer ID
    ...
}

A differenza delle associazioni indipendenti :

Associazione indipendente

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Ho lavorato con NHibernate in passato e ho utilizzato associazioni indipendenti, che non solo sembrano più OO, ma hanno anche (con il caricamento lento) il vantaggio di darmi accesso all'intero oggetto Cliente, invece del suo ID. Questo mi consente, ad esempio, di recuperare un'istanza di Order e quindi di fare a Order.Customer.FirstNamemeno di dover eseguire un join esplicitamente, il che è estremamente conveniente.

Quindi, per ricapitolare, le mie domande sono:

  1. Ci sono svantaggi significativi nell'utilizzo di associazioni indipendenti? e...
  2. Se non ce ne sono, quale sarebbe la ragione per utilizzare le associazioni di chiavi esterne?

Risposte:


106

Se vuoi sfruttare appieno l'ORM, utilizzerai sicuramente il riferimento all'entità:

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Una volta generato un modello di entità da un database con FK, genererà sempre riferimenti a entità. Se non si desidera utilizzarli, è necessario modificare manualmente il file EDMX e aggiungere proprietà che rappresentano FK. Almeno questo era il caso di Entity Framework v1 in cui erano consentite solo associazioni indipendenti.

Entity Framework v4 offre un nuovo tipo di associazione chiamata Associazione chiave esterna. La differenza più ovvia tra l'associazione di chiave indipendente e quella esterna è nella classe Order:

public class Order
{
    public int ID { get; set; }
    public int CustomerId { get; set; }  // <-- Customer ID
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Come puoi vedere hai sia la proprietà FK che il riferimento all'entità. Esistono più differenze tra due tipi di associazioni:

Associazione indipendente

  • È rappresentato come oggetto separato in ObjectStateManager. Ha il suo EntityState!
  • Quando costruisci un'associazione hai sempre bisogno di diritti da entrambe le estremità dell'associazione
  • Questa associazione è mappata allo stesso modo dell'entità.

Associazione chiave esterna

  • Non è rappresentato come oggetto separato in ObjectStateManager. A causa di ciò è necessario seguire alcune regole speciali.
  • Quando si costruisce un'associazione non sono necessarie entrambe le estremità dell'associazione. È sufficiente avere un'entità figlio e PK dell'entità padre, ma il valore PK deve essere univoco. Pertanto, quando si utilizza l'associazione di chiavi esterne, è necessario assegnare anche ID univoci temporanei alle entità appena generate utilizzate nelle relazioni.
  • Questa associazione non è mappata ma definisce invece vincoli referenziali.

Se si desidera utilizzare l'associazione di chiavi esterne, è necessario selezionare Includi colonne di chiavi esterne nel modello in Entity Data Model Wizard.

Modificare:

Ho scoperto che la differenza tra questi due tipi di associazioni non è molto nota, quindi ho scritto un breve articolo che copre questo argomento con maggiori dettagli e la mia opinione su questo.


Grazie per la tua risposta molto perspicace, e anche per l'avvertenza sulla terminologia corretta, che mi ha aiutato a trovare molte risorse sull'argomento e sui pro / contro di entrambe le tecniche.
Daniel Liuzzi

1
Ho appena letto il tuo articolo, Ladislav. Lettura molto interessante e grande risorsa per comprendere ulteriormente la differenza tra questi due approcci. Saluti.
Daniel Liuzzi

1
@ GaussZ: Come so, non c'è stato alcun cambiamento nel modo in cui vengono gestite le associazioni da EF4 (dove sono state introdotte le associazioni FK).
Ladislav Mrnka

1
Questa e le altre risposte non sembrano toccare la preoccupazione per le prestazioni. Tuttavia, in base alla sezione 2.2 Fattori che influiscono sulle prestazioni di generazione di visualizzazioni da un articolo di MSDN, l'utilizzo di associazione indipendente sembra aumentare il costo di generazione di visualizzazioni rispetto alle associazioni di chiavi esterne.
Veverke

1
@LadislavMrnka: puoi ricontrollare il collegamento all'artice che hai menzionato sopra funziona? Non riesco ad accedervi.
Veverke

34

Usali entrambi. E rendi virtuali i riferimenti all'entità per consentire il caricamento lento. Come questo:

public class Order
{
  public int ID { get; set; }
  public int CustomerID { get; set; }
  public virtual Customer Customer { get; set; } // <-- Customer object
  ...
}

Ciò consente di risparmiare ricerche DB non necessarie, consente il caricamento lento e consente di vedere / impostare facilmente l'ID se si sa cosa si desidera che sia. Nota che avere entrambi non cambia in alcun modo la struttura della tabella.


5
Concordato. Questo è quello che ho finito per fare, come mi ha suggerito Ladislav. Ti dà davvero il meglio di entrambi i mondi; l'intero oggetto quando hai bisogno di tutte le sue proprietà, e il suo ID quando hai solo bisogno della PK e non ti importa del resto.
Daniel Liuzzi

9

L'associazione indipendente non funziona bene con AddOrUpdatequella solitamente utilizzata nel Seedmetodo. Quando il riferimento è un articolo esistente, verrà reinserito.

// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);

Il risultato è che il cliente esistente verrà reinserito e il nuovo cliente (reinserito) verrà associato al nuovo ordine.


A meno che non usiamo l'associazione della chiave esterna e assegniamo l'id.

 // Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);

Abbiamo il comportamento previsto, il cliente esistente verrà associato al nuovo ordine.


2
Questa è una buona scoperta. Tuttavia, la soluzione (e penso che sia il modo giusto) per allegare un cliente all'ordine è caricarlo dal contesto del database in questo modo: var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; Oppure puoi utilizzare il metodo Select per caricare il cliente dal contesto del database. Funziona con un'associazione indipendente.
tala9999

4

Prediligo l'approccio a oggetti per evitare ricerche non necessarie. Gli oggetti proprietà possono essere popolati altrettanto facilmente quando si chiama il metodo factory per creare l'intera entità (utilizzando un semplice codice di callback per entità annidate). Non ci sono svantaggi che posso vedere tranne che per l'utilizzo della memoria (ma metteresti nella cache i tuoi oggetti, giusto?). Quindi, tutto ciò che stai facendo è sostituire lo stack per l'heap e ottenere un miglioramento delle prestazioni dal non eseguire ricerche. Spero che questo abbia un senso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.