Entity Framework - Uwzględnij wiele poziomów właściwości

376

Metoda Include () działa całkiem dobrze w przypadku list na obiektach. Ale co, jeśli muszę zejść dwa poziomy w głąb? Na przykład poniższa metoda zwróci ApplicationServers z dołączonymi właściwościami pokazanymi tutaj. Jednak ApplicationsWithOverrideGroup to kolejny kontener, w którym przechowywane są inne złożone obiekty. Czy mogę wykonać Include () również dla tej właściwości? Lub w jaki sposób mogę w pełni załadować tę właściwość?

W obecnej postaci ta metoda:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

Zapełni tylko właściwość Enabled (poniżej), a nie właściwości Application lub CustomVariableGroup (poniżej). Jak to zrobić?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}
Bob Horn
źródło
Witam, Dlaczego otrzymuję wyjątek Expression must be a member expression, gdy próbuję to: Aby dołączyć kolekcję a potem zbieranie jeden poziom w dół: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang
1
@ BobHorn, mam ten sam problem .. W moim przypadku zagnieżdżanie sięga głęboko w dół wielu warstw, udało mi się zrobić dołączenie, o którym wspomniałeś. W wygenerowanym SQL mogłem zobaczyć, że wszystkie kolumny zwracają różne nazwy aliasów jako c1, c2 coś takiego. Moje pytanie brzmi: w jaki sposób mogę utworzyć zagnieżdżoną kolekcję DTO ze wszystkich moich dołączeń :( .. Być może możesz wziąć powyższy przykład sam, w którym zwracamy wszystkie kolumny bez żadnego niestandardowego DTO (który sam jest kolekcją DTO )
TechQuery,

Odpowiedzi:

703

Dla EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

Pamiętaj, aby dodać, using System.Data.Entity;aby uzyskać wersję, Includektóra przyjmuje lambda.


Dla EF Core

Użyj nowej metody ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);
Diego Torres
źródło
1
Nie mogę wykonać Include () w ApplicationsWithOverrideGroup. Nie pojawia się w inteligencji.
Bob Horn
Nie mogę użyć Twojej edycji, ponieważ ApplicationsWithOverrideGroup jest listą. Aplikacja jest właściwością każdego elementu na liście, a nie samej listy.
Bob Horn
1
Ahhhh, ale podany przez ciebie link wydaje się zapewniać odpowiedź. Pozwól mi spróbować: aby dołączyć kolekcję, a następnie kolekcję jeden poziom niżej: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Bob Horn
60
Pamiętaj, aby dołączyć do użycia System.Data.Entity. W przeciwnym razie Intellisense da ci tylko wersję metody Include (string string).
Dz.U. Raqueño,
5
@ Uważaj, że musisz zadzwonić Includedo każdej nieruchomości:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres
72

Jeśli dobrze cię rozumiem, pytasz o włączenie zagnieżdżonych właściwości. Jeśli tak:

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

lub

.Include("ApplicationsWithOverrideGroup.NestedProp")  

lub

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  
Dżudo
źródło
6
Dzięki, mogę spróbować. Miałem nadzieję, że będę w stanie mocno pisać na maszynie i unikać literałów łańcuchowych. Ale jeśli tak trzeba to zrobić ...
Bob Horn
1
Byłeś blisko Mogłem nie mieć pewności, że ApplicationsWithOverrideGroup była listą. Dzięki za pomoc!
Bob Horn
@Judo, mam ten sam problem. W moim przypadku zagnieżdżanie sięga głęboko w dół wielu warstw, udało mi się zrobić to, o czym wspomniałeś. W wygenerowanym SQL mogłem zobaczyć, że wszystkie kolumny zwracają różne nazwy aliasów jako c1, c2 coś takiego. Moje pytanie brzmi: w jaki sposób mogę utworzyć zagnieżdżoną kolekcję DTO ze wszystkich moich dołączeń :( .. Być może możesz wziąć powyższy przykład sam, w którym zwracamy wszystkie kolumny bez żadnego niestandardowego DTO (który sam jest kolekcją DTO )
TechQuery,
2
Pamiętaj, aby dołączyć do użycia System.Data.Entity . W przeciwnym razie Intellisense poda tylko Include(string path)wersję metody.
AlexMelw
52

EF Core: Używanie „ThenInclude” do ładowania wielu poziomów: Na przykład:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();
Thangcao
źródło
53
Wygląda na to, że jest to tylko EF Core
Chris Marisic,
27
FYI: VS2017 inteligencja nie działała dla .ThenInclude. Po prostu wpisz to, co Twoim zdaniem powinno być, a podświetlanie błędów powinno zniknąć.
JohnWrensby
4
Chciałbym podkreślić komentarz @JohnWrensby, Intellisense czasami zajmuje szczególnie dużo czasu, aby obsłużyć je z funkcji WhenInclude, co może być dość mylące dla nowych użytkowników. Miałem też przypadki, w których proste wyrażenie Uwzględnij lambda nie było obsługiwane poprawnie, dopóki po prostu nie wpiszesz go i nie skompilujesz, ignorując „błędy” pokazane w VS.
Pac0
@ Pac0 uratowałeś mi dzień. stara się zobaczyć elementy potomne i nie może.
Bendram,
28

Zrobiłem małego pomocnika dla Entity Framework 6 (styl .Net Core), aby ładnie włączać pod-byty.

Jest teraz w NuGet: Install-Package ThenInclude.EF6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

Pakiet jest dostępny na GitHub .

Lenny32
źródło
cześć, mam wyjątek w czasie wykonywania, nie mogę rzucić IncludableQueryable <observablecollection> na IncludableQueryable <genericcollection>
użytkownik2475096
używam najpierw db i zmodyfikowałem plik tt, aby uzyskać ObservableCollections dla wszystkich moich podmiotów, każda pomoc jest mile widziana.
user2475096
2
@ lenny32 coś wiedzieć o tym rozszerzeniu?
Aaron Hudon
Pamiętaj, że nie jest to wymagane, jeśli właściwość, do której nawigujesz, jest połączona z zestawem DbSet, z którego nawigujesz, i możesz łączyć łańcuch, DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)z tą jedyną wadą, że obliczasz produkt kartezjański i potencjalnie zwiększasz przepustowość.
John Zabroski
23

