Pertimbangan Performa (Entity Framework)

Topik ini menjelaskan karakteristik performa ADO.NET Entity Framework dan memberikan beberapa pertimbangan guna membantu meningkatkan performa aplikasi Entity Framework.

Tahapan Eksekusi Kueri

Untuk lebih memahami performa kueri dalam Entity Framework, pahami terlebih dahulu operasi yang terjadi ketika kueri mengeksekusi terhadap model konseptual dan mengembalikan data sebagai objek. Tabel berikut ini menjelaskan rangkaian operasi ini.

Operasi Biaya Relatif Frekuensi Komentar
Memuat metadata Sedang Sekali di setiap domain aplikasi. Metadata model dan pemetaan yang digunakan oleh Entity Framework dimuat ke dalam MetadataWorkspace. Metadata ini di-cache secara global dan tersedia untuk instans ObjectContext lain pada domain aplikasi yang sama.
Membuka koneksi database Sedang1 Sesuai kebutuhan. Karena koneksi terbuka ke database memakan sumber daya yang berharga, Entity Framework membuka dan menutup koneksi database hanya sesuai kebutuhan. Anda juga bisa secara eksplisit membuka koneksi. Untuk informasi selengkapnya, kunjungi Mengelola Koneksi dan Transaksi.
Membuat tampilan Sangat Penting Sekali di setiap domain aplikasi. (Dapat dibuat sebelumnya.) Sebelum Entity Framework bisa mengeksekusi kueri terhadap model konseptual atau menyimpan perubahan pada sumber data, kerangka kerja harus menghasilkan sekumpulan tampilan kueri lokal untuk mengakses database. Karena tingginya biaya untuk menghasilkan tampilan ini, Anda bisa membuat tampilan sebelumnya dan menambahkannya ke proyek pada waktu desain. Untuk informasi selengkapnya, lihat Petunjuk: Membuat Tampilan untuk Meningkatkan Performa Kueri.
Menyiapkan kueri Sedang2 Sekali untuk setiap kueri unik. Termasuk biaya untuk menyusun perintah kueri, membuat pohon perintah berdasarkan model dan pemetaan metadata, serta menentukan bentuk data yang dikembalikan. Karena kini kedua perintah kueri SQL Entitas dan kueri LINQ di-cache, eksekusi nanti dari kueri yang sama membutuhkan waktu lebih sedikit. Anda masih bisa menggunakan kueri LINQ yang dikompilasi untuk mengurangi biaya ini dalam eksekusi selanjutnya dan kueri yang dikompilasi bisa lebih efisien daripada kueri LINQ yang di-cache secara otomatis. Untuk informasi selengkapnya, baca Kueri yang Dikompilasi (LINQ ke Entitas). Untuk informasi umum tentang eksekusi kueri LINQ, baca LINQ ke Entitas. Catatan: Kueri LINQ ke Entitas yang menerapkan operator Enumerable.Contains ke koleksi dalam memori tidak di-cache secara otomatis. Selain itu, parameterisasi koleksi dalam memori dalam kueri LINQ yang dikompilasi tidak diperbolehkan.
Menjalankan kueri Rendah2 Sekali untuk setiap kueri. Biaya pelaksanaan perintah terhadap sumber data dengan menggunakan penyedia data ADO.NET. Karena sebagian besar paket kueri meng-cache sumber data, eksekusi dari kueri yang sama di masa depan mungkin memerlukan waktu lebih sedikit.
Memuat dan memvalidasi jenis Rendah3 Sekali untuk setiap instans ObjectContext. Jenis dimuat dan divalidasi terhadap jenis yang didefinisikan oleh model konseptual.
Pelacakan Rendah3 Sekali untuk setiap objek yang dikembalikan kueri. 4 Jika kueri menggunakan opsi penggabungan NoTracking, tahap ini tidak memengaruhi performa.

Jika kueri menggunakan opsi penggabungan AppendOnly, PreserveChanges, atau OverwriteChanges, hasil kueri dilacak di ObjectStateManager. EntityKey dihasilkan untuk setiap objek terlacak yang dikembalikan kueri dan digunakan untuk membuat ObjectStateEntry di ObjectStateManager. Jika ObjectStateEntry yang sudah ada dapat ditemukan untuk EntityKey, objek yang ada dikembalikan. Jika opsi PreserveChanges atau OverwriteChanges digunakan, objek diperbarui sebelum dikembalikan.

Untuk informasi selengkapnya, baca Resolusi Identitas, Manajemen Status, dan Pelacakan Perubahan.
Mewujudkan objek Sedang3 Sekali untuk setiap objek yang dikembalikan kueri. 4 Proses membaca objek DbDataReader yang dikembalikan dan membuat objek serta mengatur nilai properti yang didasarkan pada nilai di setiap instans kelas DbDataRecord. Jika objek sudah ada di ObjectContext dan kueri menggunakan opsi penggabungan AppendOnly atau PreserveChanges, tahap ini tidak memengaruhi performa. Untuk informasi selengkapnya, baca Resolusi Identitas, Manajemen Status, dan Pelacakan Perubahan.

1 Ketika penyedia sumber data menerapkan pengumpulan koneksi, biaya pembukaan koneksi didistribusikan di seluruh kumpulan. Penyedia .NET untuk SQL Server mendukung pengumpulan koneksi.

2 Biaya akan meningkat seiring dengan peningkatan kompleksitas kueri.

3 Total biaya meningkat sebanding dengan jumlah objek yang dikembalikan oleh kueri.

4 Overhead ini tidak diperlukan untuk kueri EntityClient karena kueri EntityClient mengembalikan EntityDataReader alih-alih objek. Untuk informasi selengkapnya, lihat Penyedia EntityClient untuk Entity Framework.

Pertimbangan Tambahan

Berikut ini adalah pertimbangan lain yang bisa mempengaruhi performa aplikasi Entity Framework.

Eksekusi Kueri

Karena kueri kadang boros sumber daya, pertimbangkan pada titik mana dan pada komputer apa kueri dalam kode Anda dijalankan.

Ditangguhkan versus dieksekusi segera

Ketika Anda membuat ObjectQuery<T> atau kueri LINQ, kueri mungkin tidak segera dijalankan. Eksekusi kueri ditangguhkan hingga hasil diperlukan, seperti selama enumerasi foreach (C#) atau For Each (Visual Basic) atau saat ditetapkan untuk mengisi koleksi List<T>. Eksekusi kueri segera dimulai saat Anda memanggil metode Execute pada ObjectQuery<T> atau saat Anda memanggil metode LINQ yang mengembalikan kueri database tunggal, seperti First atau Any. Untuk informasi selengkapnya, baca Kueri Objek dan Eksekusi Kueri (LINQ ke Entitas).

Eksekusi kueri LINQ sisi klien

Meskipun eksekusi kueri LINQ terjadi pada komputer yang menghosting sumber data, beberapa bagian kueri LINQ bisa dievaluasi pada komputer klien. Untuk informasi selengkapnya, bacalah bagian Eksekusi Penyimpanan dari Eksekusi Kueri (LINQ ke Entitas).

Kompleksitas Kueri dan Pemetaan

Kompleksitas kueri individual dan pemetaan pada model entitas akan memiliki efek signifikan pada performa kueri.

Kompleksitas pemetaan

Model yang lebih kompleks daripada pemetaan satu-ke-satu yang sederhana antara entitas dalam model konseptual dan tabel dalam model penyimpanan menghasilkan perintah yang lebih kompleks daripada model yang memiliki pemetaan satu-ke-satu.

Kompleksitas kueri

Kueri yang membutuhkan sejumlah besar gabungan dalam perintah yang dijalankan terhadap sumber data atau yang mengembalikan sejumlah besar data dapat memengaruhi performa dengan cara berikut:

  • Kueri terhadap model konseptual yang tampak sederhana bisa mengakibatkan eksekusi kueri yang lebih kompleks terhadap sumber data. Hal ini bisa terjadi karena Entity Framework menerjemahkan kueri terhadap model konseptual menjadi kueri yang setara terhadap sumber data. Saat entitas tunggal yang diatur pada model konseptual memetakan ke lebih dari satu tabel di sumber data, atau saat hubungan antara entitas dipetakan ke tabel gabungan, perintah kueri yang dijalankan terhadap kueri sumber data mungkin memerlukan satu atau beberapa gabungan.

    Catatan

    Gunakan metode ToTraceString dari kelas ObjectQuery<T> atau EntityCommand untuk melihat perintah yang dijalankan terhadap sumber data untuk kueri tertentu. Untuk informasi selengkapnya, baca Cara: Menampilkan Perintah Penyimpanan.

  • Kueri SQL Entitas Berlapis dapat membuat gabungan di server dan dapat mengembalikan sejumlah besar baris.

    Berikut ini adalah contoh kueri yang disarangkan dalam klausul proyeksi:

    SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c  ) As Inner2
        FROM AdventureWorksModel.JobCandidate AS c  ) As Inner1
        FROM AdventureWorksModel.EmployeeDepartmentHistory AS c  
    

    Selain itu, kueri tersebut menyebabkan alur kueri menghasilkan satu kueri dengan duplikasi objek di seluruh kueri berlapis. Oleh karena itu, satu kolom dapat diduplikasi beberapa kali. Di beberapa database, termasuk SQL Server, ini dapat menyebabkan tabel TempDB tumbuh sangat besar, yang dapat menurunkan performa server. Anda harus berhati-hati saat menjalankan kueri berlapis.

  • Setiap kueri yang mengembalikan sejumlah besar data bisa menyebabkan penurunan performa jika klien melakukan operasi yang melahap sumber daya dengan cara yang sebanding dengan ukuran set hasil. Di kondisi seperti itu, Anda harus mempertimbangkan untuk membatasi jumlah data yang dikembalikan oleh kueri. Untuk informasi selengkapnya, baca Cara: Memeriksa Cepat Hasil Kueri.

