Lazy loading
Kiedy korzystamy z EF należy pamiętać o tym, aby zawsze wyłączyć leniwe ładowanie (lazy loading) w przeciwnym wypadku za każdym razem gdy będziemy sięgać po dane które leżą w innej tabeli niż ta, która została początkowo zaciągnięta z bazy danych EF zrobi to za nas. Brzmi fajnie, ale gdy pomyślicie że taka operacja może wykonać się w pętli, pomysł szybko przestaje być tak miły. N wykonań pętli N pojedynczych zapytań do bazy danych. Minusem wyłączenia lenia w EF jest to, że jeśli nie powiemy wcześniej EF że chcemy wykonać operację JOIN nie będziemy mieli dostępu do danych z innej tabeli.
Kod wygląda tak, pierwsze zapytanie bez, drugie z instrukcją JOIN
using System.Collections.Generic; | |
using System.Data.Entity; | |
using System.Linq; | |
namespace SimpleEFConfiguration | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var db = new SimpleDatabase(); | |
var p1 = db.Persons.ToList(); | |
var p2 = db.Persons.Include(person => person.Properties).ToList(); | |
} | |
} | |
public class SimpleDatabase : DbContext | |
{ | |
public SimpleDatabase() | |
{ | |
this.Database.Log = text => System.Diagnostics.Debug.WriteLine(text); | |
this.Configuration.LazyLoadingEnabled = false; | |
} | |
public DbSet<Property> Properties { get; set; } | |
public DbSet<Person> Persons { get; set; } | |
} | |
public class Person | |
{ | |
public int Id { get; set; } | |
public string Name { get; set; } | |
public List<Property> Properties { get; set; } | |
} | |
public class Property | |
{ | |
public int Id { get; set; } | |
public string Name { get; set; } | |
public int PersonId { get; set; } | |
} | |
} |
Linia (@22) wyłącza lazy loading. Linia (@12) wyciąga z bazy tylko osoby, jesli pole Properties będzie nullem. Linia (@13) wyciąga z bazy osoby oraz wykonuje operację JOIN, lista cech w osobie zostanie wypełniona pasującymi wartościami. Ostrzeżenie: ponieważ w przykładzie wywołanie jest jedno pod drugim, gdy postawi się breakpoint po obu (@14), referencja osoby zostanie zaktualizowana. Może się więc okazać, że w przypadku p1 oraz p2 pola properties będą już ustawione. Zalecam w przypadku samodzielnego sprawdzania przykładu, postawić breakpoint odpowiednio w liniach @12 oraz @13 i podejrzenie wartości property.
Logowanie
Często pojawia sie problem logowania zapytań które generuje EF, zawsze szukałem, paczek, rozwiązań czy gotowych bibliotek. Aż pewnego pięknego razu na SO znalazłem odpowiedź, która wygląda tak jak w linii 21. Najprościej jak się da, dopinam się do wewnętrznego logera oraz wypisuje na Debug.Trace informacje o zapytaniach, wyglądają one tak:
Opened connection at 11.03.2016 00:16:47 +01:00 | |
'SimpleEfConfiguration.exe' (CLR v4.0.30319: SimpleEfConfiguration.exe): Loaded 'EntityFrameworkDynamicProxies-SimpleEfConfiguration'. | |
SELECT | |
[Extent1].[Id] AS [Id], | |
[Extent1].[Name] AS [Name] | |
FROM [dbo].[People] AS [Extent1] | |
-- Executing at 11.03.2016 00:16:47 +01:00 | |
-- Completed in 2 ms with result: SqlDataReader | |
Closed connection at 11.03.2016 00:16:47 +01:00 | |
Opened connection at 11.03.2016 00:16:47 +01:00 | |
SELECT | |
[Project1].[Id] AS [Id], | |
[Project1].[Name] AS [Name], | |
[Project1].[C1] AS [C1], | |
[Project1].[Id1] AS [Id1], | |
[Project1].[Name1] AS [Name1], | |
[Project1].[PersonId] AS [PersonId] | |
FROM ( SELECT | |
[Extent1].[Id] AS [Id], | |
[Extent1].[Name] AS [Name], | |
[Extent2].[Id] AS [Id1], | |
[Extent2].[Name] AS [Name1], | |
[Extent2].[PersonId] AS [PersonId], | |
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] | |
FROM [dbo].[People] AS [Extent1] | |
LEFT OUTER JOIN [dbo].[Properties] AS [Extent2] ON [Extent1].[Id] = [Extent2].[PersonId] | |
) AS [Project1] | |
ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC | |
-- Executing at 11.03.2016 00:16:47 +01:00 | |
-- Completed in 4 ms with result: SqlDataReader | |
Closed connection at 11.03.2016 00:16:47 +01:00 |
Widać tam wywołania dwóch zapytań (@12i @13) prosty oraz ten z JOIN, widoczny jest czas potrzeby na wykonywanie operacji jak i informacje o rozpoczęciu i zakończeniu połączenia. Wszystko co potrzeba aby sprawdzić czy zapytanie zostało wygenerowane w akceptowalny przez nas sposób – me gusta!