Saya mencari cara tercepat untuk memasukkan ke dalam Kerangka Entitas.
Saya bertanya ini karena skenario di mana Anda memiliki TransactionScope aktif dan penyisipan sangat besar (4000+). Ini berpotensi bertahan lebih dari 10 menit (batas waktu transaksi default), dan ini akan menyebabkan transaksi tidak lengkap.
c#
sql
entity-framework
Bongo Sharp
sumber
sumber
Jawaban:
Untuk komentar Anda di komentar untuk pertanyaan Anda:
Itu hal terburuk yang bisa Anda lakukan! Memanggil
SaveChanges()
untuk setiap record memperlambat insert massal sangat turun Saya akan melakukan beberapa tes sederhana yang kemungkinan besar akan meningkatkan kinerja:SaveChanges()
sekali setelah SEMUA catatan.SaveChanges()
setelah misalnya 100 catatan.SaveChanges()
setelah misalnya 100 catatan dan buang konteks dan buat yang baru.Untuk sisipan massal, saya bekerja dan bereksperimen dengan pola seperti ini:
Saya memiliki program pengujian yang memasukkan 560.000 entitas (9 properti skalar, tanpa properti navigasi) ke dalam DB. Dengan kode ini berfungsi kurang dari 3 menit.
Untuk kinerja, penting untuk memanggil
SaveChanges()
setelah "banyak" catatan ("banyak" sekitar 100 atau 1000). Ini juga meningkatkan kinerja untuk membuang konteks setelah SaveChanges dan membuat yang baru. Ini membersihkan konteks dari semua entites,SaveChanges
tidak melakukan itu, entitas masih melekat pada konteks di negaraUnchanged
. Ini adalah ukuran pertumbuhan entitas terlampir dalam konteks yang memperlambat penyisipan langkah demi langkah. Jadi, sangat membantu untuk menghapusnya setelah beberapa waktu.Berikut adalah beberapa pengukuran untuk entitas 560000 saya:
Perilaku dalam tes pertama di atas adalah bahwa kinerjanya sangat non-linear dan menurun sangat dari waktu ke waktu. ("Banyak jam" adalah perkiraan, saya tidak pernah menyelesaikan tes ini, saya berhenti di 50.000 entitas setelah 20 menit.) Perilaku non-linear ini tidak begitu signifikan dalam semua tes lainnya.
sumber
AutoDetectChangesEnabled = false;
pada DbContext. Ini juga memiliki efek kinerja tambahan yang besar: stackoverflow.com/questions/5943394/...DbContext
, TIDAKObjectContext
?Kombinasi ini meningkatkan kecepatan dengan cukup baik.
sumber
Cara tercepat akan menggunakan ekstensi insert massal , yang saya kembangkan
Catatan: ini adalah produk komersial, tidak gratis
Ia menggunakan SqlBulkCopy dan datareader khusus untuk mendapatkan kinerja maksimal. Akibatnya lebih dari 20 kali lebih cepat daripada menggunakan insert biasa atau AddRange
penggunaannya sangat sederhana
sumber
Anda harus melihat menggunakan
System.Data.SqlClient.SqlBulkCopy
ini. Berikut dokumentasinya , dan tentu saja ada banyak tutorial online.Maaf, saya tahu Anda sedang mencari jawaban sederhana untuk membuat EF melakukan apa yang Anda inginkan, tetapi operasi massal tidak benar-benar dimaksudkan untuk ORM.
sumber
Saya setuju dengan Adam Rackis.
SqlBulkCopy
adalah cara tercepat untuk mentransfer catatan massal dari satu sumber data ke yang lain. Saya menggunakan ini untuk menyalin catatan 20K dan butuh waktu kurang dari 3 detik. Lihat contoh di bawah ini.sumber
AsDataReader()
metode ekstensi, dijelaskan dalam jawaban ini: stackoverflow.com/a/36817205/1507899Saya akan merekomendasikan artikel ini tentang cara melakukan sisipan massal menggunakan EF.
Kerangka Entitas dan MASUK massal lambat
Dia mengeksplorasi bidang-bidang ini dan membandingkan kinerja:
sumber
karena tidak pernah disebutkan di sini saya ingin recomment EFCore.BulkExtensions di sini
sumber
Saya telah menyelidiki jawaban Slauma (yang luar biasa, terima kasih untuk idenya), dan saya telah mengurangi ukuran bets sampai saya mencapai kecepatan optimal. Melihat hasil Slauma:
Terlihat bahwa ada peningkatan kecepatan ketika bergerak dari 1 ke 10, dan dari 10 ke 100, tetapi dari 100 hingga 1000 kecepatan memasukkan jatuh lagi.
Jadi saya fokus pada apa yang terjadi ketika Anda mengurangi ukuran bets menjadi nilai di antara 10 dan 100, dan inilah hasil saya (saya menggunakan konten baris yang berbeda, jadi waktu saya nilainya berbeda):
Berdasarkan hasil saya, sebenarnya optimal adalah sekitar nilai 30 untuk ukuran batch. Ini kurang dari 10 dan 100. Masalahnya adalah, saya tidak tahu mengapa 30 optimal, saya juga tidak bisa menemukan penjelasan logis untuk itu.
sumber
Seperti yang orang lain katakan, SqlBulkCopy adalah cara untuk melakukannya jika Anda ingin kinerja insert yang sangat bagus.
Agak sulit untuk diimplementasikan tetapi ada perpustakaan yang dapat membantu Anda. Ada beberapa di luar sana tapi saya akan tanpa malu mencabut perpustakaan saya sendiri saat ini: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities
Satu-satunya kode yang Anda perlukan adalah:
Jadi seberapa cepat itu? Sangat sulit untuk mengatakan karena itu tergantung pada banyak faktor, kinerja komputer, jaringan, ukuran objek dll. Tes kinerja yang saya buat menunjukkan 25k entitas dapat dimasukkan pada sekitar 10-an dengan cara standar di host lokal JIKA Anda mengoptimalkan konfigurasi EF seperti disebutkan dalam jawaban lain. Dengan EFUtilities yang membutuhkan waktu sekitar 300ms. Yang lebih menarik adalah bahwa saya telah menyelamatkan sekitar 3 juta entitas dalam waktu kurang dari 15 detik menggunakan metode ini, rata-rata sekitar 200k entitas per detik.
Satu masalah tentu saja jika Anda perlu memasukkan data yang dirilis. Ini dapat dilakukan secara efisien ke dalam sql server menggunakan metode di atas tetapi mengharuskan Anda untuk memiliki strategi pembuatan Id yang memungkinkan Anda menghasilkan id di kode aplikasi untuk induk sehingga Anda dapat mengatur kunci asing. Ini dapat dilakukan dengan menggunakan GUID atau sesuatu seperti pembuatan HiLo id.
sumber
EFBatchOperation
memiliki konstruktor yang Anda lewatiDbContext
untuk daripada melewati setiap metode statis. Versi generikInsertAll
danUpdateAll
yang secara otomatis menemukan koleksi, mirip denganDbContext.Set<T>
, akan bagus juga.Dispose()
konteks membuat masalah jika entitas yang AndaAdd()
andalkan pada entitas yang dimuat sebelumnya (misalnya properti navigasi) dalam konteksSaya menggunakan konsep serupa untuk menjaga konteks saya kecil untuk mencapai kinerja yang sama
Tetapi alih-alih
Dispose()
konteksnya dan menciptakan kembali, saya cukup melepaskan entitas yang sudahSaveChanges()
bungkus dengan try catch dan
TrasactionScope()
jika perlu, jangan perlihatkan di sini untuk menjaga kode tetap bersihsumber
Saya tahu ini adalah pertanyaan yang sangat lama, tetapi seorang pria di sini mengatakan bahwa mengembangkan metode ekstensi untuk menggunakan penyisipan massal dengan EF, dan ketika saya memeriksa, saya menemukan bahwa biaya perpustakaan $ 599 hari ini (untuk satu pengembang). Mungkin masuk akal untuk seluruh perpustakaan, namun untuk memasukkan sebagian besar ini terlalu banyak.
Berikut adalah metode ekstensi yang sangat sederhana yang saya buat. Saya menggunakannya berpasangan dengan database terlebih dahulu (jangan diuji dengan kode terlebih dahulu, tapi saya pikir itu berfungsi sama). Ubah
YourEntities
dengan nama konteks Anda:Anda dapat menggunakannya untuk koleksi apa pun yang mewarisi
IEnumerable
, seperti itu:sumber
await bulkCopy.WriteToServerAsync(table);
Coba gunakan a Prosedur Tersimpan yang akan mendapatkan XML dari data yang ingin Anda masukkan.
sumber
Saya telah membuat ekstensi generik dari contoh @Slauma di atas;
Pemakaian:
sumber
Ada beberapa perpustakaan pihak ketiga yang mendukung Sisipan Massal yang tersedia:
Lihat: Entity Framework Bulk Insert library
Hati-hati, saat memilih perpustakaan sisipan massal. Hanya Entity Framework Extensions yang mendukung semua jenis asosiasi dan warisan dan hanya itu yang masih didukung.
Penafian : Saya adalah pemilik Entity Framework Extensions
Perpustakaan ini memungkinkan Anda untuk melakukan semua operasi massal yang Anda butuhkan untuk skenario Anda:
Contoh
sumber
Gunakan
SqlBulkCopy
:sumber
Salah satu cara tercepat untuk menyimpan daftar Anda harus menerapkan kode berikut
AutoDetectChangesEnabled = false
Tambah, AddRange & SaveChanges: Tidak mendeteksi perubahan.
ValidateOnSaveEnabled = false;
Tidak mendeteksi pelacak perubahan
Anda harus menambahkan nuget
Sekarang Anda dapat menggunakan kode berikut
sumber
SqlBulkCopy super cepat
Ini implementasi saya:
sumber
[Pembaruan 2019] EF Core 3.1
Mengikuti apa yang telah dikatakan di atas, menonaktifkan AutoDetectChangesEnabled di EF Core bekerja dengan sempurna: waktu penyisipan dibagi dengan 100 (dari beberapa menit hingga beberapa detik, catatan 10k dengan hubungan lintas tabel)
Kode yang diperbarui adalah:
sumber
Berikut ini adalah perbandingan kinerja antara menggunakan Entity Framework dan menggunakan kelas SqlBulkCopy pada contoh realistis: Bagaimana Cara Massal Memasukkan Objek Kompleks ke dalam Database SQL Server
Seperti yang sudah ditekankan oleh orang lain, ORM tidak dimaksudkan untuk digunakan dalam operasi massal. Mereka menawarkan fleksibilitas, pemisahan kekhawatiran dan manfaat lainnya, tetapi operasi massal (kecuali pembacaan massal) bukan salah satunya.
sumber
Pilihan lain adalah menggunakan SqlBulkTools yang tersedia dari Nuget. Ini sangat mudah digunakan dan memiliki beberapa fitur canggih.
Contoh:
Lihat dokumentasi untuk contoh lebih lanjut dan penggunaan lanjutan. Penafian: Saya adalah penulis perpustakaan ini dan pandangan saya adalah pendapat saya sendiri.
sumber
Sesuai pengetahuan saya ada
no BulkInsert
diEntityFramework
untuk meningkatkan kinerja sisipan besar.Dalam skenario ini Anda dapat pergi dengan SqlBulkCopy di
ADO.net
untuk memecahkan masalah Andasumber
WriteToServer
yang membutuhkanDataTable
.Pernahkah Anda mencoba memasukkan melalui pekerja latar belakang atau tugas?
Dalam kasus saya, saya memasukkan register 7760, didistribusikan di 182 tabel berbeda dengan hubungan kunci asing (oleh NavigationProperties).
Tanpa tugas, butuh 2 menit setengah. Dalam Tugas (
Task.Factory.StartNew(...)
), butuh 15 detik.Saya hanya melakukan
SaveChanges()
setelah menambahkan semua entitas ke konteks. (untuk memastikan integritas data)sumber
Semua solusi yang ditulis di sini tidak membantu karena ketika Anda melakukan SaveChanges (), masukkan pernyataan dikirim ke database satu per satu, itulah cara Entity bekerja.
Dan jika perjalanan Anda ke database dan kembali adalah 50 ms misalnya maka waktu yang diperlukan untuk memasukkan adalah jumlah catatan x 50 ms.
Anda harus menggunakan BulkInsert, ini tautannya: https://efbulkinsert.codeplex.com/
Saya mendapat waktu memasukkan berkurang dari 5-6 menit menjadi 10-12 detik dengan menggunakannya.
sumber
Anda dapat menggunakan pustaka paket massal . Versi Insert Massal 1.0.0 digunakan dalam proyek yang memiliki kerangka kerja Entitas> = 6.0.0.
Keterangan lebih lanjut dapat ditemukan di sini- kode sumber Operasi Massal
sumber
[SOLUSI BARU UNTUK POSTGRESQL] Hei, saya tahu ini posting yang cukup lama, tapi saya baru saja mengalami masalah yang sama, tetapi kami menggunakan Postgresql. Saya ingin menggunakan bulkinsert yang efektif, yang ternyata cukup sulit. Saya belum menemukan perpustakaan gratis yang tepat untuk melakukannya pada DB ini. Saya hanya menemukan pembantu ini: https://bytefish.de/blog/postgresql_bulk_insert/ yang juga ada di Nuget. Saya telah menulis mapper kecil, yang secara otomatis memetakan properti dengan cara Entity Framework:
Saya menggunakannya dengan cara berikut (Saya memiliki entitas bernama Undertaking):
Saya menunjukkan contoh dengan transaksi, tetapi juga bisa dilakukan dengan koneksi normal yang diambil dari konteks. undertakingsToAdd adalah enumerable dari catatan entitas normal, yang ingin saya masukkan ke DB.
Solusi ini, yang saya dapatkan setelah beberapa jam meneliti dan mencoba, adalah seperti yang Anda harapkan jauh lebih cepat dan akhirnya mudah digunakan dan gratis! Saya benar-benar menyarankan Anda untuk menggunakan solusi ini, tidak hanya karena alasan yang disebutkan di atas, tetapi juga karena itu satu-satunya yang saya tidak punya masalah dengan Postgresql itu sendiri, banyak solusi lain bekerja dengan sempurna misalnya dengan SqlServer.
sumber
Rahasianya adalah memasukkan ke dalam tabel pementasan kosong yang identik. Sisipan cepat keringanan. Kemudian jalankan satu insert dari itu ke dalam tabel besar utama Anda. Kemudian potong tabel pementasan siap untuk batch berikutnya.
yaitu.
sumber
Tapi, untuk lebih dari (+4000) sisipan saya sarankan untuk menggunakan prosedur tersimpan. terlampir waktu berlalu. Saya memang memasukkannya 11.788 baris dalam 20 "
itu kode itu
sumber
Gunakan prosedur tersimpan yang mengambil input data dalam bentuk xml untuk memasukkan data.
Dari kode c # Anda, masukkan data yang dimasukkan sebagai xml.
misal dalam c #, sintaksnya akan seperti ini:
sumber
Gunakan teknik ini untuk meningkatkan kecepatan memasukkan catatan dalam Entity Framework. Di sini saya menggunakan prosedur tersimpan sederhana untuk memasukkan catatan. Dan untuk menjalankan prosedur tersimpan ini saya menggunakan metode .FromSql () dari Entity Framework yang mengeksekusi Raw SQL.
Kode prosedur yang tersimpan:
Selanjutnya, lewati semua 4000 record Anda dan tambahkan kode Entity Framework yang mengeksekusi yang tersimpan
onces prosedur setiap loop ke-100.
Untuk ini saya membuat kueri string untuk menjalankan prosedur ini, terus menambahkannya setiap set catatan.
Kemudian periksa apakah loop berjalan dalam kelipatan 100 dan dalam hal itu jalankan menggunakan
.FromSql()
.Periksa kode di bawah ini:
sumber