2

使用 EF Core 2.2.2,我的数据库中有一个表,用于存储许多其他表的注释。换句话说,它有点像主从关系中的明细表,但有多个主表。考虑这个简化的 EF 模型:

public class Person
{
  public Guid PersonID { get; set; }
  public string Name { set; set; }
}

public class InvoiceItem
{
  public Guid InvoiceItemID { get; set; }
  public Guid InvoiceID { get; set; }
  public string Description { get; set; }
}

public class Invoice
{
  public Guid InvoiceID { get; set; }
  public int InvoiceNumber { get; set; }

  public List<Item> Items { get; set; }
}

public class Notes
{
  public Guid NoteID { get; set; }
  public Guid NoteParentID { get; set; }
  public DateTime NoteDate { get; set; }
  public string Note { get; set; }
}

在这种情况下,Notes 可以存储 Person 便笺或 Invoice 便笺(或 InvoiceItem 便笺,尽管我们只是说 UI 不支持它)。

我有这样设置的查询方法:

public IQueryable<PersonDTO> GetPersonQuery()
{
  return from p in Context.People
             select new PersonDTO
             {
               PersonID = p.PersonID,
               Name = p.Name
             };
}

public List<PersonDTO> GetPeople()
{
  return (from p in GetPersonQuery()
              return p).ToList();
}

public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
  return from p in Context.Invoices
             select new InvoiceDTO
             {
               InvoiceID = p.InvoiceID,
               InvoiceNumber = p.InvoiceNumber
             };
}

public List<InvoiceDTO> GetInvoices()
{
  return (from i in GetInvoiceQuery()
              return i).ToList();
}

这些都按预期工作。现在,假设我将 InvoiceItems 添加到 Invoice 查询中,如下所示:

public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
  return from p in Context.Invoices
             select new InvoiceDTO
             {
               InvoiceID = p.InvoiceID,
               InvoiceNumber = p.InvoiceNumber,
               Items = (from ii in p.Items
                             select new ItemDTO
                             {
                               ItemID = ii.ItemID,
                               Description = ii.Description
                             }).ToList()
             };
}

这也很好用,并且只发出几个查询。但是,以下内容:

public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
  return from p in Context.Invoices
             select new InvoiceDTO
             {
               InvoiceID = p.InvoiceID,
               InvoiceNumber = p.InvoiceNumber,
               Items = (from ii in p.Items
                             select new ItemDTO
                             {
                               ItemID = ii.ItemID,
                               Description = ii.Description
                             }).ToList(),
              Notes = (from n in Context.Notes
                             where i.InvoiceID = n.NoteParentID
                             select new NoteDTO
                             {
                               NoteID = n.NoteID,
                               Note = n.Note
                             }).ToList(),
             };
}

针对 Invoice 表中的每个 Invoice 行向 Note 表发送一个单独的查询。因此,如果 Invoice 表中有 1,000 张发票,这相当于向数据库发送了 1,001 个查询。

似乎 Items 子查询没有相同的问题,因为 Invoices 和 Items 之间存在显式关系,而 Invoices 和 Notes 之间没有特定关系(因为并非所有注释都与发票相关)。

有没有办法重写最终查询,这样它就不会为表中的每张发票发送单独的注释查询?

4

1 回答 1

1

问题确实是相关的子查询与集合导航属性。EF Core 查询翻译器在处理此类子查询时仍然存在问题,这些子查询实际上是逻辑集合导航属性,应该以类似的方式进行处理。

有趣的是,使用中间投影(letLINQ 查询语法中的运算符)模拟集合导航属性似乎可以解决这个问题:

var query =
    from i in Context.Invoices
    let i_Notes = Context.Notes.Where(n => i.InvoiceID == n.NoteParentID) // <--
    select new InvoiceDTO
    {
        InvoiceID = i.InvoiceID,
        InvoiceNumber = i.InvoiceNumber,
        Items = (from ii in i.Items
                 select new ItemDTO
                 {
                     ItemID = ii.ItemID,
                     Description = ii.Description
                 }).ToList(),
        Notes = (from n in i_Notes // <--
                 select new NoteDTO
                 {
                     NoteID = n.NoteID,
                     Note = n.Note
                 }).ToList(),
    };
于 2019-03-07T08:43:13.653 回答