Sto avendo problemi con l'avere due riferimenti di chiavi esterne allo stesso tavolo. I campi ID chiave esterna vengono popolati ma i campi di navigazione e gli elenchi (i campi Team) non sono - sono entrambi nulli.
Le mie classi sono:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Fixture> HomeFixtures { get; set; }
public virtual ICollection<Fixture> AwayFixtures { get; set; }
}
public class Fixture
{
public int Id { get; set; }
public int HomeTeamId { get; set; }
public int AwayTeamId { get; set; }
public Team HomeTeam { get; set; }
public Team AwayTeam { get; set; }
}
e il mio dbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Team> Teams { get; set; }
public DbSet<Fixture> Fixtures { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Fixture>()
.HasOne(f => f.HomeTeam)
.WithMany(t => t.HomeFixtures)
.HasForeignKey(t => t.HomeTeamId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
modelBuilder.Entity<Fixture>()
.HasOne(f => f.AwayTeam)
.WithMany(t => t.AwayFixtures)
.HasForeignKey(t => t.AwayTeamId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
}
}
Ho provato ad aggiungere [ForeignKey()]
alle proprietà HomeTeam e AwayTeam ma non ha alcun effetto. Ho anche provato a cambiare il metodo OnModelCreating per lavorare nell'altro modo, cioè
modelBuilder.Entity<Team>()
.HasMany(t => t.HomeFixtures)
.WithOne(f => f.HomeTeam)
.HasForeignKey(f => f.HomeTeamId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
e lo stesso per i dispositivi a distanza, ma questo produce un comportamento identico.
Non sembra importare come chiedo ma il caso più semplice è
Fixture fixture = await _context.Fixtures.SingleOrDefaultAsync(f => f.Id == id);
L'oggetto fixture restituito contiene Id del team che sono validi e nel database ma gli oggetti Team non sono ancora popolati.
Qualcuno ha idea di cosa sto facendo male? Questo è un progetto nuovo di zecca e un database nuovo di zecca quindi non c'è alcun codice legacy che interferisce. Sto usando Visual Studio 2017rc con Entity Framework Core.
Al momento EF Core non supporta il caricamento lazy. Tracciamento del problema qui
Ciò significa che le proprietà di navigazione predefinite non verranno caricate e rimarranno nulle. Per ovviare a questo problema, puoi utilizzare il caricamento avido o i modelli di caricamento espliciti.
Carico
Il caricamento di Eager è un pattern in cui si richiedono i dati di riferimento necessari con impazienza durante l'esecuzione della query utilizzando Include
API. L'utilizzo è leggermente diverso da come ha funzionato in EF6. Per includere qualsiasi navigazione, si specifica l'espressione lambda (o nome stringa) nel metodo include nella query.
es. await _context.Fixtures.Include(f => f.HomeTeam).FirstOrDefaultAsync(f => f.Id == id);
Attualmente, l'inclusione filtrata non è supportata, quindi puoi richiedere di caricare completamente la navigazione o escluderla. Quindi l'espressione lambda non può essere complessa. Deve essere un semplice accesso alla proprietà. Anche per caricare la navigazione annidata, puoi ThenInclude
chiamate di accesso alle proprietà (come abc
) o quando la navigazione dopo la raccolta (dato che non puoi concatenarli) usa ThenInclude
.
es. await _context.Fixtures.Include(f => f.HomeTeam).ThenInclude(t=> t.HomeFixtures).FirstOrDefaultAsync(f => f.Id == id);
Sarebbe bene ricordare che include rappresentare un percorso di navigazione dal tipo di entità a cui è stato chiamato per popolare tutte le naviagazioni sul percorso. Spesso potresti aver bisogno di scrivere chiamate ripetute se includi più navigazioni al 2 ° o ulteriore livello. Questo è solo per la sintassi e la query verrà ottimizzata e non eseguirà lavori ripetuti. Inoltre con string include puoi specificare l'intero percorso di navigazione senza dover utilizzare ThenInclude. Poiché la navigazione di riferimento può utilizzare join per recuperare tutti i dati necessari in una singola query, e la raccolta delle raccolte può caricare tutti i dati correlati in una singola query, il caricamento ansioso è il modo più performante.
Caricamento esplicito
Quando hai caricato un oggetto nella memoria e hai bisogno di caricare una navigazione, mentre il caricamento lazy lo avrebbe caricato mentre accedevi alla proprietà di navigazione, in assenza di ciò devi chiamare il metodo Load
da solo. Questi metodi sono definiti su ReferenceEntry
o CollectionEntry
.
per esempio
Fixture fixture = await _context.Fixtures.SingleOrDefaultAsync(f => f.Id == id);
_context.Entry(fixture).Reference(f => f.HomeTeam).Load();
var team = await _context.Teams.SingleOrDefaultAsync(t => t.Id == id);
_context.Entry(team).Collection(f => f.HomeFixtures).Load();
Per la navigazione di riferimento è necessario il Reference
su EntityEntry
per ottenere ReferenceEntry. Per il metodo di navigazione equivalente alla Collection
è Collection
. Quindi basta chiamare il metodo Load
per caricare i dati nella navigazione. C'è anche una versione asincrona di LoadAsync
se ne hai bisogno.
Spero che questo ti aiuti.