2013-04-02 8 views
55

Widzę kilka naprawdę dziwnych perf związanych z bardzo prostym zapytaniem przy użyciu Entity Framework Code-First z platformą .NET 4. Wersja LINQ2Entities wygląda następująco:Entity Framework zapytanie powolne, ale ten sam SQL w SqlQuery jest szybki

context.MyTables.Where(m => m.SomeStringProp == stringVar); 

Zajmuje to 3000 milisekund do wykonania. Wygenerowany kod SQL wygląda bardzo prosto:

SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp], 
... 
FROM [MyTable] as [Extent1] 
WHERE [Extent1].[SomeStringProp] = '1234567890' 

To zapytanie działa niemal natychmiast po uruchomieniu za pomocą Management Studio. Kiedy zmienić kod C#, aby korzystać z funkcji sqlquery, działa w 5-10 milisekund:

context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar); 

Tak, dokładnie ten sam SQL, wynikające z istoty są śledzone zmiany w obu przypadkach, ale dziki różnica między perf dwa. Co daje?

+2

Oczekuję, że widzisz opóźnienia inicjalizacji - prawdopodobnie zobacz kompilację. Zobacz MSDN: ["Rozważania wydajności dla Entity Framework 5"] (http://msdn.microsoft.com/en-us/data/hh949853.aspx) –

+0

Próbowałem generowania widoków i nie wydaje mi się pomóc. Ponadto uruchomiono inne zapytanie EF przed zwolnieniem, aby wykluczyć możliwość inicjalizacji. Nowe zapytanie działało szybko, powolne nadal działało powoli, mimo że rozgrzewka kontekstu miała miejsce podczas pierwszego zapytania. –

+0

@marc_s - Nie, SqlQuery zwróci w pełni zmaterializowaną i śledzoną instancję obiektu. Zobacz http://msdn.microsoft.com/en-us/library/system.data.entity.dbset.sqlquery(v=vs.103).aspx –

Odpowiedz

57

Znaleziono. Okazuje się, że jest to kwestia typów danych SQL. Kolumna SomeStringProp w bazie danych była varcharem, ale EF zakłada, że ​​typy łańcuchów .NET to nvarchary. Wynikowy proces tłumaczenia podczas kwerendy dla DB, aby dokonać porównania, zajmuje dużo czasu. Myślę EF Prof prowadził mnie na manowce trochę tu, dokładniejsze przedstawienie zapytania jest prowadzony byłby następujący:

SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp], 
... 
FROM [MyTable] as [Extent1] 
WHERE [Extent1].[SomeStringProp] = N'1234567890' 

Tak otrzymany poprawka jest do opisywania modelu kodu pierwszego, wskazując poprawną SQL Typ danych:

public class MyTable 
{ 
    ... 

    [Column(TypeName="varchar")] 
    public string SomeStringProp { get; set; } 

    ... 
} 
+0

Niezłe dochodzenie. Twoje zapytanie cierpiało z powodu "niejawnej konwersji", jak wyjaśniono tutaj: http://www.brentozar.com/archive/2012/07/identifying-correcting-sql-server-implicit-conversion/ – Jaime

+0

Uratowałeś mi kilka godzin debugowania. To był dokładnie problem. – Cody

+0

Świetny człowiek! Moja procedura jest teraz 20x szybsza! –

2

miałem ten sam problem (kwerenda jest szybki, gdy wykonywane z menedżerem SQL), ale gdy są wykonywane z EF upłynie czas.

Okazuje się, że encja (która została utworzona z widoku) miała niepoprawne klucze encji. Tak więc istota miała zduplikowane wiersze z tymi samymi kluczami i myślę, że musiała robić grupowanie w tle.

1

doszedłem również po drugiej stronie to z kompleksem zapytanie ef. Jedną z poprawek dla mnie, która zmniejszyła 6 sekundową kwerendę ef do kwerendy drugiej sekundy sql, która została wygenerowana, było wyłączenie leniwego ładowania.

Aby znaleźć to ustawienie (ef 6), przejdź do pliku .edmx i sprawdź Właściwości -> Generowanie kodu -> Włączono ładowanie Lazy. Ustaw na false.

Ogromna poprawa wydajności dla mnie.

+3

To fajne, ale nie ma nic wspólnego z pytaniem o plakaty. –

25

Przyczyną spowolnienia moje pytania skierowane w EF porównywał pustych skalary z pustych skalarów:

long? userId = 10; // nullable scalar 

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value 
           ^^^^^^^^^ ^^^^^^ 
           Type: long Type: long? 

To zapytanie trwało 35 sekund. Ale malutki refaktoryzacja:

long? userId = 10; 
long userIdValue = userId.Value; // I've done that only for the presentation pursposes 

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList() 
           ^^^^^^^^^ ^^^^^^^^^^^ 
           Type: long Type: long 

daje niesamowite rezultaty. Trwało to tylko 50ms. Możliwe, że jest to błąd w EF.

+4

To jest takie dziwne –

1

Możesz używać następujących sztuczki, aby zapiąć zapytań -

  1. Ustawic ctx.Configuration.ProxyCreationEnabled do false tuż przed dostać kontekst.
  2. Również, .Select(c => new {c.someproperty}) pobierze tylko wymagane dane, a nie całą wiązkę.

Daj mi znać, jeśli to pomogło.

0

Miałem również ten problem. Okazuje się, że winowajcą w moim przypadku było wyczuwanie parametrów SQL-Server. Read more.

Pierwszą wskazówką, że moim problemem było w rzeczywistości ze względu na wąchanie parametrów było to, że uruchomienie zapytania z "set arithabort off" lub "set arithabort on" spowodowało drastycznie inne czasy wykonania w Management Studio. Dzieje się tak dlatego, że domyślnie ADO.NET używa "set arithabort off", a Management Studio domyślnie "ustawia arithabort on", a cache planu zapytań utrzymuje różne plany w zależności od tego parametru.

I wyłączono buforowanie planu kwerend dla zapytania, z rozwiązaniem można znaleźć here.

Powiązane problemy