LINQ-to-EntitiesのInclude
メソッドを完全に理解していないことに気付きました。LINQ-to-Entitiesでの奇妙な動作が含まれています
たとえば、次の2つのコードスニペットを使用します。私はそれらが同じ出力を生成すると期待します(最初のバージョンは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
秒1が予期せず{Book Id} - {Edition Title}
を印刷し、唯一の各書籍の初版を与えるのに対し、最初のスニペットが正しく、{Edition 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 } }
注意をId = 8
かとはEdition
がないこと。
上記のコードは完全なコードです(空のテストページの場合は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つを持っています)多対多の関係を示すために修正されました。編集1.
はまた
((ObjectQuery)db.Authors.Include("Books.Editions").AsEnumerable())
.ToTraceString()
の出力はCASE
文は私のMySQLのフィールドのどれもがNULL可能でないことを考えると、面白いです
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
でご覧ください。
著者、書籍、エディションのすべてのコンテンツを表示できますか?私はあなたの目に会うよりも多くのデータを持っていると思います。情報を重複させる可能性もあります。インクルードステートメントはそれを並べ替えて、 'First()'を別の著者に返します。 – doctorless
ここで@d_r_wと同意しなければなりません。著者だけでなく書籍も出力できますか? –
@d_r_w @John私の編集を参照してください。また、 'edition.Id +" - "+ book.Id +" - "+ author.Id'を出力すると、book IDとauthor IDは正しくなりますが、edition IDはbook IDと同じに出力されます。 – James