← Back to list

DI : Conditional Registration ve Open Generic Registration

Conditional Registration (Config’e Göre Kayıt)

Murat Baseren · 2025-12-23 03:32 · 2 claps · 2.7 min read
#dependency-injection #conditional #generics
Open on Medium ↗

DI : Conditional Registration ve Open Generic Registration

Conditional Registration (Config’e Göre Kayıt)

Dependency Injection(DI) ile servis kayıtlarımızı yaparken, belli koşullara göre farklı servis kayıtları yapmamız gerekebilir.

// appsettings.json
{
  "Features": {
    "UseRedisCache": true,
    "UseSqlServerLogging": false
  }
}

// Program.cs
var useRedis = builder.Configuration.GetValue<bool>("Features:UseRedisCache");

if (useRedis)
{
    services.AddStackExchangeRedisCache(options => 
    {
        options.Configuration = "localhost:6379";
    });
    services.AddSingleton<ICacheService, RedisCacheService>();
}
else
{
    services.AddMemoryCache();
    services.AddSingleton<ICacheService, MemoryCacheService>();
}

// Kullanım - Controller'da fark etmez
public class ProductController
{
    private readonly ICacheService _cache; // Redis veya Memory olabilir

    public ProductController(ICacheService cache)
    {
        _cache = cache;
    }
}

Open Generic Registration (Generic Interface Kaydetme)

Generic tipleri kullanarak servis kayıtlarında entity ler için generic servislerin hepsinin tek satırda kayıt edilmesini sağlayabilirsiniz. (Bu baya hoşunuza gidecek) 😅

// Generic interface
public interface IRepository<T> where T : class
{
    Task<T> GetById(int id);
    Task Save(T entity);
}

// Generic implementation
public class Repository<T> : IRepository<T> where T : class
{
    private readonly AppDbContext _db;

    public Repository(AppDbContext db) => _db = db;

    public async Task<T> GetById(int id) => await _db.Set<T>().FindAsync(id);
    public async Task Save(T entity) => await _db.Set<T>().AddAsync(entity);
}

// Program.cs - Tek satırda tüm entity'ler için!
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// Kullanım
public class ProductService
{
    private readonly IRepository<Product> _productRepo;
    private readonly IRepository<Category> _categoryRepo;

    // Otomatik olarak Repository<Product> ve Repository<Category> gelir!
    public ProductService(
        IRepository<Product> productRepo, 
        IRepository<Category> categoryRepo)
    {
        _productRepo = productRepo;
        _categoryRepo = categoryRepo;
    }
}

Aslında burada otomatik olarak kendisi tek tek entity class ları derleme koduna yazarak kodu derliyor gibi geliyor ama işin arkaplanı öyle değil.

Aslında tüm entity’leri bilmiyor, lazy registration yapıyor!

Nasıl Çalışıyor?

services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// Bu satır şunu söylüyor:
// "Biri IRepository<X> isterse, ona Repository<X> ver"
// X ne olursa olsun!

Runtime’da çözülüyor.

public class ProductService
{
    // Constructor'da IRepository<Product> istedi
    public ProductService(IRepository<Product> productRepo)
    {
        // DI şunu yapıyor:
        // 1. "IRepository<Product> isteniyor"
        // 2. "Kayıtlara bak: IRepository<> → Repository<> var"
        // 3. "Product'ı yerine koy: Repository<Product> oluştur"
        // 4. Inject et!
    }
}

public class CategoryService
{
    // Farklı entity istedi
    public CategoryService(IRepository<Category> categoryRepo)
    {
        // DI yine aynı kayıttan Repository<Category> oluşturur
    }
}

Yani önceden tüm entity’ler için kayıt yapmıyor, talep gelince oluşturuyor!

Hariç Tutma Nasıl Yapılır?

Yöntem 1: Özel Entity İçin Override

// Genel kayıt
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// Product için özel implementation
services.AddScoped<IRepository<Product>, ProductRepositoryWithCache>();

