2015-09-15 8 views
5

ho le seguenti entitàEntity Framework-registrazione Spiegazione

//Active Auction Entity 
public class ActiveAuction 
{ 
    public int Id { get; set; } 

    public string Title { get; set; } 

    public int? FirstAuctionId { get; set; } 

    public int? SecondAuctionId { get; set; } 

    public int? ThirdAuctionId { get; set; } 

    public virtual Auction FirstAuction { get; set; } 

    public virtual Auction SecondAuction { get; set; } 

    public virtual Auction ThirdAuction { get; set; } 
} 

// Auction Entity 
public class Auction 
{ 
    public int AuctionId { get; set; } 

    public AuctionStatus AuctionStatus { get; set; } 

    public int? DepartmentId { get; set; } 

    public virtual Department Department { get; set; } 

} 

// Department Entity 
public class Department 
{ 
    public int DepartmentId { get; set; } 

    public string DepartmentName { get; set; } 

    public int? AdminId { get; set; } 

    public virtual Admin Admin { get; set; } 
} 

Quello che sto cercando di fare è quello di ottenere Active Auctions con Auctions caricati e Auction hanno anche Departments caricato So che avrei dovuto scrivere Include per ogni oggetto da caricare in modo che il codice SQL generato da EF dovrà join per selezionare lì oggetto per me

ma questo è il codice esistente

using (var dc = DataContext()) 
    { 
     await dc.Auction 
      .Include("Department.Admin") 
      .Where(i => i.Id == id && i.AuctionStatus == AuctionStatus.Accepted).ToListAsync(); 

     return await dc.ActiveAuction. 
      SingleOrDefaultAsync(i => i.Id == id); 
    } 

I non so come funziona, ma questo codice funziona e il valore restituito ActiveAuctions include tutti gli oggetti desiderati

Ho verificato l'esecuzione di SQL sul database e come previsto genera query separate.

Voglio una spiegazione per capire come è stato caricato ActiveAcutions restituito con le altre entità menzionate !!?

+0

È 'Categories.Admin' un refuso per' Department.Admin'? o hai rimosso 'Categorie' dall'oggetto' Auction' per abbreviare il codice? – Johan

+0

dovrebbe essere Department.Admin Ho risolto che, Admin è un'altra entità esistente anche –

+0

Grande, si consideri l'uso di inclusioni fortemente tipizzate (ad esempio '.Include (a => a.Department.Admin)'. Eviterà errori di ortografia/inclusione di oggetti che non esiste. – Johan

risposta

5

La ragione è semplice. Come probabilmente saprai, Entity Framework tiene traccia delle entità recuperate dal database, principalmente per rilevare eventuali modifiche e applicare tali modifiche al database quando chiami SaveChanges. Tuttavia, ciò significa che il contesto EF ha una sorta di cache di entità prelevate dal database fino ad ora.

EDIT: Come sottolineato correttamente nei commenti da @GertArnold - la mia spiegazione con i proxy dinamici era completamente sbagliato - funziona in questo modo, anche se è ProxyCreationEnabledfalse. La vera ragione è la correzione delle relazioni che viene eseguita dal framework di entità quando viene chiamato DetectChanges (viene chiamato implicitamente su vari eventi, come associare entità al contesto o eseguire query su DbSet). Durante la correzione della relazione, EF sincronizza le proprietà di navigazione e le chiavi esterne che nel tuo caso causano comportamenti che osservi. Ulteriori informazioni sui rapporti sistemare in MSDN

per verificare questo è possibile utilizzare questo semplice codice:

using (var ctx = new TestEntities()) { 
    ctx.Configuration.LazyLoadingEnabled = false;     
    ctx.Configuration.ProxyCreationEnabled = false;     
    var code = ctx.Codes.First();     
    var error = ctx.Errors.First(); 
    Debug.Assert(Object.ReferenceEquals(error.Code, code));         
} 

Qui ho prenderti qualche entità (codice), poi vado a prendere un'altra entità (errore) che ha Codice proprietà di navigazione . Vedete che il caricamento lazy è disabilitato. Il seguente asserito passerà, perché error.Code e il codice è lo stesso oggetto .NET, che conferma che è stato recuperato dalla cache di contesto.

+0

Nonostante la spiegazione ampia, non si arriva veramente al vero motivo: correzione delle relazioni. La creazione del proxy non è necessaria perché ciò accada, quindi imho menziona che aggiunge solo rumore. –

+0

@GertArnold se ho capito bene, prima ha caricato l'asta e il dipartimento, il secondo caricato ActiveAuction, non dovrebbe funzionare in questo modo? – brykneval

+0

@GertArnold grazie per averlo sottolineato - anzi, avevo completamente torto con la mia spiegazione sui proxy dinamici. – Evk

-2

"Il caricamento lento è il processo mediante il quale un'entità o una raccolta di entità viene caricata automaticamente dal database la prima volta che si accede a una proprietà che fa riferimento all'entità/alle entità." Vedi questo articolo: https://msdn.microsoft.com/en-us/data/jj574232.aspx

+0

Mentre il problema dell'OP può riguardare il caricamento lento, ma come effettivamente funziona in questo caso specifico dovrebbe essere spiegato di più. Come vedi, non esiste una chiara relazione tra Auction e ActiveAuction * qui. La prima query sembra caricare solo tutto ciò che riguarda l'asta, ma l'ActiveAuction (usato nella seconda query) sembra essere riempito con i dati caricati * automaticamente *? – Hopeless

+0

@Perfetto se il caricamento lazy è abilitato, l'OP potrebbe essere pigro caricandoli (senza che se ne accorga) ... è stato il mio primo pensiero sulla domanda, in realtà. – Jcl

