Sto cercando di proiettare i dati relativi ma continuo a ricevere errori.
Ho una lista di progetti, per ogni progetto ci possono essere più serie di linee di base (o nessuna), ogni linea di base contiene più pietre miliari. L'elenco proiettato di progetti dovrebbe contenere una proprietà che contiene una pietra miliare particolare nella linea di base impostata più di recente.
Il mio modello è in SQL attraverso EFCore:
public class Project
{
public int ProjectID { get; set; }
public string Name { get; set; }
public ICollection<Baseline> Baselines { get; set; }
}
public class Baseline
{
public int BaselineID { get; set; }
public int ProjectID { get; set; }
public Project Project { get; set; }
public string Name { get; set; }
public DateTime DateSet {get; set;}
public string Description { get; set; }
public ICollection<BaselineDate> BaselineDates { get; set; }
}
public class BaselineDate
{
public int BaselineDateID { get; set; }
public int BaselineID { get; set; }
public Baseline Baseline { get; set; }
public int MilestoneTypeID { get; set; }
public MilestoneType MilestoneType { get; set; }
public DateTime Date { get; set; }
public string Comment { get; set; }
}
Nel mio controller, definisco la classe proiettata:
public class ProjectInfo
{
public int ProjectID { get; set; }
public string ProjectName { get; set; }
public DateTime? ProjectStart { get; set; }
}
public IList<ProjectInfo> ProjectInfoList { get; set; }
E poi in una funzione cerco di usare efcore per interrogare i modelli:
ProjectInfoList = await _context.Project
.Where(project => project.Branch == Branch)
.Select(project => new ProjectSummary
{
ProjectID = project.ProjectID,
ProjectName = project.Name,
ProjectStart = project.Baselines
.DefaultIfEmpty(new Baseline { BaselineDates = new List<BaselineDate>() })
.OrderByDescening(b => b.DateSet)
.FirstOrDefault()
.BaselineDates
.Where(d => d.Comment == "Project Start")
.FirstOrDefault()
.Date
}
.AsNoTracking()
.ToListAsync();
Funziona bene finché c'è una linea di base per ogni progetto. Tuttavia, quando un progetto esiste senza una linea di base, viene generata un'eccezione nulla.
ArgumentNullException: il valore non può essere nullo. Nome del parametro: source System.Linq.Enumerable.Where (IEnumerable source, Func predicate)
Provo a aggiungere .DefaultIfEmpty (new Baseline ()) ma genera più eccezioni.
Tieni presente che Entity Framework traduce l'intera espressione LINQ in SQL (beh, più precisamente, cerca di farlo). Poiché non esiste alcuna nozione di riferimenti null in SQL, è possibile utilizzare tranquillamente un'espressione che nel codice C # (o: LINQ-to-objects) avrebbe generato un'eccezione di riferimento null.
Ma a parte questo, la subquery offendente può essere riscritta in modo che anche in LINQ-to-objects non generi un'eccezione se le proprietà della raccolta non sono nulle:
ProjectInfoList = await _context.Project
.Where(project => project.Branch == Branch)
.Select(project => new ProjectSummary
{
ProjectID = project.ProjectID,
ProjectName = project.Name,
ProjectStart = (from bl in project.Baselines
from bd in bl.BaselineDates
where bd.Comment == "Project Start"
orderby bl.DateSet descending, bd.Date descending
select (DateTime?)bd.Date).FirstOrDefault()
}
.AsNoTracking()
.ToListAsync();
Lo faccio nella sintassi della query per una migliore leggibilità. Il from ... from
costrutto è SelectMany
nella sintassi del metodo. SelectMany
syntax, quando sono richiesti i dati di entrambi i genitori e figli ( bl.DateSet, bd.Date
), è piuttosto scomodo.