Więcej przykładów EFCore na MSDN pokazuje, że możesz robić dość skomplikowane rzeczy przy pomocy Includei ThenInclude.

To dobry przykład tego, jak bardzo możesz się skomplikować (to wszystko jedno zdanie!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Zobacz, jak możesz łączyć w łańcuchy, Includenawet po, ThenIncludea to „resetuje” cię z powrotem do poziomu jednostki najwyższego poziomu (Instruktorzy).

Możesz nawet powtórzyć tę samą kolekcję „pierwszego poziomu” (CourseAssignments) wiele razy, a następnie osobne ThenIncludespolecenia, aby dostać się do różnych encji potomnych.

Uwaga: Twoje zapytanie musi być oznaczone na końcu łańcucha Includelub ThenIncludes. Następujące NIE działa:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

Zdecydowanie zalecamy skonfigurowanie rejestrowania i upewnienie się, że zapytania nie wymkną się spod kontroli, jeśli podajesz więcej niż jedną lub dwie rzeczy. Ważne jest, aby zobaczyć, jak to naprawdę działa - a zauważysz, że każde „dołączenie” jest zwykle nowym zapytaniem, aby uniknąć masowego łączenia zwracającego zbędne dane.

AsNoTracking może znacznie przyspieszyć, jeśli nie zamierzasz edytować elementów i ponownie zapisywać.

Simon_Weaver
źródło
Czy istnieje sposób, aby uzyskać zarówno zapisy, jak i wydziały bez powtarzania się. Obejmuje zaliczkę i przypisanie kursu? (Jak dotąd wydaje się, że interfejs API może sięgać głębiej w .ThenInclude lub wrócić do najwyższego poziomu za pomocą .Include, ale nie ma nic, co mogłoby pozostać na tym samym poziomie?)
William Jockusch
Jeśli chcesz, aby leniwe ładowanie było na bieżąco z blogami EF Core 2.1. Msdn.microsoft.com/dotnet/2018/02/02/... ale jeśli chcesz po prostu załadować więcej na tym samym poziomie, myślę, że jest to zgodne z projektem. Nie jestem pewien, co myślisz - nie wymaga to dużo więcej, a to znacznie zmniejsza to, co wraca z bazy danych. Jednostka może mieć tylko jedną lub dwie rzeczy „na tym samym poziomie”, ale może również mieć 50 dla dużego projektu, ponieważ jawność sprawia, że ​​Twoja aplikacja jest znacznie szybsza.
Simon_Weaver
To było dobre wytłumaczenie koncepcji włączenia „resetu” poziomu z powrotem do poziomu początkowego. Pomógł mi owinąć głowę wokół dziedziczności systemu włączonego. Twoje zdrowie!
AFM-Horizon
22

Musiałem także użyć wielu dołączeń i na 3. poziomie potrzebowałem wielu właściwości

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

To może komuś pomóc :)

dnxit
źródło
1
czy można to zrobić bez powtarzania.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd,
cóż, to zależy, jak głęboko chcesz zejść
dnxit
7

Pozwólcie, że jasno stwierdzę, że można użyć przeciążenia łańcucha, aby uwzględnić poziomy zagnieżdżone, niezależnie od mnogości odpowiednich relacji, jeśli nie przeszkadza ci użycie literałów łańcucha:

query.Include("Collection.Property")
mrmashal
źródło
1
Ta metoda pomogła mi dowiedzieć się, jak można to zakodować w VB, ponieważ nie mogę znaleźć nigdzie po godzinach googlingu.
Koder
Działa to dla mnie świetnie, często tego używam !!! Działa nawet w połączeniu z instrukcjami .SelectMany:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie