Stu Smith氏は自身のブログの最近の記事で、「LINQ to Entitiesは直前に実行したクエリーによって異なる結果を返すぞ(リンク)」と主張した。もしそれが本当なら、Entity Frameworkは利用するにはとても難しいものになる。私たちはADO.NETチームのElisa Flasko氏に事の真相について尋ねた。
このケースは実のところ著者の誤解です。著者が実行した2つ目のクエリー、var orderは実際にはLINQ to Objectsクエリーであって、LINQ to Entities(もしくはLINQ to SQL)クエリーではありません。LINQ to Objectsクエリーは最初のクエリー、すなわちvar aliceでメモリーに取り込んだデータに対して問い合わせを行っているのです。
結果が異なる理由は、LINQ to SQLが遅延ロードをサポートしているのに対し、Entity Frameworkのバージョン1はサポートしていないからです。最初のクエリーについては、著者はcustomerエンティティだけをメモリーに取ってきています(これはLINQ to SQLもLINQ to ENtitiesも同じです)。しかし2つ目のクエリー(orders)では、著者は関連するOrdersエンティティへのアクセスを試みています。ここで遅延ロードならデータベースに情報を取り出すために2回目のアクセスをするのですが、Entity Frameworkの明示的なローディングでは、特別にデータベースへ「魔法のように」アクセスすることはありません。Ordersはメモリーには存在せず、LINQ to ObjectsクエリーをLINQ to Entitiesの結果にかぶせても、Ordersは取得できないのです。一方、foreachをOrdersにかけた後では(著者ははっきり data.Ordersを呼ぶことで、コンテキストにアクセスし、データベースのすべてのOrdersに問い合わせをかけています)、Ordersはメモリーに置かれ、そのためLINQ to Objectsがそれらに問い合わせることができたのです。
実を言うと、私たちはEntity FrameworkのV2で遅延ロードを利用できるようにしています。とはいえ、デフォルトで遅延ロードがオフになっているのは変わりません。その理由は、開発者がデータベースをN+1回検索してしまう性能問題にうっかりはまってしまわないことを確実にするためです。N+1問題は遅延ロードでは簡単に起きてしまいます。それよりは、明示的にフレームワークに伝えることで、開発者が遅延ロードによる性能問題に気を遣わせない方を選びました。
さて、これは重要な設計の問題だろうか?それとも単なるトレーニングの問題だろうか?