web-dev-qa-db-fra.com

EF Core n'a pas pu être traduit et sera évalué localement

J'ai une requête dans EF Core 1.1.2 qui est évaluée côté client et je voudrais savoir s'il existe un meilleur moyen de la traduire en sql?

La requête:

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)
  }

Si non, quelles autres options ai-je?

  • Comme je sais qu'il n'y a toujours pas moyen de cartographier les vues.
  • La méthode FromSQL () peut renvoyer des types déjà connus dans le contexte uniquement. Par exemple, je ne peux pas marquer un modèle comme [NotMapped].
  • Revenir à ef6 n’est pas une option, car .net core est l’infrastructure cible.

Des modèles:

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; }
}
4
Perrier

Actuellement (et cela ressemble également à l'EF Core entrant v.2.0), les requêtes GroupBy sont traitées localement, il est donc essentiel de les éviter dans la mesure du possible. 

Et votre requête semble être éligible à cela. Il n'est pas nécessaire de commencer par multiplier le jeu de données avec des jointures, puis de le regrouper.

J'ai remarqué que vous n'utilisez que reference les propriétés de navigation et les FK dans vos entités, comme un enregistrement de table de base de données et SQL. Mais EF vous permet de définir également les propriétés de navigation collection correspondantes qui vous permettent de lancer des requêtes à partir de la racine logique, éliminant ainsi le besoin de jointures et de regrouper par.

Si vous définissez une propriété de navigation de Location à Inventory

public class Location
{
    // ...
    public ICollection<Inventory> Inventories { get; set; }
}

alors la requête équivalente pourrait être simplement:

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)
}

qui sera entièrement traduit en SQL.

Si, pour une raison quelconque, vous ne pouvez pas créer la propriété de navigation de collection ci-dessus, vous pouvez toujours commencer par les emplacements et les corréler manuellement aux inventaires:

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)
}
4
Ivan Stoev

Si vous ajoutez la propriété de navigation comme Ivan le suggère correctement:

public class Location
{
    // ...
    public ICollection<Inventory> Inventories { get; set; }
}

Ensuite, vous pouvez simplement créer une requête comme celle-ci:

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)
   });
0
Brad