ASP.NET Core Mülakat Soruları: Middleware, DI ve Minimal APIs ile 25 Kritik Soru

ASP.NET Core mülakatlarında çıkan middleware pipeline, dependency injection ve minimal APIs sorularına detaylı cevaplar. Kod örnekleri ve best practice'ler.

ASP.NET Core middleware pipeline, dependency injection ve minimal APIs konseptlerini gösteren teknik diyagram

ASP.NET Core, modern web uygulamaları geliştirmek için Microsoft tarafından sunulan açık kaynaklı ve platformlar arası bir framework'tür. Mülakatlarda, işe alım süreçlerinde ASP.NET Core bilgisi test edilirken özellikle üç temel konu öne çıkar: Middleware Pipeline mimarisi, Dependency Injection (Bağımlılık Enjeksiyonu) ve .NET 6 ile gelen Minimal APIs yaklaşımı. Bu makale, ASP.NET Core teknik mülakatlarında karşılaşılabilecek en kritik 25 soruyu ve detaylı cevaplarını ele alarak, hazırlık sürecini desteklemeyi amaçlamaktadır.

Mülakatçılar Neyi Test Eder?

ASP.NET Core mülakatlarında, adayların yalnızca sözdizimi bilgisi değil, framework'ün temel mimari kararlarını anlama düzeyi de ölçülür. Middleware sırasının neden önemli olduğu, captive dependency anti-pattern'ini nasıl tespit edileceği ve Minimal APIs'in hangi senaryolarda Controllers'a tercih edilmesi gerektiği gibi konular, deneyimli geliştiricileri ayırt eden sorulardır.

ASP.NET Core Middleware Pipeline Soruları

1. Middleware Pipeline nedir ve ASP.NET Core'da nasıl çalışır?

Middleware Pipeline, gelen HTTP isteklerini işleyen ve yanıtları oluşturan bir bileşenler zinciridir. Her middleware bileşeni, isteği bir sonraki middleware'e iletme veya yanıtı kısa devre yaparak döndürme kararını verebilir. Pipeline yapısı, cross-cutting concern'leri (loglama, kimlik doğrulama, hata yönetimi) merkezi ve yeniden kullanılabilir şekilde ele almayı sağlar.

Pipeline'daki her middleware, RequestDelegate türünde bir sonraki bileşene referans tutar ve InvokeAsync veya Invoke metodu aracılığıyla çalıştırılır. İstek pipeline boyunca ilerlerken, her middleware hem istek hem de yanıt üzerinde işlem yapabilir.

2. Middleware sıralaması neden kritik öneme sahiptir?

Middleware sırası, güvenlik, performans ve doğru işlevsellik açısından hayati önem taşır. Yanlış sıralama, kimlik doğrulamadan önce yetkilendirme kontrolü gibi mantık hatalarına veya exception handling middleware'inin hataları yakalayamamasına neden olabilir.

Program.cs — Middleware order matterscsharp
var app = builder.Build();

app.UseExceptionHandler("/error");   // 1. Outermost: catches all exceptions
app.UseHsts();                        // 2. HTTP Strict Transport Security
app.UseHttpsRedirection();            // 3. Redirect HTTP to HTTPS
app.UseStaticFiles();                 // 4. Serve static files (short-circuits if found)
app.UseRouting();                     // 5. Match request to endpoint
app.UseAuthentication();              // 6. Identify the user
app.UseAuthorization();               // 7. Check permissions
app.MapControllers();                 // 8. Execute the matched endpoint

app.Run();

Bu sıralamada UseStaticFiles, routing'den önce gelir çünkü statik dosya istekleri için endpoint eşleştirmesine gerek yoktur ve performans kazancı sağlar. UseAuthentication her zaman UseAuthorization'dan önce gelmelidir, aksi takdirde kullanıcı kimliği belirlenmeden yetkilendirme kontrolü yapılmaya çalışılır.

3. Özel bir middleware nasıl yazılır?

Özel middleware yazarken, convention-based veya factory-based pattern kullanılabilir. Convention-based yaklaşımda, bir sınıf RequestDelegate alan bir constructor ve InvokeAsync metodu içerir.

