4

これらのクラスがあるとしましょう:

public class Car
{
  public int CarId { get; set} 
  public virtual ICollection<Door> Doors { get; set} 
}

public class Door
{
   public int DoorId { get; set} 
   public decimal Weight { get; set} 
   public int CarId { get; set} 
}

そして、私はこのようなことをしたい

foreach ( var car in db.Cars )
{
   var x = car.Doors.Min(d => d.Weight);
}

EFTraceLog でわかるように、彼は Select * from Doors where CarId = @... のようなものを作成し、db サーバーではなくアプリケーション サーバーで "Min" を計算します

私は非常に大きな車とドアテーブルを持っているので、この操作は数分続きます. しかし、コードをこれに変更すると

foreach ( var car in db.Cars )
{
   var x = db.Doors.Where(d => d.CarId == car.CarId).Min(d => d.Weight);
}

それから数秒です。

なぜこのような大きな違いがあり、どのように修正するのでしょうか? ここでの問題は、書くのがはるかに簡単だということです

var x = car.Doors.Min(d => d.Weight);

それから

var x = db.Doors.Where(d => d.CarId == car.CarId).Min(d => d.Weight);

アップデート

Entity Framework 5.0 を使用しています

更新 2

これらの亜種を試しましたが、遅いです

var x = car.Doors.Select(door => door.Weight).Min();
var x = car.Doors.OrderBy(x => x.Weight).Select(x => x.Weight).FirstOrDefault();
var x = car.Doors.OrderBy(x => x.Weight).Select(x => x.Weight).First();
var x = car.Doors.OrderBy(x => x.Weight).FirstOrDefault().Weight;
var x = car.Doors.OrderBy(x => x.Weight).First().Weight;

速いのはこれだけ

var x = db.Doors.Where(d => d.CarId == car.CarId).Min(d => d.Weight);

アップデート 3

最適なクエリはこの sql を生成します

declare @p__linq__0 Int32 = cast(N'204' as Int32);

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
MIN([Extent1].[Weight]) AS [A1]
FROM [dbo].[Doors] AS [Extent1]
WHERE [Extent1].[CarId] = @p__linq__0
) AS [GroupBy1]
4

2 に答える 2

5

Linq to EntitiesMin余分な突起なしでのみサポートします。Minこれは、 Linq To Objects を使用して current が呼び出されることを意味します(Doorsコレクションが具体化されるため、生成された SQL がログに表示されます)。

MSDNから:

サポート:

TSource Min<TSource>(this IQueryable<TSource> source)

サポートされていません:

TResult Min<TSource, TResult>(this IQueryable<TSource> source,Expression<Func<TSource, TResult>> selector)

と(サポートされている) を使用して、同じ結果を得ることOrderByができます。SelectFirstOrDefault

var min = car.Doors.OrderBy(x => x.Weight).Select(x => x.Weight).FirstOrDefault();

アップデート:

プロジェクションEntity Frameworkを使用した Lazy-Load をサポートしていないようです。つまり、参照されるエンティティ (Car.Doorsあなたの場合) をロードするたびに、フレームワークがすべてのデータを Lazy-Load し、ロードするプロパティ (あなたの場合) を選択できませんWeight

これが、2 つの呼び出しの違いの理由です。

// Accessing 'Doors' thru 'Car' means 'Lazy Load'
car.Doors.Select(x => x.Weight).Min();

しかし、

// No 'Lazy Load' involved, hence projection is possible
db.Doors.Where(x => x.CarId == carId).Select(x => x.Weight).Min();

アクセスCar.DoorsしてEager Load Doorsを試すこともできます:

foreach ( var car in db.Cars.Include(x => x.Doors))
{
   var x = car.Doors.Select(x => x.Weight).Min();
}

(私はServyMinによって提案された短縮された代替案を使用しました)

于 2013-10-03T15:22:55.407 に答える
0

haim770 's answerMinによると、射影を取るオーバーロードはEF ではサポートされていませんがSelect、代わりに使用して最初に射影を実行して、目的の結果を得ることができます。

var minWeight = db.Doors.Select(door => door.Weight).Min();
于 2013-10-03T15:44:40.153 に答える