Kueri Entity Framework lambat, tetapi SQL yang sama di SqlQuery cepat

95

Saya melihat beberapa kinerja yang sangat aneh terkait dengan kueri yang sangat sederhana menggunakan Entity Framework Code-First dengan .NET framework versi 4. Kueri LINQ2Entities terlihat seperti ini:

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

Ini membutuhkan lebih dari 3000 milidetik untuk dieksekusi. SQL yang dihasilkan terlihat sangat sederhana:

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

Kueri ini berjalan hampir secara instan saat dijalankan melalui Management Studio. Ketika saya mengubah kode C # untuk menggunakan fungsi SqlQuery, ini berjalan dalam 5-10 milidetik:

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

Jadi, SQL yang sama persis, entitas yang dihasilkan dilacak perubahannya dalam kedua kasus, tetapi perbedaan kinerja yang liar di antara keduanya. Apa yang memberi?

Brian Sullivan
sumber
2
Saya berharap Anda melihat penundaan inisialisasi - mungkin melihat kompilasi. Lihat MSDN:Performance Considerations for Entity Framework 5
Nicholas Butler
Saya sudah mencoba membuat tampilan sebelumnya, dan sepertinya tidak membantu. Juga, jalankan kueri EF lain sebelum yang lambat untuk menyingkirkan hal-hal inisialisasi. Kueri baru berjalan cepat, kueri lambat masih berjalan lambat, meskipun pemanasan konteks terjadi selama kueri pertama.
Brian Sullivan
1
@marc_s - Tidak, SqlQuery akan mengembalikan instance entitas yang sepenuhnya terwujud dan dilacak perubahan. Lihat msdn.microsoft.com/en-us/library/…
Brian Sullivan
Apakah SQL yang dihasilkan untuk kueri EF Anda benar-benar menyebariskan nilai parameter, atau menggunakan parameter? Ini seharusnya tidak memengaruhi kecepatan kueri untuk kueri individu, tetapi dapat menyebabkan queryplan membengkak di server seiring waktu.
Jim Wooley
Sudahkah Anda mencoba menjalankan kueri yang sama dua kali / beberapa kali? Berapa lama waktu yang dibutuhkan saat menjalankan kedua kalinya? Sudahkah Anda mencoba ini di .NET Framework 4.5 - ada beberapa peningkatan kinerja terkait EF di .NET Framework 4.5 yang dapat membantu.
Pawel

Jawaban:

97

Menemukannya. Ternyata ini masalah tipe data SQL. The SomeStringPropkolom dalam database adalah varchar, tapi EF mengasumsikan bahwa NET tipe string adalah nvarchars. Proses penerjemahan yang dihasilkan selama query untuk DB melakukan perbandingan adalah yang membutuhkan waktu lama. Saya pikir EF Prof sedikit menyesatkan saya di sini, representasi yang lebih akurat dari kueri yang sedang dijalankan adalah sebagai berikut:

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

Jadi perbaikan yang dihasilkan adalah menganotasi model code-first, yang menunjukkan tipe data SQL yang benar:

public class MyTable
{
    ...

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

    ...
}
Brian Sullivan
sumber
1
Investigasi yang bagus. Permintaan Anda mengalami "konversi implisit", seperti yang dijelaskan di sini: brentozar.com/archive/2012/07/…
Jaime
Menghemat beberapa jam debugging. Inilah masalahnya.
Cody
1
Dalam kasus saya, saya menggunakan EDMX dengan database lama, yang digunakan varcharuntuk semuanya, dan memang inilah masalahnya. Saya ingin tahu apakah saya dapat membuat EDMX untuk mempertimbangkan varchar untuk semua kolom string.
Alisson
1
Penemu yang hebat. tapi @Jaime apa yang harus kita lakukan untuk pendekatan database pertama karena semuanya (misalnya anotasi data pada Model db) dihapus setelah memperbarui model EF dari database.
Nauman Khan
Menetapkan ini sebagai beranda saya untuk sementara waktu sehingga saya dapat menghidupkan kembali kegembiraan menemukan jawaban yang begitu bagus lagi untuk sementara waktu. Terima kasih!!!
OJisBad
44

Alasan memperlambat kueri yang saya buat di EF adalah membandingkan bukan skalar nullable dengan skalar nullable:

long? userId = 10; // nullable scalar

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

Kueri itu membutuhkan waktu 35 detik. Tapi refactoring kecil seperti itu:

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

memberikan hasil yang luar biasa. Hanya butuh 50ms untuk menyelesaikannya. Mungkin saja itu bug di EF.

menangis
sumber
13
Ini sangat aneh
Daniel Cardenas
1
YA TUHAN. Hal ini tampaknya juga dapat terjadi saat menggunakan antarmuka IUserId.Id yang menyebabkan masalah pada saya, tetapi pertama pemetaan Id ke integer berfungsi ... apakah saya harus memeriksa sekarang semua kueri dalam aplikasi 100.000 baris saya?
Dirk Boer
apakah bug ini telah dilaporkan? Ini masih dalam versi terbaru 6.2.0
Dirk Boer
2
Masalah yang sama juga terjadi di EF Core. Terima kasih telah menemukan ini!
Yannickv
Satu saran lainnya adalah memproses variabel sebelum dimasukkan ke dalam ekspresi LINQ. Jika tidak, sql yang dihasilkan akan lebih lama dan lebih lambat. Saya mengalami ketika memiliki ekspresi Trim () dan ToLower () di dalam LINQ yang mengganggu saya.
samheihey
4

Saya memiliki masalah yang sama (kueri cepat saat dijalankan dari SQL manager) tetapi ketika dieksekusi dari EF batas waktu kedaluwarsa.

Ternyata entitas (yang dibuat dari tampilan) memiliki kunci entitas yang salah. Jadi entitas memiliki baris duplikat dengan kunci yang sama, dan saya rasa itu harus melakukan pengelompokan di latar belakang.

Vladimir Gedgafov
sumber
3

Saya juga menemukan ini dengan kueri ef yang kompleks. Satu perbaikan bagi saya yang mengurangi kueri ef 6 detik menjadi kueri sql sub kedua yang dihasilkannya adalah mematikan pemuatan lambat.

Untuk menemukan pengaturan ini (ef 6) pergi ke file .edmx dan lihat di Properties -> Pembuatan kode -> Lazy Loading Enabled. Setel ke false.

Peningkatan besar dalam kinerja bagi saya.

pengguna2622095
sumber
4
Itu keren, tapi tidak ada hubungannya dengan pertanyaan poster.
Jace Rhea
2

Saya punya masalah ini juga. Ternyata penyebab dalam kasus saya adalah SQL-Server parameter sniffing .

Petunjuk pertama bahwa masalah saya sebenarnya karena parameter sniffing adalah bahwa menjalankan kueri dengan "set arithabort off" atau "set arithabort on" menghasilkan waktu eksekusi yang berbeda secara drastis di Management Studio. Ini karena ADO.NET secara default menggunakan "set arithabort off" dan Management Studio default ke "set arithabort on". Cache paket kueri menyimpan paket yang berbeda bergantung pada parameter ini.

Saya menonaktifkan cache paket kueri untuk kueri tersebut, dengan solusi yang dapat Anda temukan di sini .

Oskar Sjöberg
sumber