POCOクラスをデコレートして、子エンティティをロードするたびにInclude()
を使用せずに自動的にeager-loadする方法はありますか?
ホイール、ドア、エンジン、バンパー、ウィンドウ、排気などの複雑なタイプのプロパティを持つクラスカーがあるとします。アプリでは、DbContext 20の異なる場所から車をさまざまなクエリなどで読み込む必要があります。車をロードするたびにすべてのプロパティを含めるように指定する必要はありません。
私は言いたい
_List<Car> cars = db.Car
.Where(x => c.Make == "Ford").ToList();
//NOT .Include(x => x.Wheels).Include(x => x.Doors).Include(x => x.Engine).Include(x => x.Bumper).Include(x => x.Windows)
foreach(Car car in cars)
{
//I don't want a null reference here.
String myString = car.**Bumper**.Title;
}
_
どういうわけか、POCOクラスやOnModelCreating()
を装飾したり、車をロードするときに車のすべての部分をロードするように設定するEFの構成を設定できますか?私はこれを熱心に行いたいので、私の理解では、ナビゲーションプロパティを仮想化することはできません。 NHibernateが同様の機能をサポートしていることは知っています。
何か足りないのかと思って。前もって感謝します!
乾杯、
ネイサン
私は以下の解決策が好きですが、拡張メソッドへの呼び出しをネストできるかどうか疑問に思っています。たとえば、Engineで同様の状況があるとしますどこにも含めたくない部分がたくさんあります。このようなことはできますか? (私はまだこれが機能する方法を見つけていません)。このようにして、後でEngineがFuelInjectorsを必要とすることがわかった場合、BuildEngineにのみ追加でき、BuildCarにも追加する必要はありません。 また、呼び出しをネストできる場合、どのようにコレクションへの呼び出しをネストできますか? BuildCar()内から各ホイールのBuildWheel()を呼び出したいですか?
_public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.Include(x => x.Wheels).BuildWheel()
.Include(x => x.Doors)
.Include(x => x.Engine).BuildEngine()
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
public static IQueryable<Engine> BuildEngine(this IQueryable<Engine> query) {
return query.Include(x => x.Pistons)
.Include(x => x.Cylendars);
}
//Or to handle a collection e.g.
public static IQueryable<Wheel> BuildWheel(this IQueryable<Wheel> query) {
return query.Include(x => x.Rim)
.Include(x => x.Tire);
}
_
これは、このような状況で他の誰にも役立つ場合に非常によく似た別のスレッドですが、拡張メソッドへの次の呼び出しを行うことができません。
いいえ マッピングではできません 。一般的な回避策は、単純な拡張方法です。
public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.Include(x => x.Wheels)
.Include(x => x.Doors)
.Include(x => x.Engine)
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
これで、すべてのリレーションでCar
をクエリするたびに、次のようになります。
var query = from car in db.Cars.BuildCar()
where car.Make == "Ford"
select car;
編集:
そのように呼び出しをネストすることはできません。インクルードは、使用しているコアエンティティで機能します。そのエンティティはクエリの形状を定義するため、Include(x => Wheels)
を呼び出した後でもIQueryable<Car>
を使用しており、IQueryable<Engine>
の拡張メソッドを呼び出すことはできません。再びCar
で開始する必要があります。
public static IQueryable<Car> BuildCarWheels(this IQuerable<Car> query) {
// This also answers how to eager load nested collections
// Btw. only Select is supported - you cannot use Where, OrderBy or anything else
return query.Include(x => x.Wheels.Select(y => y.Rim))
.Include(x => x.Wheels.Select(y => y.Tire));
}
この方法は次のように使用します。
public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.BuildCarWheels()
.Include(x => x.Doors)
.Include(x => x.Engine)
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
使用法は、Include(x => x.Wheels)
を呼び出しません。これは、ネストされたエンティティの積極的な読み込みを要求したときに自動的に追加される必要があるためです。
このような複雑な熱心なロード構造によって生成される複雑なクエリに注意してください。その結果、パフォーマンスが非常に低下し、データベースから 大量の重複データが転送されます になります。
これと同じ問題があり、Include
属性に言及した他のリンクを見ました。私のソリューションでは、IncludeAttribute
という属性を作成したと想定しています。次の拡張メソッドと ユーティリティメソッド を使用:
public static IQueryable<T> LoadRelated<T>(this IQueryable<T> originalQuery)
{
Func<IQueryable<T>, IQueryable<T>> includeFunc = f => f;
foreach (var prop in typeof(T).GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(IncludeAttribute))))
{
Func<IQueryable<T>, IQueryable<T>> chainedIncludeFunc = f => f.Include(prop.Name);
includeFunc = Compose(includeFunc, chainedIncludeFunc);
}
return includeFunc(originalQuery);
}
private static Func<T, T> Compose<T>(Func<T, T> innerFunc, Func<T, T> outerFunc)
{
return arg => outerFunc(innerFunc(arg));
}