Setiap perintah yang secara otomatis dihasilkan oleh Entity Framework bisa lebih kompleks daripada perintah serupa yang ditulis secara eksplisit oleh pengembang database. Jika Anda membutuhkan kontrol eksplisit atas perintah yang dijalankan terhadap sumber data Anda, pertimbangkan untuk menentukan pemetaan ke fungsi bernilai tabel atau prosedur tersimpan.

Hubungan

Untuk performa kueri yang optimal, Anda harus menentukan hubungan antara entitas baik sebagai asosiasi pada model entitas maupun sebagai hubungan logis di sumber data.

Jalur Kueri

Secara default, saat Anda menjalankan ObjectQuery<T>, objek terkait tidak dikembalikan (meskipun objek yang mewakili hubungan itu sendiri). Anda bisa memuat objek terkait dengan salah satu dari tiga cara:

  1. Atur jalur kueri sebelum ObjectQuery<T> dijalankan.

  2. Panggil metode Load pada properti navigasi yang diekspos objek.

  3. Atur opsi LazyLoadingEnabled pada ObjectContext ke true. Ingatlah bahwa ini dilakukan secara otomatis saat Anda membuat kode lapisan objek dengan Perancang Model Data Entitas. Untuk informasi selengkapnya, baca Gambaran Umum Kode yang Dihasilkan.

Saat Anda mempertimbangkan opsi mana yang akan digunakan, ketahuilah bahwa ada tradeoff antara jumlah permintaan terhadap database dan jumlah data yang dikembalikan dalam satu kueri. Untuk informasi selengkapnya, baca Memuat Objek Terkait.

Menggunakan jalur kueri

Jalur kueri akan menentukan grafik objek yang dikembalikan kueri. Ketika Anda menentukan jalur kueri, hanya satu permintaan terhadap database yang diperlukan untuk mengembalikan semua objek yang ditentukan jalur. Menggunakan jalur kueri bisa mengakibatkan perintah kompleks dijalankan terhadap sumber data dari kueri objek yang tampaknya sederhana. Ini terjadi karena satu atau beberapa gabungan dibutuhkan untuk mengembalikan objek terkait dalam satu kueri. Kompleksitas ini lebih besar pada kueri terhadap model entitas yang kompleks, seperti entitas dengan warisan atau jalur yang mencakup banyak hubungan.

Catatan

