EF Core - 许多查询发送到数据库以进行子查询
EF Core - many queries sent to database for subquery
使用 EF Core 2.2.2,我的数据库中有一个 table,用于存储许多其他 table 的注释。换句话说,它有点像主从关系中的细节 table,但有多个主 table。考虑这个简化的 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 notes 或 Invoice notes(或 InvoiceItem notes,尽管我们只是说 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(),
};
}
针对发票 table 中的每个发票行向备注 table 发送单独的查询。因此,如果 Invoice table 中有 1,000 张发票,这将向数据库发送大约 1,001 个查询。
Items 子查询似乎没有同样的问题,因为 Invoices 和 Items 之间存在明确的关系,而 Invoices 和 Notes 之间没有特定关系(因为并非所有票据都与发票相关) .
有没有办法重写最终查询,这样它就不会为 table 中的每张发票发送单独的注释查询?
问题确实是相关子查询与集合导航 属性。 EF Core 查询翻译器在处理此类子查询时仍然存在问题,这些子查询实际上是逻辑集合导航属性,应该以类似的方式进行处理。
有趣的是,使用中间投影(LINQ 查询语法中的 let
运算符)模拟集合导航 属性 似乎解决了这个问题:
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(),
};
使用 EF Core 2.2.2,我的数据库中有一个 table,用于存储许多其他 table 的注释。换句话说,它有点像主从关系中的细节 table,但有多个主 table。考虑这个简化的 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 notes 或 Invoice notes(或 InvoiceItem notes,尽管我们只是说 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(),
};
}
针对发票 table 中的每个发票行向备注 table 发送单独的查询。因此,如果 Invoice table 中有 1,000 张发票,这将向数据库发送大约 1,001 个查询。
Items 子查询似乎没有同样的问题,因为 Invoices 和 Items 之间存在明确的关系,而 Invoices 和 Notes 之间没有特定关系(因为并非所有票据都与发票相关) .
有没有办法重写最终查询,这样它就不会为 table 中的每张发票发送单独的注释查询?
问题确实是相关子查询与集合导航 属性。 EF Core 查询翻译器在处理此类子查询时仍然存在问题,这些子查询实际上是逻辑集合导航属性,应该以类似的方式进行处理。
有趣的是,使用中间投影(LINQ 查询语法中的 let
运算符)模拟集合导航 属性 似乎解决了这个问题:
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(),
};