RequestTimingMiddleware.cscsharp
public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimingMiddleware> _logger;

    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        
        await _next(context);  // Call the next middleware
        
        stopwatch.Stop();
        _logger.LogInformation(
            "Request {Method} {Path} completed in {Elapsed}ms",
            context.Request.Method,
            context.Request.Path,
            stopwatch.ElapsedMilliseconds);
    }
}

// Registration in Program.cs
app.UseMiddleware<RequestTimingMiddleware>();

Bu örnekte middleware, her isteğin ne kadar sürede tamamlandığını ölçer ve loglar. Constructor'da RequestDelegate ve ILogger dependency injection ile alınır, ancak InvokeAsync metodunda da scoped veya transient servisler parametre olarak enjekte edilebilir.

4. Terminal middleware nedir ve ne zaman kullanılır?

Terminal middleware, pipeline'ı sonlandıran ve sonraki middleware'leri çağırmayan bileşendir. app.Run() ile kayıt edilir ve genellikle fallback işlemler için kullanılır.

Terminal middleware, tüm diğer middleware'ler işlendikten sonra çalıştığı için, yakalanmayan isteklere varsayılan bir yanıt döndürmek veya son bir loglama işlemi yapmak için idealdir.

5. app.Use() ve app.Run() arasındaki fark nedir?

app.Use(), sonraki middleware'e çağrı yapabilen inline middleware tanımlamak için kullanılır. Lambda içinde next() delegate'ini çağırmak, pipeline'ın devam etmesini sağlar. app.Run() ise terminal middleware tanımlar ve next parametresi almaz, yani pipeline'ı sonlandırır.

app.Map() ve app.MapWhen() ise belirli URL path veya koşullara göre pipeline dallandırması yapmak için kullanılır.

6. Middleware'de exception handling en iyi nasıl yapılır?

ASP.NET Core'da exception handling için iki yaygın yaklaşım vardır: UseExceptionHandler middleware ve UseDeveloperExceptionPage. Production ortamında UseExceptionHandler kullanılarak kullanıcı dostu hata sayfaları gösterilirken, development ortamında UseDeveloperExceptionPage detaylı stack trace sağlar.

Exception handler middleware, pipeline'ın en dışında yer almalıdır ki tüm downstream middleware'lerden fırlatılan istisnaları yakalayabilsin. Global exception handling, try-catch blokları yazmaya göre daha temiz ve merkezi bir yaklaşım sunar.

Dependency Injection Mülakat Soruları

7. ASP.NET Core'da Dependency Injection'ın üç yaşam döngüsü nedir?

ASP.NET Core'un built-in DI container'ı üç yaşam döngüsü sunar:

  • Transient: Her istek için yeni bir instance oluşturulur. Hafif, stateless servisler için uygundur.
  • Scoped: Her HTTP isteği başına bir instance oluşturulur ve istek boyunca aynı instance paylaşılır. Repository ve DbContext gibi request-bound servisler için idealdir.
  • Singleton: Uygulama başlatıldığında bir kez oluşturulur ve tüm istekler aynı instance'ı kullanır. Configuration, cache ve stateless servisler için uygundur.

Yanlış yaşam döngüsü seçimi, memory leak'lere, thread-safety problemlerine veya captive dependency anti-pattern'ine yol açabilir.

8. Captive dependency nedir ve nasıl tespit edilir?

Captive dependency, kısa yaşam döngülü bir servisi (Scoped veya Transient) daha uzun yaşam döngülü bir servisin (Singleton) içinde kullanma anti-pattern'idir. Bu durum, servisin amaçlanan yaşam döngüsünün ihlal edilmesine neden olur.

Captive Dependency Tespiti

.NET 6 ve sonrasında, Development ortamında ValidateScopes ve ValidateOnBuild ayarları etkinleştirilerek captive dependency'ler uygulama başlangıcında tespit edilebilir. Bu validation, production ortamında performans nedeniyle devre dışı bırakılır.

Program.cs — Captive dependency detectioncsharp
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheService, CacheService>(); // CacheService takes IUserRepository

// This throws at startup in Development when ValidateScopes is enabled:
builder.Host.UseDefaultServiceProvider(options =>
{
    options.ValidateScopes = true;    // Catches captive dependencies
    options.ValidateOnBuild = true;   // Validates all registrations at startup
});

9. Constructor injection yerine method injection ne zaman tercih edilmelidir?

