C # Entity Framework: Toplu Uzantılar Giriş Belleği Sorunu

Aug 17 2020

Şu anda EF Uzantıları kullanıyorum. Anlamadığım bir şey, "performansa yardımcı olması gerekiyordu"

ancak List değişkenine bir milyondan fazla kayıt yerleştirmek bir Bellek Sorunudur. Öyleyse, her şeyi hafızada tutmadan milyon kaydı güncellemek istiyorsanız, bu nasıl verimli bir şekilde yapılabilir?

A kullanmalı mıyız for loopve gruplar halinde güncelleme 10.000 mi yazmalı? EFExtensions BulkUpdate'in bunu destekleyecek herhangi bir yerel işlevi var mı?

Misal:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics');  // this creates IQueryable

await productUpdate.ForEachAsync(c => c.ProductBrand = 'ABC Company');

_dbContext.BulkUpdateAsync(productUpdate.ToList());

Kaynak:

https://entityframework-extensions.net/bulk-update

Yanıtlar

Pac0 Aug 18 2020 at 00:27

Sorguya benzer bir koşulla toplu güncelleme yapmanın "uygun" EF Uzantıları yolunu buldum:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics')
    .UpdateFromQuery( x => new Product { ProductBrand = "ABC Company" });

Bu , belgelereUPDATE ... SET ... WHERE göre , önce varlıkları yüklemeye gerek kalmadan uygun bir SQL ile sonuçlanmalıdır :

Neden UpdateFromQuerydaha hızlı olduğunu SaveChanges, BulkSaveChangesve BulkUpdate?

UpdateFromQuerygibi doğrudan SQL'de bir ifade yürütür UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key].

Diğer işlemler normalde bir veya daha fazla veritabanı gidiş-dönüşü gerektirir ve bu da performansı yavaşlatır.

Bu dotnet keman örneğinde , örneklerinden uyarlanmış çalışma sözdizimini kontrol edebilirsiniz BulkUpdate.

Diğer hususlar

  • Maalesef bunun için toplu işlemlerden söz edilmiyor.

  • Bunun gibi büyük bir güncelleme yapmadan önce, bu sütunda sahip olabileceğiniz dizinleri devre dışı bırakmayı ve daha sonra bunları yeniden oluşturmayı düşünmeye değer olabilir. Bu, özellikle çoğuna sahipseniz yararlıdır.

  • İçinde durumu hakkında Dikkatli Whereo EF tarafından SQL gibi tercüme edilemez eğer, o zaman "olağan" korkunç gidiş dönüş anlamına tamam istemci tarafında olacak "- bellekte değişikliği - Yük güncelleme"

2 Flater Aug 17 2020 at 23:43

Bu aslında EF'in yapılmadığı bir şey. EF'in veritabanı etkileşimleri kayıt nesnesinden başlar ve buradan akar. Varlık değiştirilmediyse (ve dolayısıyla yüklenmediyse), EF kısmi bir GÜNCELLEME (yani her şeyin üzerine yazamaz) oluşturamaz ve benzer şekilde, bir anahtar yerine bir koşula dayalı olarak kayıtları SİLEMEZ.

Koşullu güncelleme / silme mantığı için EF eşdeğeri (bu kayıtların tümü yüklenmeden) yoktur.

UPDATE People
SET FirstName = 'Bob'
WHERE FirstName = 'Robert'

veya

DELETE FROM People
WHERE FirstName = 'Robert'

Bunu EF yaklaşımını kullanarak yapmak, tüm bu varlıkları yalnızca veritabanına geri göndermek için (bir güncelleme veya silme ile) yüklemenizi gerektirecektir ve bu, zaten bulduğunuz gibi bant genişliği ve performans kaybıdır.

Burada bulduğum en iyi çözüm, EF'in LINQ dostu yöntemlerini atlamak ve bunun yerine ham SQL'i kendiniz çalıştırmaktır. Bu yine de bir EF bağlamı kullanılarak yapılabilir.

using (var ctx = new MyContext())
{
    string updateCommand = "UPDATE People SET FirstName = 'Bob' WHERE FirstName = 'Robert'";
    int noOfRowsUpdated = ctx.Database.ExecuteSqlCommand(updateCommand);

    string deleteCommand = "DELETE FROM People WHERE FirstName = 'Robert'";
    int noOfRowsDeleted = ctx.Database.ExecuteSqlCommand(deleteCommand);
}

Daha fazla bilgi burada . Elbette , ilgili yerlerde SQL enjeksiyonuna karşı korumayı unutmayın .

Ham SQL çalıştırmak için belirli sözdizimi, EF / EF Core sürümüne göre değişebilir, ancak bildiğim kadarıyla tüm sürümler, ham SQL yürütmenize izin verir.


Özellikle EF Extensions veya BulkUpdate'in performansı hakkında yorum yapamam ve bunları onlardan satın almayacağım.

Belgelerine dayanarak, koşullu güncelleme / silme mantığına izin verecek doğru imzalara sahip yöntemlere sahip görünmüyorlar.

  • BulkUpdate bunu optimize etmenize izin verecek mantıksal koşulu (UPDATE komutunuzdaki WHERE) girmenize izin vermiyor gibi görünüyor.
  • BulkDeleteyine BatchSizede kayıtları birer birer işlediklerini (sanırım her parti için) ve koşullu tek bir DELETE sorgusu (WHERE cümlesi) kullanmadıklarını gösteren bir ayara sahiptir .

Sorudaki amaçladığınız koda göre, EF Extensions size gerçekten ihtiyacınız olanı vermiyor. EF'nin varlıklarını yükleme ihtiyacını atladığından, veritabanında basitçe ham SQL çalıştırmak daha performanslı ve daha ucuzdur.

Güncelleme
düzeltilmiş olabilir , burada görüldüğü gibi koşullu güncelleme mantığı için bazı destek vardır . Bununla birlikte, örnek hala hafızadaki her şeyi yüklerken ve bu koşullu WHERE mantığının amacının, hepsini zaten belleğe yüklediyseniz (neden bellek içi LINQ kullanmıyorsunuz?)

Ancak, bu varlıklar yüklenmeden çalışsa bile, yine de:

  • daha sınırlı (geçerli SQL olan herhangi bir boole koşuluna izin veren SQL ile karşılaştırıldığında yalnızca eşitlik kontrollerine izin verilir),
  • nispeten karmaşık (sözdizimlerini beğenmiyorum, belki bu özneldir)
  • ve daha maliyetli (hala ücretli bir kitaplık)

kendi ham SQL sorgunuzu döndürmeye kıyasla. Yine de burada kendi ham SQL'inizi almanızı öneririm, ama bu sadece benim fikrim.