REFERANS
Custom Conventions — EF Core 7+
Modeldeki tüm entity'lere otomatik uygulanan kurallar. "Her string en fazla 256 karakter olsun", "Her tabloda CreatedAt shadow property olsun", "Her FK'ya index koy" gibi DRY kurallar bir kere yazılır, tüm entity'lere uygulanır — her config'te tekrar yazmaya gerek kalmaz.
IModelFinalizingConvention — Tüm String'lere MaxLength
public class MaxStringLengthConvention : IModelFinalizingConvention
{
private readonly int _maxLength;
public MaxStringLengthConvention(int maxLength = 256)
{
_maxLength = maxLength;
}
public void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
IConventionContext<IConventionModelBuilder> context)
{
foreach (var entity in modelBuilder.Metadata.GetEntityTypes())
{
foreach (var property in entity.GetProperties()
.Where(p => p.ClrType == typeof(string)))
{
// Sadece açıkça MaxLength belirtilmemiş olanları ayarla
if (!property.GetMaxLength().HasValue)
{
property.Builder.HasMaxLength(_maxLength);
}
}
}
}
}
Convention ile Otomatik Soft Delete Filter
public class SoftDeleteConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
IConventionContext<IConventionModelBuilder> context)
{
foreach (var entity in modelBuilder.Metadata.GetEntityTypes())
{
var isDeletedProperty = entity.FindProperty("IsDeleted");
if (isDeletedProperty is not null && isDeletedProperty.ClrType == typeof(bool))
{
var parameter = Expression.Parameter(entity.ClrType, "e");
var prop = Expression.Property(parameter, "IsDeleted");
var condition = Expression.Equal(prop, Expression.Constant(false));
var lambda = Expression.Lambda(condition, parameter);
entity.SetQueryFilter(lambda);
}
}
}
}
Convention ile Otomatik Index (FK'lara)
public class ForeignKeyIndexConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
IConventionContext<IConventionModelBuilder> context)
{
foreach (var entity in modelBuilder.Metadata.GetEntityTypes())
{
foreach (var fk in entity.GetForeignKeys())
{
// FK property'lerine index oluştur (yoksa)
var properties = fk.Properties;
var existingIndex = entity.FindIndex(properties);
if (existingIndex is null)
{
entity.AddIndex(properties);
}
}
}
}
}
Kayıt
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Custom conventions
configurationBuilder.Conventions.Add(_ => new MaxStringLengthConvention(256));
configurationBuilder.Conventions.Add(_ => new SoftDeleteConvention());
// Property type conventions (daha basit)
configurationBuilder.Properties<string>().HaveMaxLength(256);
configurationBuilder.Properties<decimal>().HavePrecision(18, 2);
// Tüm DateTime'lar → datetime2
configurationBuilder.Properties<DateTime>().HaveColumnType("datetime2");
}
ConfigureConventions vs OnModelCreating
ConfigureConventions |
OnModelCreating |
|
|---|---|---|
| Çalışma sırası | Önce | Sonra (override eder) |
| Amaç | Genel kurallar | Entity-specific config |
| Öncelik | Düşük — explicit config kazanır | Yüksek — son söz |
| Kullanım | DRY global kurallar | Tek entity'e özel detay |
ConfigureConventions'da belirlenen kural,IEntityTypeConfiguration'da override edilebilir. Yani önce genel kural yazılır, istisnalar entity config'de belirtilir.