Gunakan metode ToTraceString untuk melihat perintah yang akan dihasilkan oleh ObjectQuery<T>. Untuk informasi selengkapnya, baca Cara: Menampilkan Perintah Penyimpanan.

Saat jalur kueri menyertakan terlalu banyak objek terkait atau objek berisi terlalu banyak data baris, sumber data mungkin tidak dapat menyelesaikan kueri. Ini terjadi jika kueri membutuhkan penyimpanan sementara perantara yang melebihi kemampuan sumber data. Saat ini terjadi, Anda dapat mengurangi kompleksitas kueri sumber data dengan memuat objek terkait secara eksplisit.

Anda bisa secara eksplisit memuat objek terkait dengan memanggil metode Load pada properti navigasi yang mengembalikan EntityCollection<TEntity> atau EntityReference<TEntity>. Memuat objek secara eksplisit membutuhkan perjalanan pulang-pergi ke database setiap kali Load dipanggil.

Catatan

jika Anda memanggil Load saat mengulangi kumpulan objek yang dikembalikan, seperti saat Anda menggunakan pernyataan foreach (For Each dalam Visual Basic), penyedia khusus sumber data harus mendukung beberapa kumpulan hasil aktif pada satu koneksi. Untuk database SQL Server, Anda harus menentukan nilai MultipleActiveResultSets = true di dalam string koneksi penyedia.

Anda juga bisa menggunakan metode LoadProperty ketika tidak ada properti EntityCollection<TEntity> atau EntityReference<TEntity> pada entitas. Ini berguna ketika Anda menggunakan entitas POCO.

Meskipun secara eksplisit memuat objek terkait akan mengurangi jumlah gabungan dan mengurangi jumlah data redundan, Load membutuhkan koneksi berulang ke database, yang dapat menjadi mahal ketika secara eksplisit memuat sejumlah besar objek.

Menyimpan Perubahan

Saat Anda memanggil metode SaveChanges pada ObjectContext, perintah buat, perbarui, atau hapus terpisah dihasilkan untuk setiap objek yang ditambahkan, diperbarui, atau dihapus dalam konteks. Perintah ini dijalankan pada sumber data di dalam satu transaksi. Seperti halnya kueri, performa operasi membuat, memperbarui, dan hapus tergantung pada kompleksitas pemetaan pada model konseptual.

Transaksi Terdistribusi

Operasi pada transaksi eksplisit yang memerlukan sumber daya yang dikelola oleh koordinator transaksi terdistribusi (DTC) akan jauh lebih mahal daripada operasi serupa yang tidak memerlukan DTC. Promosi ke DTC akan terjadi pada situasi berikut:

  • Transaksi eksplisit dengan operasi terhadap database SQL Server tahun 2000 atau sumber data lain yang selalu mempromosikan transaksi eksplisit ke DTC.

  • Transaksi eksplisit dengan operasi terhadap SQL Server tahun 2005 saat koneksi dikelola oleh Entity Framework. Hal ini terjadi karena SQL Server tahun 2005 mempromosikan ke DTC setiap kali koneksi ditutup dan dibuka kembali di dalam satu transaksi, yang merupakan perilaku default dari Entity Framework. Promosi DTC ini tidak terjadi jika menggunakan SQL Server tahun 2008. Untuk menghindari promosi ini ketika menggunakan SQL Server tahun 2005, Anda harus secara eksplisit membuka dan menutup koneksi dalam transaksi. Untuk informasi selengkapnya, kunjungi Mengelola Koneksi dan Transaksi.

Transaksi eksplisit digunakan saat satu atau beberapa operasi dijalankan di dalam transaksi System.Transactions. Untuk informasi selengkapnya, kunjungi Mengelola Koneksi dan Transaksi.

Strategi untuk Meningkatkan Performa

Anda bisa meningkatkan performa keseluruhan kueri dalam Kerangka Entitas dengan menggunakan strategi berikut.

Membuat tampilan

