İLERİ
Shadow Properties
C# entity class'ında tanımlanmayan ama veritabanında var olan sütunlar. Entity'yi "kirletmeden" audit alanları (CreatedAt, ModifiedBy), FK'lar veya altyapısal sütunlar eklemek için kullanılır.
Veritabanı sağlayıcısı
Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.
// Shadow property tanımlama
builder.Property<DateTime>("CreatedAt");
builder.Property<string>("CreatedBy").HasMaxLength(100);
// Shadow property'ye erişim
var createdAt = context.Entry(entity).Property<DateTime>("CreatedAt").CurrentValue;
// Shadow FK (navigation property olmaksızın)
builder.HasOne<User>()
.WithMany()
.HasForeignKey("UserId");
// ☝️ Entity class'ında UserId property'si yok — EF bunu shadow olarak yönetir.
// DB'de FK sütunu oluşur, JOIN/Include çalışır — ama C# class temiz kalır.
// SaveChanges override ile otomatik set
public override Task<int> SaveChangesAsync(CancellationToken ct = default)
{
foreach (var entry in ChangeTracker.Entries<BaseEntity>())
{
if (entry.State == EntityState.Added)
entry.Property("CreatedAt").CurrentValue = DateTime.UtcNow;
if (entry.State is EntityState.Added or EntityState.Modified)
entry.Property("UpdatedAt").CurrentValue = DateTime.UtcNow;
}
return base.SaveChangesAsync(ct);
}
SQL'de shadow property'ler normal sütun olarak görünür:
CREATE TABLE [Products] (
[Id] INT IDENTITY(1,1) NOT NULL,
[Name] NVARCHAR(200) NOT NULL,
[CreatedAt] DATETIME2 NOT NULL, -- Shadow property → DB'de normal sütun
[CreatedBy] NVARCHAR(100) NULL, -- Entity class'ında yok ama DB'de var!
[UpdatedAt] DATETIME2 NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ([Id])
);
CREATE TABLE products (
id INTEGER GENERATED ALWAYS AS IDENTITY,
name VARCHAR(200) NOT NULL,
created_at TIMESTAMPTZ NOT NULL, -- Shadow property → DB'de normal sütun
created_by VARCHAR(100) NULL, -- Entity class'ında yok ama DB'de var!
updated_at TIMESTAMPTZ NULL,
CONSTRAINT pk_products PRIMARY KEY (id)
);
Örnek veri:
| Id | Name | CreatedAt | CreatedBy | UpdatedAt |
|---|---|---|---|---|
| 1 | Laptop | 2025-01-15 08:30:00 | admin@firma.com | 2025-02-01 10:00:00 |
| 2 | iPhone | 2025-01-16 10:00:00 | ahmet@firma.com | NULL |
Productclass'ındaCreatedAt,CreatedByyok — ama DB'de var ve EF otomatik dolduruyor!
Shadow Property ile Sorgulama
// EF.Property<T>() ile LINQ'da shadow property kullanılabilir
var recentProducts = await context.Products
.OrderByDescending(p => EF.Property<DateTime>(p, "CreatedAt"))
.Take(10)
.ToListAsync();
// Filtreleme
var todayAdded = await context.Products
.Where(p => EF.Property<DateTime>(p, "CreatedAt").Date == DateTime.Today)
.ToListAsync();
Oluşan SQL:
SELECT TOP(10) [p].[Id], [p].[Name], [p].[Price]
FROM [Products] AS [p]
ORDER BY [p].[CreatedAt] DESC;
SELECT p.id, p.name, p.price
FROM products AS p
ORDER BY p.created_at DESC
LIMIT 10;
Shadow Property'ye Index Ekleme
// Shadow property üzerine index — sorgulama performansı için
builder.HasIndex("CreatedAt");
builder.HasIndex("TenantId", "CreatedAt"); // Composite
Ne Zaman Shadow Property, Ne Zaman Normal Property?
| Kriter | Shadow Property | Normal Property |
|---|---|---|
| Domain logic'te kullanılıyor mu? | Hayır → Shadow | Evet → Normal |
| API response'ta dönülecek mi? | Hayır → Shadow | Evet → Normal |
| Sadece altyapısal (audit, FK) | Shadow | — |
| Entity class'ı temiz kalsın mı? | Shadow | — |