Constructor injection, bağımlılıkların açıkça görünür olması ve immutability sağlaması nedeniyle tercih edilen yaklaşımdır. Ancak bazı senaryolarda method injection (FromServices attribute ile) daha uygundur: optional dependency'ler için, bağımlılık sadece belirli bir action metodunda kullanılacaksa veya constructor'da çok fazla parametre olmasını önlemek için.

Minimal APIs'de method injection, endpoint handler'larında servislerin parametre olarak doğrudan alınmasını sağlar ve bu da kod okunabilirliğini artırır.

10. Keyed services nedir ve nasıl kullanılır?

Keyed services, aynı interface'i implement eden birden fazla servisi DI container'a kaydedip, kullanım anında hangi implementasyonun enjekte edileceğini belirleyebilme özelliğidir.

Program.cs — Registering keyed servicescsharp
builder.Services.AddKeyedSingleton<INotificationService, EmailNotification>("email");
builder.Services.AddKeyedSingleton<INotificationService, SmsNotification>("sms");
builder.Services.AddKeyedSingleton<INotificationService, PushNotification>("push");

// Resolving in a controller or service
public class OrderService
{
    public OrderService(
        [FromKeyedServices("email")] INotificationService emailService,
        [FromKeyedServices("sms")] INotificationService smsService)
    {
        // Each parameter gets its specific implementation
    }
}

Bu yaklaşım, strategy pattern implementasyonlarını daha temiz ve deklaratif hale getirir.

11. TryAdd metotları ne zaman kullanılmalıdır?

TryAddScoped, TryAddSingleton ve TryAddTransient metotları, bir servis zaten kayıtlıysa duplicate kayıt yapmadan geçen extension'lardır. Bu özellik özellikle library yazarları için önemlidir, çünkü kütüphane varsayılan implementasyonlar sağlarken, uygulama geliştiricisinin kendi implementasyonunu register etmesine izin verir.

12. Decorator pattern DI ile nasıl uygulanır?

Decorator pattern, bir servisi wrap ederek ek davranış eklemek için kullanılır. ASP.NET Core DI'da decorator pattern, concrete implementasyonu register edip, ardından interface için factory function ile decorated instance döndürerek uygulanır.

csharp
// Decorating IProductService with caching
builder.Services.AddScoped<ProductService>();
builder.Services.AddScoped<IProductService>(sp =>
{
    var inner = sp.GetRequiredService<ProductService>();
    var cache = sp.GetRequiredService<IMemoryCache>();
    return new CachedProductService(inner, cache);
});

Bu yaklaşım, caching, logging veya retry logic gibi cross-cutting concern'leri servislere eklemek için Open/Closed prensibine uygun bir yöntem sağlar.

.NET mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Minimal APIs Mülakat Soruları

13. Minimal APIs nedir ve Controllers'a göre avantajları nelerdir?

Minimal APIs, .NET 6 ile tanıtılan, HTTP API'leri oluşturmak için hafif ve ceremony-free bir yaklaşımdır. Controller sınıfları, base class inheritance ve attribute-based routing olmadan, doğrudan endpoint tanımlamaları yapılmasını sağlar.

Avantajları arasında daha az boilerplate kod, daha hızlı startup time, lambda-based handler'lar ve functional programming paradigmalarına uygunluk bulunur. Mikroservisler, serverless fonksiyonlar ve basit API'ler için idealdir.

Ancak karmaşık model binding, custom filter pipeline veya controller-level dependency injection gerektiren senaryolarda, geleneksel Controllers yaklaşımı daha uygun olabilir.

14. Minimal APIs'de endpoint'ler nasıl organize edilir?

Minimal APIs'de endpoint'leri organize etmenin en temiz yolu, static extension metotları ve MapGroup kullanarak feature-based klasör yapısı oluşturmaktır.

Features/Products/ProductEndpoints.cscsharp
public static class ProductEndpoints
{
    public static void MapProductEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/products")
            .WithTags("Products")
            .RequireAuthorization();

        group.MapGet("/", GetAll);
        group.MapGet("/{id:int}", GetById);
        group.MapPost("/", Create);
    }

    private static async Task<IResult> GetAll(
        IProductService productService,    // Auto-resolved from DI
        CancellationToken cancellationToken)
    {
        var products = await productService.GetAllAsync(cancellationToken);
        return TypedResults.Ok(products);
    }

    private static async Task<IResult> GetById(
        int id,
        IProductService productService)
    {
        var product = await productService.GetByIdAsync(id);
        return product is null
            ? TypedResults.NotFound()
            : TypedResults.Ok(product);
    }

    private static async Task<IResult> Create(
        CreateProductRequest request,
        IProductService productService)
    {
        var product = await productService.CreateAsync(request);
        return TypedResults.Created($"/api/products/{product.Id}", product);
    }
}

