Sto provando a far funzionare le Man Days totali da volontari per sezione tra un intervallo di date. C'è una tabella delle presenze che include il giorno e l'ID membro da associare alla tabella dei membri. C'è anche una tabella delle sezioni per elencare tutte le sezioni e una tabella di riferimento dei membri in quanto un membro può trovarsi in più di una sezione.
Voglio raggruppare per la prima sezione in cui è presente un membro e quindi avere un conteggio di tutti i giorni di presenza per quella sezione.
Il seguente funziona perfettamente dove sto raggruppando da 1 a molti campo (Categoria) sulla tabella dei membri, ma non riesco a capire come cambiare questo alla prima sezione (Se il membro ha una sezione)
var result = await _context.Attendance
.Where(p => p.Day >= firstDayOfLastQuarter && p.Day <= lastDayOfLastQuarter)
.GroupBy(p => p.Member.Category.Name)
.Select(p => new { key = p.Key, count = p.Count() })
.ToListAsync();
Sto cercando qualcosa di simile
var result = await _context.Attendance
.Where(p => p.Day >= firstDayOfLastQuarter && p.Day <= lastDayOfLastQuarter && p.Member.MemberToSection.Any(s => s.MemberId == p.MemberId))
.GroupBy(p => p.Member.MemberToSection.First().Section.Name)
.Select(p => new { key = p.Key, count = p.Count() })
.ToListAsync();
Il codice T-Sql che produce il risultato desiderato è
SELECT Count(*) as Total, Section.Name
FROM Attendance
LEFT JOIN Member on Attendance.Member_Id = Member.Id
OUTER APPLY (
SELECT TOP(1) sec.id, sec.Name
FROM MemberToSection m2s
LEFT JOIN Section sec ON m2s.Section_Id = sec.Id
WHERE m2s.Member_Id = Member.Id
) as Section
WHERE Attendance.Day >= '2016-10-01' AND Attendance.Day <= '2016-12-31'
GROUP BY Section.Name
Il codice "qualcosa come" restituisce l'errore
An unhandled exception occurred while processing the request.
ArgumentException: Property 'System.String Name' is not defined for type 'Microsoft.EntityFrameworkCore.Storage.ValueBuffer'
Come richiesto qui ci sono le classi rilevanti
public partial class Attendance
{
public long Pk { get; set; }
public int MemberId { get; set; }
public DateTime Day { get; set; }
public virtual Member Member { get; set; }
}
public partial class Member
{
public Member()
{
MemberToSection = new HashSet<MemberToSection>();
}
public int Id { get; set; }
public int? CategoryId { get; set; }
public virtual ICollection<MemberToSection> MemberToSection { get; set; }
}
public partial class MemberToSection
{
public int SectionId { get; set; }
public int MemberId { get; set; }
public virtual Member Member { get; set; }
public virtual Section Section { get; set; }
}
public partial class Section
{
public Section()
{
MemberToSection = new HashSet<MemberToSection>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<MemberToSection> MemberToSection { get; set; }
}
La documentazione EF (attualmente combinata) inizia con la sezione Confronta EF Core e EF6.x che contiene l'argomento molto "utile" Qual è il posto giusto per te . Bene, l'EF Core non è per te (ancora). Quanto segue si applica agli ultimi in questo momento EF Core v1.1.0.
Innanzitutto, GroupBy
(anche con la semplice proprietà primitiva) viene sempre elaborato in memoria.
Secondo, ci sono molti bug interni che causano eccezioni durante l'elaborazione di query LINQ piuttosto valide come la tua.
In terzo luogo, dopo alcuni tentativi ed errori, il seguente costrutto equivalente funziona per il tuo caso (almeno non genera eccezioni):
.GroupBy(p => p.Member.MemberToSection.Select(m => m.Section.Name).FirstOrDefault())
(btw, irrilevante per il problema, la condizione s => s.MemberId == p.MemberId
all'interno di p.Member.MemberToSection.Any
chiamata è ridondante perché viene applicata dalla relazione, in modo semplice Any()
farebbe lo stesso .)
Ma ora non solo il GroupBy
viene eseguito in memoria, ma anche la query sta causando N + 1 query SQL simili a Linq EF nidificate selezionare i risultati in N + 1 query SQL . Congratulazioni :(