60

有沒有辦法裝飾你的POCO類來自動加載子實體,而不必在每次加載時使用Include()實體框架 - 有沒有一種方法來自動加載沒有Include()的子實體?

說我有一個類車,車輪,門,引擎,保險槓,窗戶,排氣等複雜類型的屬性和在我的應用程序,我需要從我的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); 
} 

這裏是另一種情況非常相似線程它有助於在這種情況下任何人,但它仍然不處理能夠作出擴展方法nexted電話。

Entity framework linq query Include() multiple children entities

+0

彌敦道,非常有趣的挑戰。請解釋爲什麼你想/需要避免使用像include()這樣的主動加載技術。 –

+0

不,你沒有辦法阻止你創建像DbQuery CompleteCars {在{DbContext}或一個存儲庫類中得到{return ...'(你長的'Include'鏈)。如果可以的話,是不是會切斷從數據庫中獲取「裸車」的可能性? –

+0

我想避免這種情況,因爲我有一個複雜的模型,但記錄很少,所以我希望數據可以加載,但不想錯過應用程序中任何位置的孫子。 –

回答

52

不,你cannot do that in mapping。典型的解決方法是簡單的擴展方法:

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),因爲它應該會自動添加,當你要求其嵌套的eager加載實體。

請注意這種複雜的加載結構產生的複雜查詢。它可能會導致性能很差,並從數據庫中導致a lot of duplicate data transferred

+0

這正是我需要的!謝謝! –

+0

我可以將調用嵌套到子實體的這些擴展方法嗎? (查看更新後的問題) –

+0

讓[Include]屬性獲得我的投票! (見答案中的鏈接) –

1

有這個相同的問題,看到其中提到了Include屬性的鏈接。我的解決方案假定您創建了一個名爲IncludeAttribute的屬性。用下面的擴展方法和utility method

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)); 
    }