EF Core - много запросов, отправленных в базу данных для подзапроса - PullRequest
2 голосов
/ 06 марта 2019

Используя 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, хотя давайте просто скажем, что пользовательский интерфейс этого не поддерживает).

У меня есть методы запросов, настроенные следующим образом:

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

отправляет отдельный запрос к таблице примечаний для каждой строки счета в таблице счетов.Таким образом, если в таблице «Счета-фактуры» имеется 1000 счетов-фактур, в базу данных отправляется примерно 1 1001 запрос.

Похоже, что подзапрос «Предметы» не имеет той же проблемы, поскольку существует явная связь между «Счета-фактуры» и «Предметы», в то время как между Счетами-фактурами и «Примечаниями» нет конкретной связи (поскольку не все примечания связаны со счетами-фактурами).).

Есть ли способ переписать этот окончательный запрос, чтобы он не отправлял отдельный запрос на примечание для каждого счета в таблице?

1 Ответ

1 голос
/ 07 марта 2019

Проблема действительно в коррелированном свойстве подзапроса в сравнении со свойством навигации по коллекции.В переводчике запросов EF Core по-прежнему возникают проблемы при обработке таких подзапросов, которые на самом деле являются логическими свойствами навигации по коллекциям и должны обрабатываться аналогичным образом.

Интересно, что имитация свойства навигации по коллекции с промежуточной проекцией (оператор let вСинтаксис запроса LINQ), кажется, решает проблему:

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(),
    };
...