Ho una tabella di relazioni da molti a molti in cui sto cercando di visualizzare un elenco di report in una tabella.
Il mio tavolo ha questo aspetto:
Tabella dei rapporti:
Id| ReportName |
1 | report 1 |
2 | report 2 |
3 | report 3 |
Tabella delle categorie di report:
Id| Name |
1 | General |
2 | Specific |
Tabella di giunzione ReportMapping:
Id| ReportId | CategoryId |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 2 | 2 |
I report possono avere più categorie in questo esempio sono solo 2 ma potrebbero essercene molti di più, diciamo che 1 report può avere 5 categorie come General, Specific, Test2, Test3 e Test4
Mi piacerebbe visualizzare un formato su una tabella / lista sulla mia applicazione core .net qualcosa del tipo:
ReportId| Report Name | Report Categories
1 | report 1 | General, Specific
2 | report 2 | General, Specific
Sto avendo problemi a far funzionare tutto in SQL Server e in EF core linq. Qualche suggerimento su come iniziare? Finora sono in grado di unire le tabelle insieme ma non ho idea di come concatenare i miei risultati in una riga per i report che hanno più categorie. Sto ottenendo cose come sotto invece del mio risultato desiderato come nell'esempio sopra:
ReportId | Report Name | Report Categories
1 | report 1 | General
1 | report 1 | Specific
2 | report 2 | General
2 | report 2 | Specific
Qualsiasi aiuto sarebbe apprezzato, grazie!
Il modello che stai descrivendo è quasi identico al modello Post
/ Tag
dell'esempio Molti a molti nella documentazione di EF Core.
Quindi avresti 3 classi che rappresentano i record della tabella
public class Report
{
public int Id { get; set; }
public string ReportName { get; set; }
public ICollection<ReportMapping> Mappings { get; set; } // navigation
}
public class ReportCategory
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ReportMapping> Mappings { get; set; } // navigation
}
public class ReportMapping
{
public int Id { get; set; }
public int ReportId { get; set; }
public int CategoryId { get; set; }
public Report Report { get; set; } // navigation
public ReportCategory Category { get; set; } // navigation
}
e 3 DbSet
che rappresentano le tue tabelle:
public DbSet<Report> Reports { get; set; }
public DbSet<ReportCategory> ReportCategories { get; set; }
public DbSet<ReportMapping> ReportMappings { get; set; }
Si noti che la proprietà Id
(colonna) nell'entità di giunzione (tabella) è ridondante, quindi nel caso in cui non si sia vincolati a un database esistente, prendere in considerazione la rimozione e configurare il PK composito come nell'esempio
modelBuilder.Entity<ReportMapping>()
.HasKey(e => new { e.ReportId, e.CategoryId });
Notare anche le proprietà contrassegnate con // navigation
. Queste sono le cosiddette proprietà di navigazione (vedere Definizione di termini ), che rappresentano i fini delle relazioni e consentono di accedere ai dati correlati all'interno delle query LINQ senza utilizzare i costrutti di join
- vedi Non usare Join di Linq. Navigare! ed è il modo consigliato / preferito EF (Core) di scrivere query LINQ.
Quindi questo è il tuo modello di database. Poiché desideri che una query restituisca un tipo di risultato specifico, inizia definendo una classe che rappresenta quel risultato (detto DTO, ViewModel ecc.), Ad esempio:
public class ReportInfo
{
public int ReportId { get; set; }
public string ReportName { get; set; }
public IEnumerable<string> ReportCategories { get; set; }
}
Nota che sto definendo le ReportCategories
come sequenza di stringhe piuttosto che come singola stringa. Questo perché, in primo luogo, la concatenazione di stringhe di un set di risultati non è supportata in modo nativo dai database e, in secondo luogo, concatenare con la virgola è solo uno dei molti modi in cui questi dati possono essere presentati. In generale la formattazione dei dati è responsabilità dei clienti. Quindi restituisci i dati nel loro formato nativo non elaborato (elenco di stringhe) e consenti al client di formattarlo (in questo caso, può farlo facilmente utilizzando string.Join(",", info.ReportCategories)
).
Infine, la query effettiva. Con le proprietà di navigazione sul posto è abbastanza semplice, in pratica basta Select
s:
var result = db.Reports
.Select(r => new ReportInfo
{
ReportId = r.Id,
ReportName = r.ReportName,
ReportCategories = r.Mappings
.Select(m => m.Category.Name)
.ToList() // <-- to avoid N + 1 subquery in EF Core 2.1+
})
.ToList();