// DI önce spesifik olanı arar:
// IRepository<Product> istendi → ProductRepositoryWithCache gelir
// IRepository<Category> istendi → Repository<Category> gelir

Yöntem 2: Interface ile Sınırlama

// Sadece IEntity implement eden classlar için
public interface IEntity
{
    int Id { get; set; }
}

public interface IRepository<T> where T : class, IEntity
{
    Task<T> GetById(int id);
}

public class Repository<T> : IRepository<T> where T : class, IEntity
{
    // Sadece IEntity olan classlar kullanabilir
}

// Kullanım
public class Product : IEntity { } // ✅ Çalışır
public class RandomClass { } // ❌ Çalışmaz

services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

Yöntem 3: Manuel Filtreleme (Advanced)

public static class RepositoryExtensions
{
    public static IServiceCollection AddRepositories(this IServiceCollection services)
    {
        var entityTypes = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(t => t.IsClass 
                && !t.IsAbstract 
                && t.Namespace == "MyApp.Entities" // Sadece Entities namespace'i
                && t.Name != "AuditLog") // AuditLog hariç
            .ToList();

        foreach (var entityType in entityTypes)
        {
            var repositoryInterface = typeof(IRepository<>).MakeGenericType(entityType);
            var repositoryImplementation = typeof(Repository<>).MakeGenericType(entityType);

            services.AddScoped(repositoryInterface, repositoryImplementation);
        }

        return services;
    }
}

// Program.cs
services.AddRepositories();

Yöntem 4: Attribute ile İşaretleme

[AttributeUsage(AttributeTargets.Class)]
public class RepositoryEnabledAttribute : Attribute { }

[RepositoryEnabled] // Bu entity için repository oluştur
public class Product { }

public class TempData { } // Bu entity için repository oluşturma

// Registration
public static IServiceCollection AddRepositories(this IServiceCollection services)
{
    var entityTypes = Assembly.GetExecutingAssembly()
        .GetTypes()
        .Where(t => t.GetCustomAttribute<RepositoryEnabledAttribute>() != null)
        .ToList();

    foreach (var entityType in entityTypes)
    {
        var repoInterface = typeof(IRepository<>).MakeGenericType(entityType);
        var repoImpl = typeof(Repository<>).MakeGenericType(entityType);
        services.AddScoped(repoInterface, repoImpl);
    }

    return services;
}

Dikkat: Generic Constraint’ler Önemli! ⚠️

// ❌ YANLIŞ - Herhangi bir class için repository
public interface IRepository<T> where T : class { }

public class MyService
{
    // Bu da çalışır ama mantıksız!
    public MyService(IRepository<string> repo) { } 
}

// ✅ DOĞRU - Sadece entity'ler için
public interface IRepository<T> where T : class, IEntity { }

public class MyService
{
    public MyService(IRepository<string> repo) { } // ❌ Compile error!
    public MyService(IRepository<Product> repo) { } // ✅ OK
}

Özetle;

  • Tüm entity’leri önceden kaydediyor mu? → ❌ Hayır, lazy (talep gelince)
  • Hangi entity’lere uygulanır? → Generic constraint’e uyan her class
  • Hariç tutma nasıl? → Override, interface constraint, attribute
  • Performans sorunu var mı? → ❌ Hayır, ilk kullanımda resolve edilir.

Görüşmek üzere.. 👋


메타데이터
post_id
d7bc9fa881cc
slug
dependency-injection-conditional-registration-ve-open-generic-registration-d7bc9fa881cc
url
https://medium.com/@muratbaseren/dependency-injection-conditional-registration-ve-open-generic-registration-d7bc9fa881cc
canonical_url
https://medium.com/@muratbaseren/dependency-injection-conditional-registration-ve-open-generic-registration-d7bc9fa881cc
author_url
https://medium.com/@muratbaseren
status
ok
fetched_at
2026-07-09 04:10:03