+0

@Jcl, poiché capisco che il caricamento lazy caricherà * oggetti correlati * accessibili tramite le proprietà di navigazione. Ma in questo caso potresti indicare quali sono gli oggetti correlati di quali oggetti? Gli oggetti caricati qui sono 'ActiveAuction' ma quello *** è chiaramente *** relativo a' Auction'? Qui non vedo alcuna relazione o alcun tipo di proprietà di navigazione. Questo è ciò che confonde la domanda dell'OP. Inoltre non sono molto sicuro che il carico pigro sia correlato qui, quindi stavo solo chiedendo questa risposta per ulteriori spiegazioni. – Hopeless

2

Il comportamento visualizzato è perché l'EF mantiene internamente la cache degli oggetti per ogni istanza del contesto DB. Ogni volta che viene eseguita una query, EF controlla prima la cache interna per vedere se contiene l'entità necessaria. Se l'entità viene trovata, viene restituita dalla cache senza nemmeno eseguire query sul database.

nel campione qui cosa succede:

  • prima query ottiene alcuni Auction entità e aggiungerli alla cache;
  • La seconda query viene filtrata dalle entità ActiveAuction e le aggiunge alla cache;
  • Quando si esaminano i risultati della seconda query, si vede che le proprietà sono già occupate perché sono state trovate nella cache per chiave;

È possibile modificare questo comportamento modificando l'impostazione MergeOption sull'istanza di contesto del database. Questa impostazione cambia il modo in cui EF unisce i risultati dal database alla cache.

valori possibili sono i seguenti:

appendOnly (valore di default)

oggetti che non esistono nel contesto dell'oggetto sono collegati al contesto. Se un oggetto è già nel contesto , i valori attuali e originali delle proprietà dell'oggetto nella voce non vengono sovrascritti con i valori dell'origine dati. Lo stato della voce dell'oggetto e lo stato delle proprietà dell'oggetto nella voce non cambiano . AppendOnly è l'opzione di unione predefinita.

NoTracking

oggetti sono mantenuti in uno stato indipendente e non sono tracciati nel ObjectStateManager. Tuttavia, le entità generate da Entity Framework e le entità POCO con proxy mantengono un riferimento al contesto dell'oggetto per facilitare il caricamento degli oggetti correlati.

OverwriteChanges

oggetti che non esistono nel contesto dell'oggetto sono attaccati alla contesto. Se un oggetto è già nel contesto, i valori originali e delle proprietà dell'oggetto nella voce vengono sovrascritti con valori di origine dati. Lo stato della voce dell'oggetto è impostato su Invariato, nessuna proprietà è contrassegnata come modificata.

PreserveChanges

oggetti che non esistono nel contesto dell'oggetto sono attaccati alla contesto.

(https://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption(v=vs.110).aspx)

E qui è un buon articolo che descrive tutte le opzioni in maggiori dettagli e contiene alcuni esempi di codice pure.

http://www.develop1.net/public/post/Do-you-understand-MergeOptions.aspx

EDIT: Volevo solo aggiungere che il comportamento di default può portare a risultati strani, se la prima query fa proiezione del soggetto (ad esempio, seleziona solo alcune colonne).In questo caso l'entità verrà comunque aggiunta alla cache anche se è solo a metà carica. Ciò significa che la seconda query restituirà anche l'entità metà caricata.

+0

L'API ObjectContext è deprecata. –

+0

@GertArnold. Non importa come 'DBContext' è solo un wrapper attorno a' ObjectContext', quindi i principi rimangono gli stessi. –

0

Non è possibile utilizzare .Include("Department.Admin") poiché EF non carica inclusioni interne come la propria, ad esempio Admin.

Si noti che, quando si lavora prima con il codice EF, è meglio determinare le relazioni reali tra le tabelle, sia in classi o utilizzando API fluente, per soddisfare un buon progetto senza anomalie. Quindi, l'utilizzo di tre Auction s in ActiveAuction senza alcun chiarimento potrebbe disturbare.

Un'altra cosa è che, Sembra che si sta utilizzando ActiveAuction come chiave esterna in ogni Auction senza alcuna dichiarazione! Quindi, cambiare la classe Auction a questo:

public class Auction 
{ 
    [ForeignKey("ActiveOne")] 
    public int AuctionId { get; set; } 

    public AuctionStatus AuctionStatus { get; set; } 

    public int? DepartmentId { get; set; } 

    public virtual Department Department { get; set; } 

    [Required] 
    public virtual ActiveAuction ActiveOne { get; set; } //match this name 

} 

Con questo in mente, credo che ci sono alcune ambiguità nei tuoi modelli. Parliamo della query. Come hai detto:

Quello che sto cercando di fare è quello di ottenere ActiveAuctions con Auctions caricato e Auction hanno anche Departments caricato

Così si può usare qualcosa di simile questa query:

await context.Auctions.Include("Department").Include("ActiveOne") 
.Where(i => i.id == id && i.AuctionStatus == AuctionStatus.Accepted) 
.Select(i => i.ActiveOne).ToListAsync(); 
+0

"Non è possibile utilizzare .Include (" Department.Admin ") poiché EF non carica inclusioni interne come la tua" cosa intendi!? per favore spiegalo –

+0

Non puoi usare membri annidati in "Include", o almeno non funzionerà correttamente, ho sperimentato di lavorare con EF specialmente Codice prima di circa 4 anni. –

0

Quando EF carica qualsiasi entità le collega con le entità esistenti nella sua cache in modo che il modello sia completo con tutti i riferimenti che puntano a qualsiasi carico ed oggetti.