Membuat tampilan berdasarkan model entitas adalah biaya yang signifikan saat pertama kali aplikasi mengeksekusi kueri. Gunakan utilitas EdmGen.exe untuk membuat tampilan sebagai file kode Visual Basic atau C# yang dapat ditambahkan ke proyek selama proses desain. Anda juga bisa menggunakan Text Template Transformation Toolkit untuk menghasilkan tampilan yang telah dikompilasi sebelumnya. Tampilan yang dibuat sebelumnya divalidasi pada waktu yang dijalankan untuk memastikan bahwa tampilan tersebut konsisten dengan versi model entitas yang ditentukan saat ini. Untuk informasi selengkapnya, lihat Petunjuk: Membuat Tampilan untuk Meningkatkan Performa Kueri.

Ketika bekerja dengan model yang sangat besar, pertimbangan berikut ini berlaku:

Format metadata .NET membatasi jumlah karakter string pengguna pada biner tertentu menjadi 16.777.215 (0xFFFFFF). Jika Anda menghasilkan tampilan untuk model yang sangat besar dan file tampilan mencapai batas ukuran ini, Anda akan mendapatkan kesalahan kompilasi "Tidak ada ruang logis yang tersisa untuk membuat lebih banyak string pengguna." Batasan ukuran ini berlaku untuk seluruh binari terkelola. Untuk informasi selengkapnya, baca blog yang menunjukkan cara menghindari kesalahan saat bekerja dengan model besar dan kompleks.

Pertimbangkan untuk menggunakan opsi gabungan NoTracking untuk kueri

Ada biaya yang diperlukan untuk melacak objek yang dikembalikan pada konteks objek. Mendeteksi perubahan pada objek dan memastikan bahwa beberapa permintaan untuk entitas logis yang sama mengembalikan instans objek yang sama mengharuskan objek dilampirkan ke instans ObjectContext. Jika Anda tidak berencana untuk membuat pembaruan atau penghapusan ke objek dan tidak membutuhkan manajemen identitas, pertimbangkan untuk menggunakan opsi penggabungan NoTracking saat Anda menjalankan kueri.

Mengembalikan jumlah data yang tepat

Dalam beberapa skenario, menentukan jalur kueri menggunakan metode Include ini jauh lebih cepat karena memerlukan lebih sedikit perjalanan pulang pergi ke database. Namun, pada skenario lain, perjalanan pulang pergi tambahan ke database untuk memuat objek terkait mungkin lebih cepat karena kueri yang lebih sederhana dengan lebih sedikit gabungan menghasilkan redundansi data yang lebih sedikit. Karena itu, kami sarankan Anda menguji performa dengan berbagai cara untuk mengambil objek terkait. Untuk informasi selengkapnya, baca Memuat Objek Terkait.

Untuk menghindari mengembalikan terlalu banyak data di dalam satu kueri, pertimbangkan untuk membuat hasil kueri menjadi grup yang lebih mudah dikelola. Untuk informasi selengkapnya, baca Cara: Memeriksa Cepat Hasil Kueri.

Membatasi cakupan ObjectContext

Dalam kebanyakan kasus, Anda harus membuat instans ObjectContext dalam pernyataan using (Using…End Using dalam Visual Basic). Hal ini bisa meningkatkan performa dengan memastikan bahwa sumber daya yang terkait dengan konteks objek dibuang secara otomatis ketika kode keluar dari blok pernyataan. Namun, saat kontrol terikat ke objek yang dikelola oleh konteks objek, instans ObjectContext harus dipertahankan selama pengikatan diperlukan dan dibuang secara manual. Untuk informasi selengkapnya, kunjungi Mengelola Koneksi dan Transaksi.

Pertimbangkan untuk membuka koneksi database secara manual

Saat aplikasi Anda menjalankan serangkaian kueri objek atau sering memanggil SaveChanges untuk tetap membuat, memperbarui, dan menghapus operasi ke sumber data, Entity Framework harus terus membuka dan menutup koneksi ke sumber data. Pada situasi ini, pertimbangkan untuk membuka koneksi secara manual di awal operasi ini dan menutup atau membuang koneksi ketika operasi selesai. Untuk informasi selengkapnya, kunjungi Mengelola Koneksi dan Transaksi.

Data Performa

Beberapa data performa untuk Entity Framework diterbitkan dalam postingan berikut di blog tim ADO.NET:

Lihat juga