を呼び出している.AsEnumerable()
ため、クエリは LINQ to Entities を使用して評価されるのではなく、LINQ to Objects を使用して評価されます。
これは、最初のものは 2 回往復する可能性が高いことを意味します。1 回はすべての Car をプルし、もう 1 回はすべての CarType をプルします。次に、LINQ to Objects がそのような操作に使用するアルゴリズムを使用して、ローカルで結合を実行します。
2 つ目は、おそらく N + 1 回の往復を行っています。ここで、N は CarType の数です。すべての車を往復して取得すると、それらの車の 1 つに Entity Framework がまだ読み込まれていない CarTypeId があるたびに、データベースに戻ってその CarType が選択されます。
LINQPad の [SQL] タブを使用すると、プログラムによって実行されているすべての LINQ クエリを確認できます。
この場合に適用する必要があるベスト プラクティスは.AsEnumerable()
、Entity Framework オブジェクト セットを呼び出さないことです。代わりに、クエリ全体を作成し、最後に呼び出し.ToList()
て結果をキャプチャします。LINQ to Entities クエリ内では機能しない.AsEnumerable()
ため、回避策として呼び出している可能性がありますが、クエリからその部分を簡単に削除できます。Guid.Parse()
LINQPad でCtrl-2を押して C# ステートメント モードに切り替え、次のようなクエリを実行します。
var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224");
var carTypeNames =
(from c in Cars
where c.CarId == guid
select new CarType {
Name = c.CarType.Name
}).ToList();
carTypeNames.Dump();
指定された 2 つのクエリは、適切に実行された場合、ほぼ同等のパフォーマンスを発揮するはずです。そのため、より簡潔で読みやすいため、Navigation プロパティを優先する必要があります。または、好みに応じて、クエリを逆にして CarType コレクションに基づくものにすることもできます。
var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224");
var carTypeNames =
(from ct in CarTypes
where ct.Cars.Any(c => c.CarId == guid)
select new CarType {
Name = c.CarType.Name
}).ToList();
carTypeNames.Dump();
アップデート
次のようなエンティティ オブジェクトを作成しないでください。
public class CarTypeSummary
{
public string Name{get;set;}
}
void Main()
{
var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224");
var carTypeNames =
(from ct in CarTypes
where ct.Cars.Any(c => c.CarId == guid)
select new CarTypeSummary {
Name = c.CarType.Name
}).ToList();
carTypeNames.Dump();
}
運用コードでは、多くの場合、API を基礎となるデータ型から切り離して、コードをどこも変更することなく柔軟に変更できるようにすることをお勧めします。
public interface ICarTypeSummary{string Name{get;}}
public class CarTypeSummary : ICarTypeSummary
{
public string Name{get;set;}
}
public ICarTypeSummary GetCarTypeSummaryForCar(Guid guid)
{
return (from ct in CarTypes
where ct.Cars.Any(c => c.CarId == guid)
select new CarTypeSummary {
Name = c.CarType.Name
}).FirstOrDefault();
}
このようにして、将来、実際の CarType を返すだけでよいと判断した場合、Entity Framework のキャッシュ メカニズムを利用するために、API をいじらずに実装を変更できます。
// Make the Entity class implement the role interface
public partial class CarType : ICarTypeSummary {}
public ICarTypeSummary GetCarTypeSummaryForCar(Guid guid)
{
return CarTypes.FirstOrDefault(
ct => ct.Cars.Any(c => c.CarId == guid));
}