L'EF Core ha una "prima mentalità del codice" di default, cioè dovrebbe essere usato in modo code-first, e anche se l'approccio database-first è supportato, è descritto come nient'altro che il reverse-engineering del database esistente e creando una rappresentazione in codice di esso. Ciò che intendo è che il modello (classi POCO) creato nel codice "a mano" (codice prima) e generato dal database (dal comando Scaffold-DbContext), dovrebbe essere identico.
Sorprendentemente, i documenti ufficiali EF Core dimostrano differenze significative. Ecco un esempio di creazione del modello nel codice: https://ef.readthedocs.io/en/latest/platforms/aspnetcore/new-db.html Ed ecco l'esempio di reverse-engineering dal database esistente: https: //ef.readthedocs.io/en/latest/platforms/aspnetcore/existing-db.html
Questa è la classe di entità nel primo caso:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
e questa è la classe di entità nel secondo caso:
public partial class Blog
{
public Blog()
{
Post = new HashSet<Post>();
}
public int BlogId { get; set; }
public string Url { get; set; }
public virtual ICollection<Post> Post { get; set; }
}
Il primo esempio è una classe POCO molto semplice e abbastanza ovvia. È mostrato ovunque nella documentazione (ad eccezione degli esempi generati dal database). Il secondo esempio però ha alcune aggiunte:
Ho provato a impalcare il modello dal database (gli strumenti più recenti al momento in cui scrivo) e genera entità esattamente come mostrato, quindi non si tratta di un problema di documentazione obsoleto. Quindi la strumentazione ufficiale genera codice diverso, e la documentazione ufficiale suggerisce di scrivere un codice diverso (banale) - senza classi parziali, membri virtuali, inizializzazione della costruzione, ecc.
La mia domanda è, cercando di costruire il modello in codice, come dovrei scrivere il mio codice? Mi piace utilizzare ICollection anziché List perché è più generico, ma a parte questo, non sono sicuro di dover seguire i documenti o gli strumenti di MS? Devo dichiararli come virtuali? Devo inizializzarli in un costruttore? eccetera...
So dai vecchi EF che le proprietà di navigazione virtuale consentono il caricamento lento, ma non è nemmeno supportato (ancora) in EF Core e non conosco altri usi. Forse influisce sulle prestazioni? Forse gli strumenti cercano di generare un codice a prova di futuro, in modo che quando il caricamento pigro sarà implementato, le classi e il contesto POCO saranno in grado di supportarlo? Se è così, posso abbandonarli perché non ho bisogno di un caricamento lento (tutte le query di dati sono incapsulate in un repository)?
A breve, aiutami a capire perché è la differenza e quale stile dovrei usare per costruire il modello nel codice?
Cerco di dare una risposta breve a ogni punto che hai citato
partial
classi partial
sono particolarmente utili per il codice generato dallo strumento. Si supponga di voler implementare una proprietà derivata solo per modello. Per prima cosa, dovresti farlo, ovunque tu voglia. Per il primo database, il file di classe verrà riscritto se si aggiorna il modello. Quindi, se vuoi mantenere il tuo codice di estensione, vuoi metterlo in un file diverso al di fuori del modello gestito - questo è dove partial
ti aiuta ad estendere la classe senza modificare a mano il codice generato automaticamente.
ICollection
è sicuramente una scelta adatta, anche per il codice prima. Probabilmente il tuo database non supporterà un ordine definito senza un'istruzione di ordinamento.
L'inizializzazione del costruttore è almeno una convenienza ... supponiamo di avere un database di raccolta vuoto o se non hai affatto caricato la proprietà. Senza il costruttore è necessario gestire casi null
esplicito su punti arbitrari nel codice. Se tu debba andare con List
o HashSet
è qualcosa a cui non posso rispondere adesso.
virtual
consente la creazione di proxy per le entità del database, che può aiutare con due cose: Lazy Loading come già menzionato e cambiare il monitoraggio. Un oggetto proxy può tracciare immediatamente le modifiche alle proprietà virtuali con il setter, mentre gli oggetti normali nel contesto devono essere controllati su SaveChanges
. In alcuni casi, questo potrebbe essere più efficiente (non generalmente).
virtual IDbSet
voci di contesto virtual IDbSet
consentono una progettazione più semplice dei contesti test-mockup per i test di unità. Potrebbero esistere anche altri casi d'uso.