2011-10-18 48 views
2

我意識到我不完全理解LINQ-to-Entities中的Include方法。與LINQ到實體的奇怪行爲包括

例如,下面的兩個代碼片段。我期望他們產生相同的輸出(儘管第一個版本可能更高效,因爲它避免了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來實現所需的輸出?

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

注意,沒有用EditionId = 8​​。

而上面的代碼是我的完整代碼,在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(); 

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字段是空。

+2

您能否顯示作者,書籍和版本表的全部內容?我猜你在那裏有更多的數據而不是滿足眼睛,甚至可能有重複的信息,並且include語句重新排序,使First()返回一個不同的作者。 – doctorless

+0

我必須在這裏同意@d_r_w。你可以嘗試輸出作者以及書籍嗎? –

+0

@d_r_w @John查看我的編輯。另外,如果我輸出「edition.Id +」 - 「+ book.Id +」 - 「+ author.Id」,它會得到正確的書籍ID和作者ID,但是版本ID與書籍ID相同。 – James

回答

1

實體框架的提供者在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相同的事情。我無法理解包含語句是如何嚴重地讓事情變得糟糕。我鼓勵的唯一的事情是看看SQL查詢它的產生:

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

編輯2:這很可能是可能的,問題是出在你的MySQL提供商EF,給瘋狂的SQL輸出產生。

+0

感謝您的建議,但恐怕輸出是一樣的。如果我用'Single(o => o.Id == 1)'替換'First()'也是一樣的。 – James

+0

@詹姆斯你有沒有嘗試重新啓用延遲加載? – doctorless

+0

剛剛嘗試過。看我的編輯。 – James