// Program.cs — Clean registration
app.MapProductEndpoints();

Bu yapı, endpoint'leri feature klasörleri içinde tutarak cohesion'ı artırır ve büyük projelerde navigasyonu kolaylaştırır.

15. TypedResults ve Results arasındaki fark nedir?

Results sınıfı, IResult döndüren factory metotlar sağlar ancak return type generic değildir. TypedResults ise .NET 7 ile geldi ve generic return type'lar döndürerek compile-time type safety sağlar.

TypedResults kullanımı, OpenAPI/Swagger dokümantasyonunun otomatik olarak doğru response type'ları içermesini sağlar ve tooling desteği sunar. Yeni projelerde TypedResults tercih edilmelidir.

16. Minimal APIs'de validation nasıl yapılır?

Minimal APIs'de built-in validation attribute'ları (DataAnnotations) otomatik olarak çalışmaz. Validation için üç yaygın yaklaşım vardır: FluentValidation kütüphanesi kullanarak endpoint filter'ları oluşturmak, manual validation logic handler içinde yazmak veya ASP.NET Core'un built-in validation paketlerini kullanmak.

Endpoint filter'lar, validation logic'i handler'lardan ayırarak reusability sağlar ve middleware benzeri bir pipeline oluşturur.

17. Minimal APIs'de route constraints nasıl uygulanır?

Route constraints, endpoint path'lerinde parametre tiplerini ve formatlarını kısıtlamak için kullanılır. Minimal APIs, Controllers ile aynı constraint syntax'ını destekler: {id:int} integer parametreler, {guid:guid} GUID formatı, {date:datetime} tarih formatı gibi.

Constraint'ler yalnızca routing'i etkiler, validation değildir. Güvenlik açısından, kullanıcı input'unun yine de handler içinde validate edilmesi gerekir.

18. Minimal APIs'de global exception handling nasıl yapılır?

Minimal APIs, exception handling için aynı UseExceptionHandler middleware'ini kullanır. Ancak .NET 8 ile gelen IExceptionHandler interface'i, daha granular ve testable exception handling sağlar.

Alternatif olarak, custom middleware veya endpoint filter'lar ile try-catch wrapper oluşturulabilir. Ancak middleware yaklaşımı, tüm pipeline'ı kapsadığı için daha kapsamlı koruma sunar.

İleri Seviye Mülakat Soruları

19. ASP.NET Core'da request pipeline'da short-circuiting nedir?

Short-circuiting, bir middleware'in sonraki middleware'leri çağırmadan response döndürmesidir. Bu, performans optimizasyonu sağlar çünkü gereksiz işlemler atlanır.

UseStaticFiles middleware, statik dosya bulduğunda pipeline'ı sonlandırır. UseRouting sonrası, eğer endpoint bulunamazsa, authorization middleware'lerinin çalışmasına gerek yoktur.

20. Endpoint routing ve conventional routing arasındaki fark nedir?

Endpoint routing, .NET Core 3.0 ile gelen ve routing ile endpoint execution'ı ayıran yaklaşımdır. UseRouting middleware, isteği bir endpoint'e eşleştirir ancak execute etmez. UseEndpoints veya terminal middleware endpoint'i çalıştırır.

Bu ayrım, authentication ve authorization middleware'lerinin hangi endpoint'in execute edileceğini bilmesini sağlar.

21. Options pattern nedir ve ne zaman kullanılır?

Options pattern, configuration verilerini strongly-typed POCO sınıflarına bind etme ve DI ile servislere enjekte etme yaklaşımıdır. IOptions<T>, IOptionsSnapshot<T> ve IOptionsMonitor<T> interface'leri farklı senaryolar için kullanılır:

  • IOptions<T>: Singleton, yapılandırma değişikliklerini görmez
  • IOptionsSnapshot<T>: Scoped, her request'te reload edilir
  • IOptionsMonitor<T>: Singleton ancak change notification destekler

