Photo by Markus Winkler on Unsplash

.Net 7 ve Yenilikleri (EF.Core-ExecuteDelete-ExecuteUpdate)

Furkan Güngör
Devops Türkiye☁️ 🐧 🐳 ☸️
4 min readDec 20, 2022

--

.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.

Support for ExecuteUpdate/Delete which spans multiple tables (TPT, TPC…) · Issue #28520 · dotnet/efcore (github.com)

TPH mapping kullanırken herhangi bir sorun olmayacaktır. Çünkü bu mapping türünde zaten yine bir tablomuz var. Ancak TPT ve TPCmapping türünde birden fazla tabloda işlem yapılması gerektiği için işlem başarısız olacaktır.

--

--

Furkan Güngör
Devops Türkiye☁️ 🐧 🐳 ☸️

Solution Developer — I want to change the world, give me the source code.