【问题标题】:EF Core could not be translated and will be evaluated locallyEF Core 无法翻译,将在本地进行评估
【发布时间】:2017-12-27 11:53:37
【问题描述】:

我在 EF Core 1.1.2 中有一个在客户端进行评估的查询,我想知道是否有更好的方法将其转换为 sql?

查询:

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

如果没有,我还有什么其他选择?

  • 据我所知,仍然无法映射视图。
  • FromSQL() 方法只能返回上下文中已知的类型,例如,我不能将一个模型标记为 [NotMapped]。
  • 回到 ef6 不是一个选择,因为 .net 核心是目标框架。

型号:

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

【问题讨论】:

  • 尽可能避免GroupBy 查询。查看发布的查询,它应该是可行的——既不需要连接也不需要分组。希望你有像 Location.InventoriesInventory.Item 这样的导航属性?你能发布实体模型吗?
  • @IvanStoev 我已经包含了我的模型。 Inventory.Item 确实存在,但我没有 Location.Inventories。它可以以某种方式提供帮助吗?该应用程序处理多个项目,因此位置、项目和库存是相当大的数据集。 (约 2-300 万件商品和每件商品约 4-5 个库存)
  • 您的[ForeignKey] 属性错误。它们应该出现在导航属性上并引用关键字段,例如[ForeignKey("ProjectId")] public Project Project { get; set; }.
  • @Brad 我认为它可以这样使用,因为 Guid ProjectId 是 ef 命名约定的外键。我的 ForeignKey 属性只是指向正确的导航属性。
  • 使用ProjectId 等确实符合命名约定,EF 将使用它来确定关系。但是您对 [ForeignKey] 属性的使用是错误的,并且绝对没有任何用途。我很惊讶 EF 在生成迁移时没有抛出异常,但它必须忽略它,因为它已经通过命名约定找出了关系。 docs.microsoft.com/en-us/ef/core/modeling/relationships

标签: entity-framework asp.net-core entity-framework-core


【解决方案1】:

目前(看起来也在传入的 EF Core v.2.0 中)GroupBy 查询在本地处理,因此关键是尽可能避免它们。

您的查询似乎符合条件 - 无需先将数据集与联接相乘,然后再将其分组。

我注意到您在实体中仅使用 reference 导航属性和 FK,基本上就像数据库表记录和 SQL。但 EF 还允许您定义相应的 collection 导航属性,允许您从逻辑根开始查询,从而消除连接和分组依据的需要。

如果您将导航属性从Location 定义为Inventory

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

那么等价的查询可以是:

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

这将被完全翻译成 SQL。

如果由于某种原因您无法创建上述集合导航属性,您仍然可以从位置开始并手动将它们与库存相关联:

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

【讨论】:

    【解决方案2】:

    如果您按照 Ivan 的正确建议添加导航属性:

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

    然后你可以像这样简单地创建一个查询:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多