22. ASP.NET Core'da background service'ler nasıl implement edilir?

Background service'ler, IHostedService interface'ini veya BackgroundService abstract sınıfını implement ederek oluşturulur. Bunlar, uygulama yaşam döngüsü boyunca çalışan uzun süreli işlemler için kullanılır.

Scoped service'leri kullanmak için, IServiceScopeFactory ile manuel scope oluşturulmalıdır çünkü hosted service'ler Singleton'dır.

23. ASP.NET Core'da health check'ler nasıl implement edilir?

Health check'ler, uygulamanın ve bağımlılıklarının sağlık durumunu izlemek için kullanılır. Custom health check, IHealthCheck interface'ini implement ederek yazılır. HealthCheckResult ile Healthy, Degraded veya Unhealthy durumu döndürülür.

Health check'ler, microservice'lerde orchestration ve load balancing için kritik öneme sahiptir.

24. Rate limiting ASP.NET Core'da nasıl uygulanır?

.NET 7 ile gelen built-in rate limiting middleware, farklı algoritmalar sunar: Fixed window, Sliding window, Token bucket ve Concurrency limiter.

Rate limiting policy'leri endpoint, controller veya global olarak uygulanabilir. IP, kullanıcı veya custom key bazlı limiting yapılabilir.

25. ASP.NET Core middleware'lerinde testability nasıl sağlanır?

Middleware testability, unit test ve integration test yaklaşımlarıyla sağlanır. Unit test için, fake HttpContext ve RequestDelegate oluşturularak middleware izole edilir.

RequestTimingMiddlewareTests.cscsharp
[Fact]
public async Task Middleware_LogsElapsedTime()
{
    var logger = new FakeLogger<RequestTimingMiddleware>();
    var middleware = new RequestTimingMiddleware(
        next: (ctx) => Task.CompletedTask,  // Terminal delegate
        logger: logger);

    var context = new DefaultHttpContext();
    context.Request.Method = "GET";
    context.Request.Path = "/api/products";

    await middleware.InvokeAsync(context);

    Assert.Single(logger.LogEntries);
    Assert.Contains("completed in", logger.LogEntries[0].Message);
}

Integration test için, WebApplicationFactory kullanılarak tüm pipeline test edilir. Bu yaklaşım, middleware'lerin birbirleriyle etkileşimini ve gerçek HTTP context'te davranışlarını doğrular.

Mülakatlarda Pratik Yapın

ASP.NET Core mülakat sorularına hazırlanırken, teorik bilgiyi kod yazarak pekiştirmek kritik öneme sahiptir. Custom middleware yazın, captive dependency senaryoları oluşturun ve Minimal APIs projesi geliştirin. Bu pratik deneyim, mülakatlarda confidence sağlar ve edge case'leri tartışabilme yeteneği kazandırır.

Sonuç

ASP.NET Core teknik mülakatlarında başarı, framework'ün temel yapı taşlarını derinlemesine anlamak ve bunları gerçek dünya senaryolarında nasıl uygulayacağını bilmekle mümkündür:

  • Middleware Pipeline: Exception handling, logging ve authentication gibi cross-cutting concern'leri doğru sırada ve performanslı şekilde ele alma
  • Dependency Injection: Yaşam döngülerini anlama, captive dependency'lerden kaçınma ve decorator pattern gibi ileri teknikler kullanma
  • Minimal APIs: Modern, hafif ve maintainable API'ler tasarlama ve feature-based organizasyon uygulama
  • İleri Seviye Konular: Rate limiting, health check'ler, background services ve test edilebilir middleware tasarımı

Bu konularda yetkin olmak, yalnızca mülakatlarda değil, production-grade ASP.NET Core uygulamaları geliştirirken de fark yaratır.

SharpSkill platformu, ASP.NET Core ve diğer modern teknolojilerde derinlemesine teknik mülakat hazırlığı sunmaktadır. İnteraktif testler, gerçek dünya senaryoları ve detaylı açıklamalarla donanmış flashcard'lar aracılığıyla, teorik bilgiyi pratik beceriye dönüştürebilirsiniz. .NET ekosisteminde kariyer hedeflerinize ulaşmak için SharpSkill platformunu keşfedin.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#asp.net core
#interview
#middleware
#dependency injection
#minimal apis
#dotnet

Paylaş

İlgili makaleler