#linq #linq-to-entities #entity-framework-core #ef-core-2.2
#linq #linq-to-entities #entity-framework-core #ef-core-2.2
Вопрос:
Используя ядро EF 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 может хранить заметки о персоналиях или заметки о счетах (или заметки 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(),
};
}
отправляет отдельный запрос в таблицу примечаний для каждой строки счета в таблице счетов. Итак, если в таблице Invoice 1000 счетов-фактур, это отправляет в базу данных что-то вроде 1001 запроса.
Похоже, что подзапрос Items не имеет такой же проблемы, поскольку существует явная связь между счетами и номенклатурами, в то время как между счетами и примечаниями нет конкретной связи (поскольку не все примечания связаны со счетами).
Есть ли способ переписать этот окончательный запрос таким образом, чтобы он не отправлял отдельный запрос примечания для каждого счета в таблице?
Комментарии:
1. в
GetInvoiceQuery
, если вы временно закомментируете назначение элементов, имеет ли это значение?2. кроме того, установлена ли у вас связь с внешним ключом для NotesID в родительских таблицах в БД?
3. @Matt. G По какой-то причине я не смог напрямую ответить ни на один из ваших комментариев. Не имеет значения, удаляю ли я элементы, и вообще нет столбца NotesID в таблице Invoice (или таблице Person и т.д.). Похоже, у Ивана Стоева было решение.
Ответ №1:
Проблема действительно заключается в коррелированном свойстве навигации по подзапросу и коллекции. У EF Core query translator по-прежнему возникают проблемы с обработкой таких подзапросов, которые на самом деле являются свойствами навигации по логической коллекции и должны были обрабатываться аналогичным образом.
Интересно, что имитация свойства навигации по коллекции с промежуточной проекцией ( 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(),
};