Sto usando l'ASP Net Core 2. Ho un modello di test:
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public string Position { get; set; }
public int Age { get; set; }
[IgnoreDataMember]
public ICollection<PlayerTeam> PlayerTeams { get; set; }
public Player()
{
PlayerTeams = new List<PlayerTeam>();
}
}
public class PlayerTeam
{
public int PlayerId { get; set; }
public Player Player { get; set; }
public int TeamId { get; set; }
public Team Team { get; set; }
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; } // название команды
// [IgnoreDataMember]
public ICollection<PlayerTeam> PlayerTeams { get; set; }
public Team()
{
PlayerTeams = new List<PlayerTeam>();
}
}
questo è il mio DBcontext:
public class FootbollContext: DbContext
{
public DbSet<Player> Players { get; set; }
public DbSet<Team> Teams { get; set; }
public FootbollContext(DbContextOptions<FootbollContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PlayerTeam>()
.HasKey(t => new { t.TeamId, t.PlayerId });
}
}
Ho una query nel mio controller:
FootbollContext db;
var teams = db.Teams.Select(team => new {
TeamName = team.Name,
PlayersOlder20 = team.PlayerTeams.Where(pt => pt.Player.Age > 20).Select(s => s.Player)
});
e funziona bene, ma voglio usare i metodi Include()
/ ThenInclude()
per questa query, e voglio ottenere gli stessi risultati uguali.
var teams = db.Teams.Include(p => p.PlayerTeams).ThenInclude(d => d.Player)
ma non voglio caricare tutti i dati! e non so come posso filtrare i risultati per proprietà "Età giocatori (> 20)" nella relativa tabella (non selezionabile !!) in una query SQL.
Pertanto, si desidera configurare una relazione molti a molti tra Team e Player: ogni Team
composta da zero o più Players
e ogni Player
è membro di zero o più Teams
.
Vedo alcune aberrazioni dal codice del Entity Framework: prime convenzioni . Questo causa le difficoltà che incontri
Hai ragione che in un database relazionale hai bisogno di una tabella di giunzione per configurare una relazione molti-a-molti. Sebbene il framework delle entità utilizzi questo meccanismo, è meglio non definirlo.
Se segui le convenzioni di default per una relazione molti-a-molti , non avrai bisogno della tabella di giunzione in nessuna delle tue query.
class Player
{
public int Id { get; set; }
public string Name { get; set; }
...
// every Player plays in zero or more teams:
public virtual ICollection<Team> Teams {get; set;}
}
class Team
{
public int Id { get; set; }
public string Name { get; set; } // название команды
...
// every Team has zero or more PLayers:
public virtual ICollection<Player> Players {get; set;}
}
class FootbollContext: DbContext
{
public DbSet<Player> Players { get; set; }
public DbSet<Team> Teams { get; set; }
}
Questa è tutta l'informazione Entity Framework ha bisogno di capire che vuoi un molti-a-molti tra giocatori e squadre.
Nota che ho omesso la tabella di giunzione per PLayerTeam. Non ne avrai bisogno. Lascia che il framework delle entità decida quale tabella ha bisogno.
Inoltre non ho creato le liste come fai tu nei costruttori. Prima di tutto, la tua collezione non è una lista; cosa significa Player [4]? In secondo luogo, ogni volta che li interrogate dal database questi elenchi vengono immediatamente sostituiti dagli oggetti restituiti da DbSet IQueryable.
Ora se vuoi alcune squadre con alcuni dei loro giocatori, farai quanto segue:
var result = dbContext.Teams
.Where(team => ...
.select(team => new
{ // select only the properties you will be using:
Name = team.Name,
...
// Select only the Players of this team you want:
OlderPlayers = team.Players
.Where(player => player.Age > 20)
.Select(player => new
{ // select only the player properties you plan to use:
Name = player.Name,
Position = player.Position,
...
}),
});
Il framework Entity conosce la relazione Many-to-many tra Team e Players. Effettuerà automaticamente i join corretti con la tabella di giunzione creata (di cui ufficialmente non sai che esiste) e restituirà solo le proprietà di cui avrai bisogno.
Essere consapevoli del fatto che Linq utilizza l'esecuzione posticipata. La dichiarazione di cui sopra non conserva ancora i tuoi dati. Sa solo come recuperare i dati non appena lo chiedi. Questo viene solitamente fatto usando funzioni come ToList()
, FirstOrDefault()
, Count()
, ecc. Ciò significa che dovrai assicurarti che i dati dbcontext
interrogati prima di Smaltire il tuo dbcontext
, prima di lasciare la tua istruzione using.
Come secondo esempio: tutti i nomi dei giocatori con più di 20 anni con i nomi delle squadre che suona:
var result = dbContext.Players
.Where(player => player.Age > 20)
.Select(player => new
{
Name = player.Name,
TeamNames = player.Teams
// if desired: Where(team => ...)
.Select(team => team.Name)
.ToList(),
});
.Where(play => player