Ładowanie encji powiązanych
W tym celu należy skorzystać z funkcji Include(), gdzie przez string podaje się nazwę navigation property..
var customers = context.Customers.Include("CustomerType"). Include("CustomerEmails"); foreach (var customer in customers) { Console.WriteLine("Name : {0}, Email : {1}", customer.Name, customer.CustomerType.Description); foreach (var customerEmail in customer.CustomerEmails) { Console.WriteLine(customerEmail.Email); } }
Wykorzystując funkcję Include powyższy kod przetransformuje się do jednego dużego zapytania do bazy danych. Jeśli usuniemy Include, dostejemy po dwa dodatkowe zapytania do bazy danych dla każdej encji z Customers.
Pobieranie całego grafu encji
W bardziej skomplikowanym przypadku, przedstawionym na poniższym diagramie, poprzez odpowiedni dobór kolejności wywołań funkcji Include(), również możemy w jednym zapytaniu załadować do pamięci pełny graf.
var graph = context.Courses.Include("Sections.Students") .Include("Sections.Instructor");
A zatem korzystając z funkcji Include możemy podawać nazwy encji z całego grafu poprzez ścieżkę do takiej nazwy budowaną przy użyciu navigation properties.
W przypadku dziedziczenia encji, gdy tworzymy zapytanie o encje dziedziczące, możemy bez problemu korzystać z navigation properties encji bazowych.
Korzystanie z Include w dowolnych zapytaniach LINQ
Jeżeli chcemy wykorzystać metodę Include() w przypadku dowolnych zapytań, np. joinów, group by czy where, warto pamiętać o kilku faktach:
- Niezmaterializowane wyrażenie LINQ musimy rzutować do typu ObjectQuery<T>, aby móc skorzystać z metody Include()
- Include() jest stosowane jedynie do końcowych rezultatów zapytania, w podzapytaniach będzie ignorowane
- Include() będzie ignorowane, gdy kolekcja zawiera cokolwiek innego niż encje
var events = from ev in context.Events where ev.Club.City == "New York" group ev by ev.Club into g select g.FirstOrDefault(e1 => e1.EventDate == g .Min(s => s.EventDate)); var e = ((ObjectQuery<Event>) events).Include("Club").First();
Opóźnione ładowanie encji powiązanych
Sytuacja przedstawia się następująco: mamy jeden obiekt w pamięci i chcemy pobrać dla niego dane z wielu encji powiązanych. Nie chcemy drugi raz pobierać instancji obiektu zmaterializowanego. Możemy skorzystać z metody CreateSourceQuery(), a na zwróconym przez nią obiekcie wywołać dopiero Include(). Tak zbudowane zapytanie należy dołożyć do kontekstu za pomocą funkcji Attach(). Jest to rozwiązanie bardzo wydajne, ponieważ nie musimy po raz drugi pozyskiwać kolumn z encji, którą mamy już w pamięci.
var jill = context.Employees.Where(e => e.Name == "Jill Carpenter").First(); var moreResults = jill.DepartmentReference.CreateSourceQuery() .Include("Company").First(); context.Attach(moreResults); Console.WriteLine("{0} works in {1}", jill.Name, jill.Department.Company.Name);
CreateSourceQuery() zwraca obiekt zapytania, który w momencie wykonania zwraca ten sam zestaw obiektów, który istnieje w obecnej kolekcji.
Attach() dołącza obiekt lub graf obiektów do kontekstu.
Filtrowanie i sortowanie encji powiązanych
Mamy encję w pamięci i chcemy przefiltrować oraz posortować encje z nią powiązane. Kolejny raz skorzystać można z metody CreateSourceQuery(), która pozwoli uzyskać dostęp do zapytania używanego przy pobieraniu kolekcji przez navigation property. Funkcję Include() musimy wywołać w momecie, gdy mamy do czynienia z odpowiednim typem (ObjectQuery<T>). Metoda Attach() łączy przefiltrowane encje z pierwszą encją w pamięci.
var hotel = context.Hotels.First(); var rooms = hotel.Rooms.CreateSourceQuery() .Include("Reservations") .Where(r => r is ExecutiveSuite && r.Reservations.Any()) .OrderBy(r => r.Rate); hotel.Rooms.Attach(rooms);
Sprawdzenie, czy referencja została już załadowana do pamięci
Chcemy sprawdzić, czy dana referencja do encji powiązanej (lub powiązanej kolekcji) została już załadowana do kontekstu. EF udostępnie property IsLoaded. Różnica polega na tym, że dla kolekcji sprawdzamy IsLoaded bezpośrednio na navigation property, natomiast dla pojedynczych referencji przez nazwa_propertyReference.
var project = context.Projects.Include("Manager").First(); if(project.ManagerReference.IsLoaded) Console.WriteLine("Manager reference is loaded"); else Console.WriteLine("Manager reference is NOT loaded"); if(project.Contractors.IsLoaded) Console.WriteLine("Contractors are loaded");
Aby doczytać kolekcje w sposób jawny, można skorzystać z funkcji Load(), przed jej wywołaniem warto sprawdzić, czy referencja jest już w pamięci za pomocą IsLoaded. Metoda Load jest przeładowana tak, że jako parametr przyjmuje flagę MergeOption, dostępne opcje:
- AppendOnly dołącza instancje, których nie ma obecnie w kontekście
- OverwriteChanges przywraca do kontekstu stan z bazy danych
- NoTracking wyłącza śledzenie stanu
- PreserveChanges przeciwieństwo dla OverwriteChanges
Brak komentarzy:
Prześlij komentarz