繁体   English   中英

LINQ到实体的奇怪行为包括

[英]Strange behaviour with LINQ-to-Entities Include

我意识到我不完全了解LINQ-to-Entities中的Include方法。

例如,采用下面的两个代码段。 我希望它们产生相同的输出(尽管第一个版本可能会更高效,因为它避免了JOIN )。

// 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 } }

请注意,没有Id = 8Id = 9 Edition

上面的代码是我完整的代码,在Page_Load中有一个空的测试页。

编辑2 :我已经测试了以下内容,它们没有什么不同:

  1. var author = db.Authors.Include("Books.Editions").AsEnumerable().First();
  2. var author = db.Authors.Include("Books.Editions").Single(o => o.Id == 1);
  3. var author = db.Authors.Include("Books").Include("Books.Editions").First();

编辑3 :如果启用了延迟加载,以下工作(在代码段2中):

var author = db.Authors.First();

(我想这基本上与代码片段1相同。)

但是,无论延迟加载如何,这仍然会返回奇怪的输出:

var author = db.Authors.Include("Books.Editions").First();

编辑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

鉴于MySql字段均不可为空,因此CASE语句很有趣。

实体框架的提供程序对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()方法。

编辑:您做的与片段1相同的第三次编辑是正确的,我无法理解include语句是如何使事情如此糟糕的。 我唯一可以鼓励的是看它正在生成的SQL查询:

var sql = ((System.Data.Objects.ObjectQuery)db.Authors.Include("Books.Editions").AsEnumerable().First()).ToTraceString();

编辑2:考虑到生成了疯狂的sql输出,问题很可能出在您的EF的MySQL提供程序中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM