İleri Seviye Entity Framework Core: Migrations, Performans ve Raw SQL
# Ileri Seviye Entity Framework Core: Migration, Performans ve Raw SQL
Entity Framework Core temel senaryolarda gayet iyi is gorur, ancak uretim ortaminda calisan sistemler cok daha fazlasini gerektirir. Tablolariniz cogalmaya, istek sayiniz saniyede birkacu gecmeye basladiginda ileri duzey migration stratejileri, bilingli performans ayarlari ve LINQ'in yetersiz kaldigi yerlerde raw SQL kullanma becerisi sart olur. Yuksek hacimli veri sistemlerinde edindigim deneyimlere gore, naif bir EF Core yapilandirmasi ile duzgun ayarlanmis bir yapilandirma arasindaki fark, yuk altinda 50ms ile 500ms yanit suresi arasindaki fark kadar buyuk olabiliyor.
Ileri Duzey Migration Stratejileri
Migration'lar birer deployment artifaktidir. Uygulama kodunuza gosterdiginiz ozeni migration'lara da gostermelisiniz.
Migration Icinde Veri Tohumlama (Data Seeding)
EF Core'un `HasData` metodu referans veriler icin kullanislidir, ancak sinirlamalari vardir -- sadece sabit degerlerle calisir ve her migration'da tum veriyi yeniden olusturur. Dinamik tohumlama icin migration icinde raw SQL kullanin:
public partial class SeedOrderStatuses : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: class="code-string">"OrderStatuses",
columns: new[] { class="code-string">"Id", class="code-string">"Name", class="code-string">"Description" },
values: new object[,]
{
{ class="code-number">1, class="code-string">"Pending", class="code-string">"Siparis olusturuldu" },
{ class="code-number">2, class="code-string">"Processing", class="code-string">"Siparis hazirlaniyor" },
{ class="code-number">3, class="code-string">"Shipped", class="code-string">"Siparis kargoya verildi" },
{ class="code-number">4, class="code-string">"Delivered", class="code-string">"Siparis teslim edildi" },
{ class="code-number">5, class="code-string">"Cancelled", class="code-string">"Siparis iptal edildi" }
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: class="code-string">"OrderStatuses",
keyColumn: class="code-string">"Id",
keyValues: new object[] { class="code-number">1, class="code-number">2, class="code-number">3, class="code-number">4, class="code-number">5 });
}
}Buyuk tohumlama veri setleri veya karmasik donusumler icin veri tohumlamayi sema degisikliklerinden ayri bir migration'da tutun. Bu, geri alma islemlerini ongorilebilir kilar.
Idempotent Migration'lar
Uretim ortamina yapilan deployment'lar her zaman idempotent script'ler kullanmalidir. Deployment yarisinda basarisiz olursa, script'i tekrar calistirmak guvenli olmalidir:
dotnet ef migrations script --idempotent -o migrate.sqlOlusturulan script, her migration'i `__EFMigrationsHistory` tablosuna karsi bir varlik kontrolune sarar. Ben her zaman bu script'leri staging veya production'a uygulamadan once olusturup inceliyorum -- uretim veritabanina dogrudan `dotnet ef database update` calistirmayin.
Uretim Ortaminda Geri Alma Kaliplari
Her migration'in calisan bir `Down` metodu olmali. Ancak bunun otesinde, yuksek riskli sema degisiklikleri icin belirli bir kalip uyguluyorum:
public partial class RenameCustomerEmailColumn : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
class=class="code-string">"code-comment">// Adim class="code-number">1: Yeni sutunu ekle
migrationBuilder.AddColumn<string>(
name: class="code-string">"EmailAddress",
table: class="code-string">"Customers",
type: class="code-string">"nvarchar(class="code-number">256)",
nullable: true);
class=class="code-string">"code-comment">// Adim class="code-number">2: Veriyi kopyala
migrationBuilder.Sql(
class="code-string">"UPDATE Customers SET EmailAddress = Email");
class=class="code-string">"code-comment">// Adim class="code-number">3: Veri kopyalandiktan sonra non-nullable yap
migrationBuilder.AlterColumn<string>(
name: class="code-string">"EmailAddress",
table: class="code-string">"Customers",
type: class="code-string">"nvarchar(class="code-number">256)",
nullable: false,
defaultValue: class="code-string">"");
class=class="code-string">"code-comment">// Adim class="code-number">4: Eski sutunu ancak dogrulama sonrasinda kaldir
class=class="code-string">"code-comment">// Bu, deployment dogrulamasindan sonra uygulanan AYRI bir migration'da olur
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: class="code-string">"EmailAddress",
table: class="code-string">"Customers");
}
}Bir sutunu asla tek bir migration'da yeniden adlandirmayin. Genislet-daralt (expand-contract) kalibini kullanin: yenisini ekle, kopyala, dogrula, eskisini kaldir. Bu yaklasim veri kaybini onler ve sifir kesinti sureli deployment'lari mumkun kilar.
Performans Derinlemesine
Derlenenis Sorgular (Compiled Queries)
Dakikada binlerce kez calistirilan sorgularda, ifade agaci derleme yuku birikir. Derlenenis sorgular bu maliyeti bir kez oder:
public class ProductRepository
{
private static readonly Func<AppDbContext, int, CancellationToken, Task<Product?>>
GetByIdQuery = EF.CompileAsyncQuery(
(AppDbContext ctx, int id, CancellationToken ct) =>
ctx.Products.FirstOrDefault(p => p.Id == id));
private static readonly Func<AppDbContext, string, CancellationToken, Task<List<Product>>>
GetByCategoryQuery = EF.CompileAsyncQuery(
(AppDbContext ctx, string category, CancellationToken ct) =>
ctx.Products
.Where(p => p.Category == category && p.IsActive)
.OrderBy(p => p.Name)
.ToList());
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context) => _context = context;
public Task<Product?> GetByIdAsync(int id, CancellationToken ct)
=> GetByIdQuery(_context, id, ct);
public Task<List<Product>> GetByCategoryAsync(string category, CancellationToken ct)
=> GetByCategoryQuery(_context, category, ct);
}Yuksek hacimli veri sistemlerinde profilleme yaptigimda, en cok vurulan 10 endpoint'teki derlenenis sorgular ortalama sorgu yukunu 3-4ms azaltti. Tek basina kucuk gorunebilir, ancak dakikada 10.000 istek hizinda anlamli CPU tasarrufu saglar.
Bolunmus Sorgular (Split Queries)
Bir sorgu birden fazla koleksiyon navigasyonu icerdiginde, EF Core JOIN'li tek bir SQL sorgusu olusturur ve bu kartezyen patlamaya yol acabilir. 10 ogeli ve 5 etiketi olan bir ust kayit, 15 yerine 50 satir uretir:
class=class="code-string">"code-comment">// Tek sorgu: kartezyen patlama riski
var orders = await _context.Orders
.Include(o => o.OrderItems)
.Include(o => o.Tags)
.Include(o => o.AuditEntries)
.ToListAsync(ct);
class=class="code-string">"code-comment">// Bolunmus sorgu: birden fazla roundtrip ama veri tekrari yok
var orders = await _context.Orders
.Include(o => o.OrderItems)
.Include(o => o.Tags)
.Include(o => o.AuditEntries)
.AsSplitQuery()
.ToListAsync(ct);Bolunmus sorgular tek bir buyuk sonuc setini birden fazla kucuk sete donusturur. Aktarilan toplam bayt genellikle onemli olcude duser. Birden fazla koleksiyon navigasyonu dahil ettigimde varsayilan olarak bolunmus sorgu kullaniyorum.
ExecuteUpdate ve ExecuteDelete ile Toplu Islemler
EF Core 7+ ile gelen set tabanli islemler, entity yuklenmeden dogrudan SQL'e cevirilir:
class=class="code-string">"code-comment">// class="code-number">2 yildan eski tum siparisleri arsivle -- tek UPDATE ifadesi
await _context.Orders
.Where(o => o.CreatedAt < DateTime.UtcNow.AddYears(-class="code-number">2))
.ExecuteUpdateAsync(s => s
.SetProperty(o => o.IsArchived, true)
.SetProperty(o => o.ArchivedAt, DateTime.UtcNow), ct);
class=class="code-string">"code-comment">// Arsivlenmis denetim kayitlarini temizle -- tek DELETE ifadesi
await _context.AuditLogs
.Where(a => a.IsArchived && a.CreatedAt < DateTime.UtcNow.AddYears(-class="code-number">5))
.ExecuteDeleteAsync(ct);
class=class="code-string">"code-comment">// Bir kategorideki urunlere toplu fiyat artisi
await _context.Products
.Where(p => p.Category == class="code-string">"Elektronik" && p.IsActive)
.ExecuteUpdateAsync(s => s
.SetProperty(p => p.Price, p => p.Price * class="code-number">1.1m)
.SetProperty(p => p.LastModified, DateTime.UtcNow), ct);Bu islemler degisiklik izleyiciyi tamamen atlar. Hicbir entity bellege yuklenmez. On binlerce satir uzerindeki toplu islemler icin, entity'leri yukleyip `SaveChanges` cagirmaktan kat be kat hizlidir.
Raw SQL ve SqlQuery
LINQ sorularin %90'i icin mukemmeldir. Kalan %10 icin raw SQL bir odun degil, dogru aractir.
FromSqlRaw ile Entity Sorgulari
var topProducts = await _context.Products
.FromSqlRaw(@class="code-string">"
SELECT p.*
FROM Products p
INNER JOIN OrderItems oi ON p.Id = oi.ProductId
GROUP BY p.Id, p.Name, p.Price, p.Category, p.IsActive,
p.CreatedAt, p.LastModified, p.IsDeleted
HAVING COUNT(oi.Id) > {class="code-number">0}
ORDER BY COUNT(oi.Id) DESC", minimumOrders)
.AsNoTracking()
.ToListAsync(ct);`FromSqlRaw` varsayilan olarak izlenen entity'ler dondurur. Ustune LINQ operatorleri zincirleyebilirsiniz -- EF Core SQL'inizi bir alt sorgu olarak sarar.
Rastgele Sonuc Setleri Icin SqlQuery
EF Core 8 ile gelen `SqlQuery
var salesReport = await _context.Database
.SqlQuery<MonthlySalesDto>($@class="code-string">"
SELECT
DATEPART(YEAR, o.CreatedAt) AS Year,
DATEPART(MONTH, o.CreatedAt) AS Month,
COUNT(*) AS OrderCount,
SUM(o.TotalAmount) AS TotalRevenue,
AVG(o.TotalAmount) AS AverageOrderValue
FROM Orders o
WHERE o.CreatedAt >= {startDate}
AND o.IsDeleted = class="code-number">0
GROUP BY DATEPART(YEAR, o.CreatedAt), DATEPART(MONTH, o.CreatedAt)
ORDER BY Year DESC, Month DESC")
.ToListAsync(ct);Bu, raporlama sorgulari, karmasik toplamalar ve entity modeline eslemenin zoraki veya israf olacagi tum senaryolar icin cok degerlidir.
DDL ve Non-Query Islemler Icin ExecuteSqlRaw
class=class="code-string">"code-comment">// EF Core migrationclass="code-string">'larinin ifade edemeyecegi filtreli bir indeks olustur
await _context.Database.ExecuteSqlRawAsync(@class="code-string">"
CREATE INDEX IX_Orders_ActivePending
ON Orders (CustomerId, CreatedAt)
WHERE IsDeleted = class="code-number">0 AND Status = 'Pending'", ct);Interceptor'lar ve Sorgu Filtreleri
Global Sorgu Filtreleri
Sorgu filtreleri, belirli bir entity'ye yapilan her sorguya otomatik olarak uygulanir. Cok kiracilik (multi-tenancy) ve soft delete'in temelidir:
public class AppDbContext : DbContext
{
private readonly ITenantProvider _tenantProvider;
public AppDbContext(
DbContextOptions<AppDbContext> options,
ITenantProvider tenantProvider)
: base(options)
{
_tenantProvider = tenantProvider;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
class=class="code-string">"code-comment">// Soft delete filtresi
modelBuilder.Entity<Order>()
.HasQueryFilter(o => !o.IsDeleted);
class=class="code-string">"code-comment">// Cok kiracili filtre
modelBuilder.Entity<Order>()
.HasQueryFilter(o => !o.IsDeleted
&& o.TenantId == _tenantProvider.CurrentTenantId);
}
}SaveChanges Interceptor'lari
Interceptor'lar, domain mantiginizi kirletmeden kalicilik hattina baglanmanizi saglar:
public class AuditInterceptor : SaveChangesInterceptor
{
private readonly ICurrentUserService _currentUser;
public AuditInterceptor(ICurrentUserService currentUser)
=> _currentUser = currentUser;
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken ct = default)
{
var context = eventData.Context!;
foreach (var entry in context.ChangeTracker
.Entries<IAuditable>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedAt = DateTime.UtcNow;
entry.Entity.CreatedBy = _currentUser.UserId;
break;
case EntityState.Modified:
entry.Entity.ModifiedAt = DateTime.UtcNow;
entry.Entity.ModifiedBy = _currentUser.UserId;
break;
}
}
return base.SavingChangesAsync(eventData, result, ct);
}
}
class=class="code-string">"code-comment">// Kayit
builder.Services.AddDbContext<AppDbContext>((sp, options) =>
options.UseSqlServer(connectionString)
.AddInterceptors(sp.GetRequiredService<AuditInterceptor>()));Bu kalip, denetim endiselerini is mantgigindan tamamen ayirir. `IAuditable` arayuzunu uygulayan her entity otomatik olarak zaman damgalari ve kullanici takibi alir.
Deger Donusturuculer ve Sahipli Tipler
Deger Donusturuculer (Value Converters)
Deger donusturuculer, domain modeliniz ile veritabani arasinda veriyi donusturur. Enum'lari, guclu tipli ID'leri ve karmasik deger tiplerini depolamak icin vazgecilmezdir:
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
class=class="code-string">"code-comment">// Okunabilirlik icin enum'u string olarak depola
builder.Property(o => o.Status)
.HasConversion<string>()
.HasMaxLength(class="code-number">30);
class=class="code-string">"code-comment">// Guclu tipli ID icin ozel donusturucu
builder.Property(o => o.Id)
.HasConversion(
id => id.Value,
value => new OrderId(value));
class=class="code-string">"code-comment">// Metadata icin JSON donusturucu
builder.Property(o => o.Metadata)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
v => JsonSerializer.Deserialize<Dictionary<string, string>>(
v, (JsonSerializerOptions?)null)!)
.HasColumnType(class="code-string">"nvarchar(max)");
}
}Deger Nesneleri Icin Sahipli Tipler (Owned Types)
Sahipli tipler, zengin domain nesnelerini ayri tablolar olusturmadan ust tablodaki sutunlara esler:
builder.OwnsOne(o => o.ShippingAddress, address =>
{
address.Property(a => a.Street).HasMaxLength(class="code-number">200).HasColumnName(class="code-string">"Ship_Street");
address.Property(a => a.City).HasMaxLength(class="code-number">100).HasColumnName(class="code-string">"Ship_City");
address.Property(a => a.PostalCode).HasMaxLength(class="code-number">20).HasColumnName(class="code-string">"Ship_PostalCode");
address.Property(a => a.Country).HasMaxLength(class="code-number">60).HasColumnName(class="code-string">"Ship_Country");
});
builder.OwnsOne(o => o.BillingAddress, address =>
{
address.Property(a => a.Street).HasMaxLength(class="code-number">200).HasColumnName(class="code-string">"Bill_Street");
address.Property(a => a.City).HasMaxLength(class="code-number">100).HasColumnName(class="code-string">"Bill_City");
address.Property(a => a.PostalCode).HasMaxLength(class="code-number">20).HasColumnName(class="code-string">"Bill_PostalCode");
address.Property(a => a.Country).HasMaxLength(class="code-number">60).HasColumnName(class="code-string">"Bill_Country");
});Bu yaklasim, gereksiz JOIN'lerden kacinirken domain modelinizi temiz tutar. `Address` deger nesnesi `Orders` tablosunun icinde sutunlar olarak yasiar.
Temporal Tablolar ve Soft Delete
SQL Server Temporal Tablolar
EF Core 6+ ile SQL Server temporal tablolari icin yerlesik destek geldi. Bu tablolar her satirin tam degisiklik gecmisini otomatik olarak takip eder:
builder.Entity<Product>(b =>
{
b.ToTable(class="code-string">"Products", tb => tb.IsTemporal(ttb =>
{
ttb.HasPeriodStart(class="code-string">"ValidFrom");
ttb.HasPeriodEnd(class="code-string">"ValidTo");
ttb.UseHistoryTable(class="code-string">"ProductsHistory");
}));
});Gecmis verileri sorgulama:
class=class="code-string">"code-comment">// Belirli bir andaki urun durumunu al
var productSnapshot = await _context.Products
.TemporalAsOf(new DateTime(class="code-number">2025, class="code-number">1, class="code-number">15, class="code-number">0, class="code-number">0, class="code-number">0, DateTimeKind.Utc))
.Where(p => p.Id == productId)
.SingleOrDefaultAsync(ct);
class=class="code-string">"code-comment">// Degisikliklerin tam gecmisini al
var priceHistory = await _context.Products
.TemporalAll()
.Where(p => p.Id == productId)
.OrderBy(p => EF.Property<DateTime>(p, class="code-string">"ValidFrom"))
.Select(p => new
{
p.Price,
ValidFrom = EF.Property<DateTime>(p, class="code-string">"ValidFrom"),
ValidTo = EF.Property<DateTime>(p, class="code-string">"ValidTo")
})
.ToListAsync(ct);Saglam Soft Delete Kalibi
Sorgu filtreleri ve interceptor'larin bir arada kullanimi kusursuz bir deneyim olusturur:
public interface ISoftDeletable
{
bool IsDeleted { get; set; }
DateTime? DeletedAt { get; set; }
string? DeletedBy { get; set; }
}
public class SoftDeleteInterceptor : SaveChangesInterceptor
{
private readonly ICurrentUserService _currentUser;
public SoftDeleteInterceptor(ICurrentUserService currentUser)
=> _currentUser = currentUser;
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken ct = default)
{
foreach (var entry in eventData.Context!.ChangeTracker
.Entries<ISoftDeletable>()
.Where(e => e.State == EntityState.Deleted))
{
entry.State = EntityState.Modified;
entry.Entity.IsDeleted = true;
entry.Entity.DeletedAt = DateTime.UtcNow;
entry.Entity.DeletedBy = _currentUser.UserId;
}
return base.SavingChangesAsync(eventData, result, ct);
}
}Interceptor, fiziksel silmeleri seffaf bir sekilde mantiksal silmelere donusturur. Sorgu filtresiyle birlestirildiginde, silinen kayitlar normal sorgularda gorunmez olur ancak kurtarilabilir kalir.
Esza Manlilik Yonetimi (Concurrency Handling)
Iyimser esza manlilik (optimistic concurrency), cok kullanicili senaryolarda sessiz veri ezilmelerini onler:
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public decimal Price { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; } = default!;
}Veya Fluent API ile:
builder.Property(p => p.RowVersion)
.IsRowVersion()
.IsConcurrencyToken();Cakismayi yonetme:
public async Task UpdatePriceAsync(int productId, decimal newPrice, CancellationToken ct)
{
var product = await _context.Products.FindAsync(new object[] { productId }, ct)
?? throw new NotFoundException(productId);
product.Price = newPrice;
try
{
await _context.SaveChangesAsync(ct);
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var databaseValues = await entry.GetDatabaseValuesAsync(ct);
if (databaseValues is null)
throw new ConflictException(class="code-string">"Urun baska bir kullanici tarafindan silindi.");
var dbProduct = (Product)databaseValues.ToObject();
throw new ConflictException(
$class="code-string">"Urun baska bir kullanici tarafindan degistirildi. " +
$class="code-string">"Veritabanindaki guncel fiyat: {dbProduct.Price}. " +
$class="code-string">"Sizin gonderdiginiz fiyat: {newPrice}.");
}
}Yuksek hacimli veri sistemlerinde, birden fazla kullanici veya arka plan isleminin ayni anda degistirebildigi entity'lere her zaman esza manlilik tokenllari ekliyorum. Alternatif olan "son yazan kazanir" yaklasimi, sonradan hata ayiklamasi neredeyse imkansiz olan sessiz veri bozulmasina yol acar.
Sik Yapilan Ileri Duzey EF Core Hatalari
1. Yabanci Anahtar Indekslerini Unutmak
EF Core, bildigi iliskiler icin yabanci anahtarlarda otomatik olarak indeks olusturur. Ancak yabanci anahtar sutunlarini elle ekler veya shadow property kullanirsiniz, indeksi kendiniz olusturmaniz gerekir:
class=class="code-string">"code-comment">// Elle tanimlanan FK'da eksik indeks
builder.Property(o => o.AssignedAgentId);
builder.HasIndex(o => o.AssignedAgentId); class=class="code-string">"code-comment">// Bunu unutmayin2. DbContext'i Singleton Olarak Kullanmak
`DbContext` thread-safe degildir. Singleton olarak kaydetmek veya statik bir alanda tutmak, yeniden uretmesi son derece zor olan veri bozulmasi ve aralikli cokmelere neden olur:
class=class="code-string">"code-comment">// YANLIS: DbContext thread-safe degildir
services.AddSingleton<AppDbContext>();
class=class="code-string">"code-comment">// DOGRU: Scoped yasam suresi, istek basina bir tane
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));3. Sorgu Filtresi Etkilesimlerini Goz Ardi Etmek
Birden fazla sorgu filtreniz oldugunda, bunlar AND mantiigiyla birlesir. Bir filtrenin varliigini unutursaniz, sorgular sessizce eksik sonuc dondurur:
class=class="code-string">"code-comment">// Her iki filtre de uygulanir -- bir kaydin neden "yok" gorundugunun
class=class="code-string">"code-comment">// hatasini ayiklarken kiraci filtresini unutmak kolaydir
modelBuilder.Entity<Order>().HasQueryFilter(
o => !o.IsDeleted && o.TenantId == _tenantProvider.CurrentTenantId);
class=class="code-string">"code-comment">// Hata ayiklamak icin: tum filtreleri gecici olarak atlayin
var allOrders = await _context.Orders
.IgnoreQueryFilters()
.Where(o => o.Id == missingOrderId)
.SingleOrDefaultAsync(ct);4. Arka Plan Servislerinde DbContext'i Duzgun Elden Cikarmamak
Barindirilan servisler veya arka plan islerinde, scope'lari elle olusturup elden cikarmaniz gerekir:
public class OrderCleanupService : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
public OrderCleanupService(IServiceScopeFactory scopeFactory)
=> _scopeFactory = scopeFactory;
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider
.GetRequiredService<AppDbContext>();
await context.Orders
.Where(o => o.IsArchived && o.ArchivedAt < DateTime.UtcNow.AddYears(-class="code-number">3))
.ExecuteDeleteAsync(ct);
await Task.Delay(TimeSpan.FromHours(class="code-number">1), ct);
}
}
}5. Izlenen ve Izlenmeyen Entity'leri Karistirmak
Izlenen bir entity ile ayni anahtara sahip izlenmeyen bir entity'yi eklemeye calismak hata firlatir. Bu genellikle bir cache veya mesaj kuyrugundan deserialize edilen bir entity'yi guncellemeye calisirken olur:
class=class="code-string">"code-comment">// Entity zaten izleniyorsa hata firlatir
_context.Products.Update(deserializedProduct);
class=class="code-string">"code-comment">// Guvenli yaklasim: once kontrol et, sonra ozelliklerini guncelle
var tracked = await _context.Products.FindAsync(deserializedProduct.Id);
if (tracked is not null)
{
_context.Entry(tracked).CurrentValues.SetValues(deserializedProduct);
}Sonuc
Ileri duzey EF Core kullanimi, LINQ ve degisiklik izleyicinin ne zaman size iyi hizmet ettigini ve ne zaman disina cikmaniz gerektigini bilmektir. Derlenenis sorgular, bolunmus sorgular ve toplu islemler performans tavanini yukselitir. Raw SQL ve interceptor'lar, hicbir ORM'nin kusursuz soyutlayamadigi kenar durumlari karsilar. Temporal tablolar ve esza manlilik tokenlari, uygulama kodunun tek basina saglayamayacagi sekilde veri butunlugunu korur.
Bu makaledeki kalipler, gunluk milyonlarca satir islenen uretim sistemlerinden geliyor. Teorik degiller -- her biri gercek yuk altinda gercek bir sorunu cozdu.
EF Core veri katmaninizin performans, dogruluk ve migration guvenligi acisindan denetlenmesine yardimci olabilirim.
İlgili Makaleler
Entity Framework Core: Veritabanı İşlemlerinin Modern Yolu
Entity Framework Core ile veritabanı işlemlerini yönetin. Code-first, migrations ve performans optimizasyonu.
.NET'te Clean Architecture: Ölçeklenebilir Proje Yapısı
.NET projelerinde Clean Architecture uygulayın. Katmanlar, bağımlılık yönetimi ve test edilebilir kod için rehber.
.NET Performance Optimizasyonu: Profiling ve Best Practices
.NET uygulamalarının performansını optimize edin. Profiling araçları, memory management ve async patterns.
Flutter Projeniz mi Var?
iOS, Android ve web için yüksek performanslı Flutter uygulamaları geliştiriyorum.
İletişime Geç