Ich habe eine Abfrage in EF Core 1.1.2, die auf Client-Seite ausgewertet wird und möchten wissen, ob es eine bessere Möglichkeit gibt, es in SQL zu übersetzen?
Die Abfrage:
from l in _ctx.Locations
join i in _ctx.Inventories on l.Id equals i.LocationId
join it in _ctx.Items on i.ItemId equals it.Id
where l.ProjectId == projectid
group i by new {l.Id, l.LHA} into il
select new InventoryLocations() {
Id= il.Key.Id,
LHA = il.Key.LHA,
FlaggedItems = il.Any(x=>x.Item != null && x.Item.Flagged)
}
Wenn nicht, welche anderen Optionen habe ich?
Modelle:
public class Location
{
public Guid Id { get; set; }
[ForeignKey("Project")]
public Guid ProjectId { get; set; }
public Project Project {get; set; }
public string Name { get; set; }
public string LHA { get; set; }
[ForeignKey("ScanUser")]
public Guid? ScanUserId { get; set; }
public User ScanUser { get; set; }
[ForeignKey("CheckUser")]
public Guid? CheckUserId { get; set; }
public User CheckUser { get; set; }
[ForeignKey("GroupLeader")]
public Guid? GroupLeaderId { get; set; }
public User GroupLeader { get; set; }
public int State { get; set; }
}
public class Inventory
{
public Guid Id { get; set; }
[ForeignKey("Project")]
public Guid ProjectId { get; set; }
public Project Project {get; set; }
public string EANCode { get; set; }
[ForeignKey("Location")]
public Guid LocationId { get; set; }
public Location Location { get; set; }
public Double ScanQty { get; set; }
[ForeignKey("ScanUser")]
public Guid? ScanUserId { get; set; }
public User ScanUser { get; set; }
public DateTime? ScanDate { get; set; }
[ForeignKey("Item")]
public Guid? ItemId { get; set; }
public Item Item { get; set; }
[ForeignKey("InventoryTask")]
public Guid? InventoryTaskId { get; set; }
public InventoryTask InventoryTask { get; set; }
[ForeignKey("CheckUser")]
public Guid? CheckUserId { get; set; }
public User CheckUser { get; set; }
public DateTime? CheckDate { get; set; }
public Double PrevQty { get; set; }
}
public class Item
{
public Guid Id { get; set; }
[ForeignKey("Project")]
public Guid ProjectId { get; set; }
public Project Project {get; set; }
public string ItemNo { get; set; }
public string EANCode { get; set; }
public string Name { get; set; }
public Double Price { get; set; }
public bool Deleted { get; set; }
public DateTime ChangeTime { get; set; }
public Double BaseQty { get; set; }
public bool Flagged { get; set; }
}
Derzeit (und auch im eingehenden EF Core v.2.0) werden die GroupBy
Abfragen lokal verarbeitet, daher ist es der Schlüssel, diese wo möglich zu vermeiden.
Und dafür scheint Ihre Anfrage geeignet zu sein - es besteht keine Notwendigkeit, zuerst den Datensatz mit Joins zu multiplizieren und ihn dann wieder zu gruppieren.
Ich habe bemerkt, dass Sie nur Referenznavigationseigenschaften und FKs in Ihren Entitäten verwenden, im Grunde wie Datenbanktabellensatz und SQL. Aber EF ermöglicht es Ihnen, auch eine entsprechende Sammlung Navigationseigenschaften zu definieren, mit denen Sie Abfragen von der logischen Wurzel starten können, so dass die Notwendigkeit von Joins und Gruppieren durch.
Wenn Sie die Navigationseigenschaft von Location
zu Inventory
public class Location
{
// ...
public ICollection<Inventory> Inventories { get; set; }
}
dann könnte die äquivalente Abfrage einfach sein:
from loc in _ctx.Locations
where loc.ProjectId == projectid
select new InventoryLocations()
{
Id = loc.Id,
LHA = loc.LHA,
FlaggedItems = loc.Inventories.Any(inv => inv.Item != null && inv.Item.Flagged)
}
welches vollständig in SQL übersetzt wird.
Wenn Sie aus irgendeinem Grund die oben genannte Navigationseigenschaft der Sammlung nicht erstellen können, können Sie dennoch mit Standorten beginnen und sie manuell mit Inventaren korrelieren:
from loc in _ctx.Locations
where loc.ProjectId == projectid
select new InventoryLocations()
{
Id = loc.Id,
LHA = loc.LHA,
FlaggedItems = _ctx.Inventories.Any(inv => loc.Id == inv.LocationId && inv.Item != null && inv.Item.Flagged)
}
Wenn Sie die Navigationseigenschaft hinzufügen, wie Ivan richtig vorschlägt:
public class Location
{
// ...
public ICollection<Inventory> Inventories { get; set; }
}
Dann können Sie einfach eine Abfrage wie folgt erstellen:
var locations = _ctx.Locations
.Include(x => x.Inventories)
.ThenInclude(x => x.Item)
.Where(x => x.ProjectId == projectId)
.Select(loc => new InventoryLocations
{
Id = loc.Id,
LHA = loc.LHA,
FlaggedItems = loc.Inventories.Any(inv => inv.LocationId == loc.Id && inv.Item?.Flagged)
});