.Net 7 ve Yenilikleri (EF.Core-ExecuteDelete-ExecuteUpdate)
.Net 7 sürümü ile hayatımıza giren farklı özellikler oldu. Özellikle Entity Framework Core tarafında çok sayıda iyileştirme ve yeni özellikler eklendi. Bulk operasyonlarının bu sürümde desteklenmek istendiğini zaten biliyorduk. .Net 6 yayınlandıktan sonra yapılan planlar içerisinde “En Çok İstenen Özellikler” kategorisine dahil edilen bu özellik EFCore 7 sürümü ile artık kullanılabiliyor.
Yukarıda bahsettiğim en çok istenilen özellikler listesini görmek istiyorsanız buraya tıklayabilirsiniz. 🥸
Entity Framework Core bu sürüme kadar zaten bulk operasyonları destekliyordu ancak bu operasyonun nerede yürütüleceği konusunda farklı hikayelerde farklı çözümlere ihtiyaç duyuyorduk.
Entity Framework Core entity
üzerinde oluşan değişiklikleri izler ve SaveChange
metodu çağırıldığında bu değişiklikleri veri tabanına gönderir.
Bu işlem memory’de gerçekleştiği için çok fazla sayıda güncellenecek kayıt oluştuğunda performans konusunda bazı dezavantajlar oluşabiliyordu.
Özellikle Mongo
tarafında bu işlemler tek bir query ile veri tabanında gerçekleştirilebilir. Herhangi bir varlığın hafızaya yüklenmesine gerek yoktur.
Mongo
tarafında olan bu özellik zaten yıllardır hayatımızı kolaylaştırıyor. Ancak relational database tarafında özellikle ORM
olarak Entity Framework Core kullandığımız projelerde bulk operasyonlar için varlıkların hafızaya yüklenmesi gerekiyordu. Şimdi yeni bir özellik var. ExecuteUpdate
and ExecuteDelete
🎉
ExecuteDelete
ve ExecuteUpdate
sadece bulk operasyonlarda geçerli değildir. Bu yapılar tek bir sql query oluşturarak herhangi bir memory load işlemine gerek kalmadan sorguyu veri tabanı seviyesinde çalıştırmanızı sağlar.
En ilkel haliyle ExecuteDelete
aşağıdaki gibidir.
await context.Tags.ExecuteDeleteAsync();
DELETE FROM [t]
FROM [Tags] AS [t]
İlişkili tablolarda ve şartların ön planda olduğu bir senaryo düşünelim.
public class Tag
{
public Tag(string id, string text)
{
Id = id;
Text = text;
}
public string Id { get; private set; }
public string Text { get; set; }
public List<Post> Posts { get; } = new();
}
// ----------------------------------------------------
public class Post
{
public Post(string title, string content, DateTime publishedOn)
{
Title = title;
Content = content;
PublishedOn = publishedOn;
}
public int Id { get; private set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; } = null!;
public List<Tag> Tags { get; } = new();
public Author? Author { get; set; }
}
// ----------------------------------------------------
public class Author
{
public Author(string name)
{
Name = name;
}
public int Id { get; private set; }
public string Name { get; set; }
public List<Post> Posts { get; } = new();
}
3 adet varlığımız var ve birbirleri ile farklı ilişkilere sahip. Böyle senaryolarda işleri biraz daha zorlaştırabiliriz.
await context.Tags.Where(t => t.Posts.All(e => e.PublishedOn.Year < 2022)).ExecuteDeleteAsync();
DELETE FROM [t]
FROM [Tags] AS [t]
WHERE NOT EXISTS (
SELECT 1
FROM [PostTag] AS [p]
INNER JOIN [Posts] AS [p0] ON [p].[PostsId] = [p0].[Id]
WHERE [t].[Id] = [p].[TagsId] AND NOT (DATEPART(year, [p0].[PublishedOn]) < 2022))
ExecuteUpdate
en ilkel haliyle aşağıdaki gibidir.
await context.Tags.ExecuteUpdateAsync(
s => s.SetProperty(b => b.Text, b => b.Text + " *Updated!*"));
UPDATE [b]
SET [b].[Title] = [b].[Title] + N' *Updated!*'
FROM [Tags] AS [b]
Biraz daha karmaşık bir örnek üzerinden incelemek gerekirse; güncelleme için bir şartın gerçekleşmesi gereken bir sorgu oluşturabiliriz.
await context.Tags
.Where(t => t.Posts.All(e => e.PublishedOn.Year < 2022))
.ExecuteUpdateAsync(s => s.SetProperty(t => t.Text, t => t.Text + " (old)"));
UPDATE [t]
SET [t].[Text] = [t].[Text] + N' (old)'
FROM [Tags] AS [t]
WHERE NOT EXISTS (
SELECT 1
FROM [PostTag] AS [p]
INNER JOIN [Posts] AS [p0] ON [p].[PostsId] = [p0].[Id]
WHERE [t].[Id] = [p].[TagsId] AND NOT (DATEPART(year, [p0].[PublishedOn]) < 2022))
Not :
ExecuteUpdate
ve ExecuteDelete
yalnızca tek bir tablo üzerinde işlem yapabilir. Yani işlem sonucunda birden fazla tabloda verileri silmek/güncellemek gerektiği durumlarda işlem yapamamaktadır.
TPH
mapping kullanırken herhangi bir sorun olmayacaktır. Çünkü bu mapping türünde zaten yine bir tablomuz var. Ancak TPT
ve TPC
mapping türünde birden fazla tabloda işlem yapılması gerektiği için işlem başarısız olacaktır.
Kaynaklar
What’s New in EF Core 7.0 | Microsoft Learn
Entity Framework Core 7 (EF7) is available today — .NET Blog (microsoft.com)
.NET Data Community Standup — New EF Core 7.0 APIs (Bulk Update) — YouTube
EntityFramework.Docs/ExecuteUpdateSample.cs at main · dotnet/EntityFramework.Docs (github.com)
EntityFramework.Docs/ExecuteDeleteSample.cs at main · dotnet/EntityFramework.Docs (github.com)