Bagikan melalui


Evaluasi Klien vs. Server

Sebagai aturan umum, Entity Framework Core mencoba mengevaluasi kueri di server sebanyak mungkin. EF Core mengonversi bagian kueri menjadi parameter, yang dapat dievaluasi di sisi klien. Kueri lainnya (bersama dengan parameter yang dihasilkan) diberikan kepada penyedia database untuk menentukan kueri database yang setara untuk dievaluasi di server. EF Core mendukung evaluasi klien parsial dalam proyeksi tingkat atas (pada dasarnya, panggilan terakhir ke Select()). Jika proyeksi tingkat atas dalam kueri tidak dapat diterjemahkan ke server, EF Core akan mengambil data yang diperlukan dari server dan mengevaluasi bagian kueri yang tersisa pada klien. Jika EF Core mendeteksi ekspresi, di tempat mana pun selain proyeksi tingkat atas, yang tidak dapat diterjemahkan ke server, maka itu melempar pengecualian runtime. Lihat Cara kerja kueri untuk memahami bagaimana EF Core menentukan apa yang tidak dapat diterjemahkan ke server.

Catatan

Sebelum versi 3.0, Entity Framework Core mendukung evaluasi klien di mana saja dalam kueri. Untuk informasi selengkapnya, lihat bagian versi sebelumnya.

Tip

Anda dapat melihat contoh artikel ini di GitHub.

Evaluasi klien dalam proyeksi tingkat atas

Dalam contoh berikut, metode pembantu digunakan untuk menstandarkan URL untuk blog, yang dikembalikan dari database SQL Server. Karena penyedia SQL Server tidak memiliki wawasan tentang bagaimana metode ini diterapkan, tidak mungkin untuk menerjemahkannya ke dalam SQL. Semua aspek lain dari kueri dievaluasi dalam database, tetapi meneruskan yang dikembalikan URL melalui metode ini dilakukan pada klien.

var blogs = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
    .ToList();
public static string StandardizeUrl(string url)
{
    url = url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

Evaluasi klien yang tidak didukung

Meskipun evaluasi klien berguna, terkadang dapat mengakibatkan performa yang buruk. Pertimbangkan kueri berikut, di mana metode pembantu sekarang digunakan di filter tempat. Karena filter tidak dapat diterapkan dalam database, semua data perlu ditarik ke dalam memori untuk menerapkan filter pada klien. Berdasarkan filter dan jumlah data di server, evaluasi klien dapat mengakibatkan performa yang buruk. Jadi Entity Framework Core memblokir evaluasi klien tersebut dan melempar pengecualian runtime.

var blogs = context.Blogs
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();

Evaluasi klien eksplisit

Anda mungkin perlu memaksa evaluasi klien secara eksplisit dalam kasus-kasus tertentu seperti berikut

  • Jumlah data kecil sehingga mengevaluasi klien tidak dikenakan penalti performa yang sangat besar.
  • Operator LINQ yang digunakan tidak memiliki terjemahan sisi server.

Dalam kasus seperti itu, Anda dapat secara eksplisit memilih evaluasi klien dengan memanggil metode seperti AsEnumerable atau ToList (AsAsyncEnumerable atau ToListAsync untuk asinkron). Dengan menggunakan AsEnumerable Anda akan mengalirkan hasilnya, tetapi menggunakan ToList akan menyebabkan buffering dengan membuat daftar, yang juga membutuhkan memori tambahan. Meskipun jika Anda menghitung beberapa kali, maka menyimpan hasil dalam daftar akan lebih membantu karena hanya ada satu kueri ke database. Tergantung pada penggunaan tertentu, Anda harus mengevaluasi metode mana yang lebih berguna untuk kasus ini.

var blogs = context.Blogs
    .AsEnumerable()
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();

Tip

Jika Anda menggunakan AsAsyncEnumerable dan ingin menyusun kueri lebih lanjut di sisi klien, Maka Anda dapat menggunakan pustaka System.Interactive.Async yang menentukan operator untuk asinkron enumerables. Untuk informasi selengkapnya, lihat operator linq sisi klien.

Potensi kebocoran memori dalam evaluasi klien

Karena terjemahan dan kompilasi kueri mahal, EF Core menyimpan cache rencana kueri yang dikompilasi. Delegasi yang di-cache dapat menggunakan kode klien saat melakukan evaluasi klien proyeksi tingkat atas. EF Core menghasilkan parameter untuk bagian pohon yang dievaluasi klien dan menggunakan kembali rencana kueri dengan mengganti nilai parameter. Tetapi konstanta tertentu di pohon ekspresi tidak dapat dikonversi menjadi parameter. Jika delegasi yang di-cache berisi konstanta tersebut, maka objek tersebut tidak dapat dikumpulkan karena masih direferensikan. Jika objek seperti itu berisi DbContext atau layanan lain di dalamnya, maka itu dapat menyebabkan penggunaan memori aplikasi tumbuh dari waktu ke waktu. Perilaku ini umumnya merupakan tanda kebocoran memori. EF Core memberikan pengecualian setiap kali muncul di konstanta jenis yang tidak dapat dipetakan menggunakan penyedia database saat ini. Penyebab umum dan solusinya adalah sebagai berikut:

  • Menggunakan metode instans: Saat menggunakan metode instans dalam proyeksi klien, pohon ekspresi berisi konstanta instans. Jika metode Anda tidak menggunakan data apa pun dari instans, pertimbangkan untuk membuat metode statis. Jika Anda memerlukan data instans dalam isi metode, teruskan data tertentu sebagai argumen ke metode .
  • Meneruskan argumen konstanta ke metode: Kasus ini muncul umumnya dengan menggunakan this dalam argumen ke metode klien. Pertimbangkan untuk memisahkan argumen menjadi beberapa argumen skalar, yang dapat dipetakan oleh penyedia database.
  • Konstanta lain: Jika konstanta ditemukan dalam kasus lain, maka Anda dapat mengevaluasi apakah konstanta diperlukan dalam pemrosesan. Jika perlu memiliki konstanta, atau jika Anda tidak dapat menggunakan solusi dari kasus di atas, buat variabel lokal untuk menyimpan nilai dan menggunakan variabel lokal dalam kueri. EF Core akan mengonversi variabel lokal menjadi parameter.

Versi sebelumnya

Bagian berikut berlaku untuk versi EF Core sebelum 3.0.

Versi EF Core lama mendukung evaluasi klien di bagian mana pun dari kueri--bukan hanya proyeksi tingkat atas. Itulah sebabnya kueri yang mirip dengan kueri yang diposting di bawah bagian Evaluasi klien yang tidak didukung berfungsi dengan benar. Karena perilaku ini dapat menyebabkan masalah performa yang tidak diperhatikan, EF Core mencatat peringatan evaluasi klien. Untuk informasi selengkapnya tentang melihat output pengelogan, lihat Pengelogan.

Secara opsional EF Core memungkinkan Anda mengubah perilaku default untuk melemparkan pengecualian atau tidak melakukan apa pun saat melakukan evaluasi klien (kecuali dalam proyeksi). Perilaku melempar pengecualian akan membuatnya mirip dengan perilaku di 3.0. Untuk mengubah perilaku, Anda perlu mengonfigurasi peringatan saat menyiapkan opsi untuk konteks Anda - biasanya di DbContext.OnConfiguring, atau jika Startup.cs Anda menggunakan ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}