LINQ-to-Entities Include的奇怪行为
本文关键字:Include LINQ-to-Entities | 更新日期: 2023-09-27 18:11:40
我意识到我不完全理解LINQ-to-Entities中的Include
方法。
JOIN
s)。
// Snippet 1
using (var db = new Db()) {
var author = db.Authors.First();
db.LoadProperty<Author>(author, o => o.Books);
foreach (var book in author.Books) {
db.LoadProperty<Book>(book, o => o.Editions);
foreach (var edition in book.Editions)
Response.Write(edition.Id + " - " + edition.Title + "<br />");
}
}
Response.Write("<br />");
// Snippet 2
using (var db = new Db()) {
var author = db.Authors.Include("Books.Editions").First();
foreach (var book in author.Books) {
foreach (var edition in book.Editions)
Response.Write(edition.Id + " - " + edition.Title + "<br />");
}
}
但是每个片段的输出是不同的:
1 - Some Book First Edition
2 - Another Book First Edition
3 - Another Book Second Edition
4 - Another Book Third Edition
8 - Some Book First Edition
9 - Another Book First Edition
第一个片段正确地输出{Edition Id} - {Edition Title}
,而第二个片段意外地输出{Book Id} - {Edition Title}
,并且只给出每本书的第一版。
怎么回事?是否有一种方法可以使用Include
实现所需的输出?
编辑1: MySql数据看起来像(更正):
Authors = { { Id = 1, Name = "Some Author" } }
Books = { { Id = 8, AuthorId = 1 },
{ Id = 9, AuthorId = 1 } }
Editions = { { Id = 1, Title = "Some Book First Edition" },
{ Id = 2, Title = "Another Book First Edition" },
{ Id = 3, Title = "Another Book Second Edition" },
{ Id = 4, Title = "Another Book Third Edition" } }
EditionsInBooks = { { BookId = 8, EditionId = 1 },
{ BookId = 9, EditionId = 2 },
{ BookId = 9, EditionId = 3 },
{ BookId = 9, EditionId = 4 } }
请注意,没有Edition
与Id = 8
或Id = 9
。
上面的代码是我的完整代码,在Page_Load
中为一个空的测试页。
EDIT 2:我已经测试了以下内容,它们没有区别:
-
var author = db.Authors.Include("Books.Editions").AsEnumerable().First();
-
var author = db.Authors.Include("Books.Editions").Single(o => o.Id == 1);
-
var author = db.Authors.Include("Books").Include("Books.Editions").First();
EDIT 3:如果我启用延迟加载,下面的工作(在代码片段2中):
var author = db.Authors.First();
(我想这实际上与代码片段1的操作相同)
但是,不管是否延迟加载,这仍然返回奇怪的输出:
var author = db.Authors.Include("Books.Editions").First();
EDIT 4:很抱歉,我把上面的表结构表达错了。(我正经历着这样的日子。)现在它被修正了,以显示多对多关系。请参见编辑1
还有
的输出((ObjectQuery)db.Authors.Include("Books.Editions").AsEnumerable())
.ToTraceString()
SELECT
`Project1`.`Id`,
`Project1`.`Name`,
`Project1`.`C2` AS `C1`,
`Project1`.`id1`,
`Project1`.`AuthorId`,
`Project1`.`C1` AS `C2`,
`Project1`.`id2`,
`Project1`.`Title`
FROM (SELECT
`Extent1`.`Id`,
`Extent1`.`Name`,
`Join2`.`Id` AS `id1`,
`Join2`.`AuthorId`,
`Join2`.`Id` AS `id2`,
`Join2`.`Title`,
CASE WHEN (`Join2`.`Id` IS NULL) THEN (NULL)
WHEN (`Join2`.`BookId` IS NULL) THEN (NULL)
ELSE (1) END AS `C1`,
CASE WHEN (`Join2`.`Id` IS NULL) THEN (NULL)
ELSE (1) END AS `C2`
FROM `authors` AS `Extent1`
LEFT OUTER JOIN (SELECT
`Extent2`.`Id`,
`Extent2`.`AuthorId`,
`Join1`.`BookId`,
`Join1`.`EditionId`,
`Join1`.`Id` AS `Id1`,
`Join1`.`Title`
FROM `books` AS `Extent2`
LEFT OUTER JOIN (SELECT
`Extent3`.`BookId`,
`Extent3`.`EditionId`,
`Extent4`.`Id`,
`Extent4`.`Title`
FROM `editionsinbooks` AS `Extent3`
INNER JOIN `editions` AS `Extent4`
ON `Extent4`.`Id` = `Extent3`.`EditionId`) AS `Join1`
ON `Extent2`.`Id` = `Join1`.`BookId`) AS `Join2`
ON `Extent1`.`Id` = `Join2`.`AuthorId`) AS `Project1`
ORDER BY
`Project1`.`Id` ASC,
`Project1`.`C2` ASC,
`Project1`.`id1` ASC,
`Project1`.`C1` ASC
CASE
语句很有趣,因为我的MySql字段都不是可空的。
实体框架的提供者在编译LINQ语句中的First()
表达式时可能存在错误。当Include
参与其中时,偶尔会出现一些奇怪的情况:http://wildermuth.com/2008/12/28/Caution_when_Eager_Loading_in_the_Entity_Framework
尝试将第二个代码片段重写为:
using (var db = new Db()) {
var author = db.Authors.Include("Books.Editions").AsEnumerable().First();
foreach (var book in author.Books)
{
foreach (var edition in book.Editions)
{
Response.Write(edition.Id + " - " + edition.Title + "<br />");
}
}
}
如果这修复了你的输出,那么它肯定是First()
方法。
EDIT:你是正确的关于你的第三次编辑做同样的事情片段1。我不明白include语句怎么会把事情搞得这么糟。我唯一能鼓励的是查看它生成的SQL查询:
var sql = ((System.Data.Objects.ObjectQuery)db.Authors.Include("Books.Editions").AsEnumerable().First()).ToTraceString();
EDIT 2:很可能问题出在EF的MySQL提供程序中,因为生成了疯狂的sql输出。