.NET & C# Development · Lesson 11 of 11

Interview Prep: Senior (Q81–Q250)

Mid-Level Questions (Q81–Q160)

These questions are asked in mid-level interviews (2–5 years experience). They expect you to explain the why not just the what.


Async / Await Deep Dive

Q81: How does async/await actually work under the hood?

The C# compiler transforms async methods into a state machine. Each await is a state transition point. The method is split into pieces; when the awaited task completes, the state machine continues from where it paused.

C#
// What you write
public async Task<int> AddAsync(int a, int b)
{
    await Task.Delay(100);
    return a + b;
}

// What the compiler generates (simplified)
public Task<int> AddAsync(int a, int b)
{
    var stateMachine = new AddAsyncStateMachine { a = a, b = b, _state = -1 };
    stateMachine.MoveNext();
    return stateMachine._task;
}

Q82: What is SynchronizationContext and how does it relate to await?

SynchronizationContext represents a "thread" or "context" that work can be dispatched to. In UI apps (WPF), it's the UI thread. In classic ASP.NET, it was the request context.

When you await, by default the continuation is posted back to the current SynchronizationContext. This is why code in UI apps continues on the UI thread after await.

ASP.NET Core has no SynchronizationContext — awaits don't need to restore context, making it naturally high-performance.


Q83: Explain a deadlock caused by mixing sync and async code.

C#
// In a context WITH SynchronizationContext (WPF, classic ASP.NET)
public string GetResult()
{
    return GetDataAsync().Result;  // blocks the thread, holds the sync context
}

public async Task<string> GetDataAsync()
{
    await Task.Delay(100);  // tries to resume on the captured sync context
    // BUT the sync context is blocked waiting for .Result → DEADLOCK
    return "data";
}

// Fix: use ConfigureAwait(false) in library code
public async Task<string> GetDataAsync()
{
    await Task.Delay(100).ConfigureAwait(false);  // doesn't need sync context
    return "data";
}

Q84: What is Channel<T> and when do you use it?

A high-performance producer-consumer queue built on top of async primitives.

C#
var channel = Channel.CreateUnbounded<WorkItem>();

// Producer
await channel.Writer.WriteAsync(new WorkItem(data));
channel.Writer.Complete();

// Consumer
await foreach (var item in channel.Reader.ReadAllAsync(ct))
{
    await ProcessAsync(item);
}

Use instead of BlockingCollection<T> (which blocks threads) or raw ConcurrentQueue<T> for async producer-consumer scenarios.


Q85: What is the difference between parallelism and concurrency?

  • Concurrency: handling multiple things by interleaving (async/await — one thread switches between tasks)
  • Parallelism: doing multiple things simultaneously (multiple CPU cores — Parallel.ForEachAsync, PLINQ)
C#
// Concurrency: one thread, many async I/O operations
var tasks = ids.Select(id => FetchFromApiAsync(id));
var results = await Task.WhenAll(tasks);

// Parallelism: multiple threads for CPU-bound work
var results = await Parallel.ForEachAsync(items, async (item, ct) =>
{
    await ProcessCpuIntensiveAsync(item);
});

EF Core Internals

Q86: How does EF Core change tracking work?

The DbContext maintains a ChangeTracker that tracks the state of every entity it's aware of: Added, Modified, Unchanged, Deleted, Detached.

When you call SaveChanges(), EF Core:

  1. Inspects all tracked entities
  2. Compares current values to their snapshot (original values)
  3. Generates the minimal SQL needed (UPDATE only changed columns)
C#
var customer = await _context.Customers.FindAsync(id);
// customer is now tracked as Unchanged
customer.Name = "New Name";
// ChangeTracker detects the change → Modified
await _context.SaveChangesAsync();
// Generates: UPDATE Customers SET Name='New Name' WHERE Id=@id

Q87: What is the difference between SaveChanges and ExecuteUpdateAsync?

  • SaveChanges: loads entity, change tracks it, then generates UPDATE for changed columns. Requires loading the entity first.
  • ExecuteUpdateAsync (EF Core 7+): generates a bulk UPDATE directly without loading. No change tracking, no entity.
C#
// SaveChanges: 2 queries (SELECT + UPDATE)
var customer = await _context.Customers.FindAsync(id);  // SELECT
customer.Tier = "pro";
await _context.SaveChangesAsync();  // UPDATE

// ExecuteUpdateAsync: 1 query, no entity loaded
await _context.Customers
    .Where(c => c.Id == id)
    .ExecuteUpdateAsync(s => s.SetProperty(c => c.Tier, "pro"));  // UPDATE directly

Q88: What is a global query filter and what are its pitfalls?

C#
// Applied automatically to ALL queries against this entity
modelBuilder.Entity<Customer>().HasQueryFilter(c => !c.IsDeleted);

Pitfalls:

  • Easy to forget it's there, leading to confusing "why can't I see this record?" bugs
  • Performance: filter adds a condition to every query (usually fine, but check execution plans)
  • Must use IgnoreQueryFilters() when you genuinely need deleted records

Q89: Explain the N+1 problem and all ways to solve it.

The N+1 problem: fetching N parent records, then executing 1 query per parent to get children.

Solutions:

  1. Include() — eagerly load in the same query (JOIN)
  2. AsSplitQuery() — separate queries per included collection (avoids cartesian explosion)
  3. ProjectionSelect() only the columns you need
  4. Explicit loadingawait _context.Entry(order).Collection(o => o.Items).LoadAsync()
  5. Batch loading — load all child IDs at once with a WHERE IN clause

Q90: What is an owned entity type in EF Core?

An entity that doesn't have its own table — its properties are stored in the owner's table as columns.

C#
public class Order { public Money Total { get; set; } = null!; }
public record Money(decimal Amount, string Currency);

// Configuration
builder.OwnsOne(o => o.Total, m =>
{
    m.Property(x => x.Amount).HasColumnName("TotalAmount");
    m.Property(x => x.Currency).HasColumnName("TotalCurrency");
});
// Stored in Orders table: TotalAmount, TotalCurrency columns

ASP.NET Core Internals

Q91: Explain the ASP.NET Core request pipeline.

When a request arrives:

  1. Kestrel (HTTP server) receives the TCP connection
  2. Request is passed through the middleware pipeline (chain of delegates)
  3. Routing middleware matches the URL to an endpoint
  4. Endpoint (controller action or minimal API handler) is invoked
  5. Response flows back through the middleware pipeline in reverse
  6. Kestrel sends the response
C#
// Each middleware: can modify request, call next, modify response
app.Use(async (context, next) =>
{
    // Before
    await next(context);
    // After
});

Q92: How does dependency injection work in ASP.NET Core?

The DI container is an IServiceProvider. When a controller or middleware is instantiated, the container resolves its constructor parameters by looking up registered services.

Key internals:

  • The container holds service descriptors (interface → implementation + lifetime)
  • For Scoped, it creates a new scope per request (via IServiceScopeFactory)
  • For Singleton, it checks a dictionary; for Transient, it always creates new

Q93: What are options and IOptions<T>, IOptionsSnapshot<T>, IOptionsMonitor<T>?

All read from configuration. The difference is when they refresh:

  • IOptions<T>: singleton — reads once at startup, never updates
  • IOptionsSnapshot<T>: scoped — re-reads per request (can pick up config changes)
  • IOptionsMonitor<T>: singleton + change notification — updates in real-time via OnChange()
C#
public class JwtSettings { public string Secret { get; set; } = ""; }

// Register
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("Jwt"));

// Inject
public class TokenService
{
    private readonly JwtSettings _settings;
    public TokenService(IOptions<JwtSettings> options) => _settings = options.Value;
}

Q94: What is IActionFilter and when would you use it?

Filters run code before/after controller actions. Types: Action filters, Authorization filters, Resource filters, Exception filters, Result filters.

C#
// Log execution time for all actions
public class TimingFilter : IActionFilter
{
    private Stopwatch _sw = new();

    public void OnActionExecuting(ActionExecutingContext ctx) => _sw.Start();

    public void OnActionExecuted(ActionExecutedContext ctx)
    {
        _sw.Stop();
        ctx.HttpContext.Response.Headers["X-Execution-Time"] = _sw.ElapsedMilliseconds + "ms";
    }
}

// Register globally
builder.Services.AddControllers(o => o.Filters.Add<TimingFilter>());

Q95: What is the difference between IMiddleware and the Use(async (ctx, next) => ...) pattern?

  • Anonymous lambda: simpler, can't be DI-injected properly
  • IMiddleware: interface-based, supports constructor DI with any lifetime
C#
public class LoggingMiddleware : IMiddleware
{
    private readonly ILogger<LoggingMiddleware> _logger;

    public LoggingMiddleware(ILogger<LoggingMiddleware> logger) => _logger = logger;

    public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
    {
        _logger.LogInformation("{Method} {Path}", ctx.Request.Method, ctx.Request.Path);
        await next(ctx);
    }
}
// Register: services.AddTransient<LoggingMiddleware>()
// Use: app.UseMiddleware<LoggingMiddleware>()

CQRS and MediatR

Q96: What is CQRS and why use it?

Command Query Responsibility Segregation — separates write operations (Commands) from read operations (Queries) into different models.

Benefits:

  • Commands have validation, domain logic, events
  • Queries are optimized for reading (projections, no domain overhead)
  • Scales independently — read side can use a different data store
  • Easier to test each side in isolation

Q97: How do you implement CQRS with MediatR?

C#
// 1. Install MediatR
// dotnet add package MediatR

// 2. Define Command
public record CreateOrderCommand(int CustomerId, List<OrderItemDto> Items) : IRequest<int>;

// 3. Handler
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, int>
{
    private readonly IOrderRepository _orders;
    private readonly IEmailService _email;

    public CreateOrderCommandHandler(IOrderRepository orders, IEmailService email)
    {
        _orders = orders;
        _email = email;
    }

    public async Task<int> Handle(CreateOrderCommand cmd, CancellationToken ct)
    {
        var order = new Order(cmd.CustomerId, cmd.Items);
        var id = await _orders.CreateAsync(order, ct);
        await _email.SendConfirmationAsync(order.CustomerEmail, id);
        return id;
    }
}

// 4. Query
public record GetOrderByIdQuery(int Id) : IRequest<OrderDto?>;

public class GetOrderByIdQueryHandler : IRequestHandler<GetOrderByIdQuery, OrderDto?>
{
    private readonly IOrderRepository _orders;
    public GetOrderByIdQueryHandler(IOrderRepository orders) => _orders = orders;

    public async Task<OrderDto?> Handle(GetOrderByIdQuery q, CancellationToken ct)
        => await _orders.GetDtoByIdAsync(q.Id, ct);
}

// 5. Controller — just dispatches, no business logic
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateOrderCommand cmd, CancellationToken ct)
{
    int id = await _mediator.Send(cmd, ct);
    return CreatedAtRoute("GetOrder", new { id }, new { id });
}

Q98: What is the Pipeline Behavior in MediatR?

Cross-cutting concerns as middleware for MediatR handlers.

C#
// Logging behavior — runs around every handler
public class LoggingBehavior<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
{
    private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
        => _logger = logger;

    public async Task<TResponse> Handle(
        TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        _logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
        var response = await next();
        _logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
        return response;
    }
}

// Validation behavior
public class ValidationBehavior<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
        => _validators = validators;

    public async Task<TResponse> Handle(
        TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        if (!_validators.Any()) return await next();

        var context = new ValidationContext<TRequest>(request);
        var failures = _validators
            .SelectMany(v => v.Validate(context).Errors)
            .Where(f => f is not null)
            .ToList();

        if (failures.Count != 0)
            throw new ValidationException(failures);

        return await next();
    }
}

Domain-Driven Design

Q99: What is a Domain Event and how do you implement it in .NET?

A domain event signals that something meaningful happened in the domain. Other parts of the system subscribe to react.

C#
// 1. Base entity with domain events
public abstract class AggregateRoot
{
    private readonly List<IDomainEvent> _events = new();
    public IReadOnlyList<IDomainEvent> DomainEvents => _events.AsReadOnly();
    protected void AddEvent(IDomainEvent e) => _events.Add(e);
    public void ClearEvents() => _events.Clear();
}

public record OrderPlacedEvent(int OrderId, int CustomerId, decimal Total) : IDomainEvent;

// 2. Aggregate raises event
public class Order : AggregateRoot
{
    public void Complete()
    {
        Status = "delivered";
        AddEvent(new OrderPlacedEvent(Id, CustomerId, Total));
    }
}

// 3. After SaveChanges, dispatch events
public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
{
    var events = ChangeTracker.Entries<AggregateRoot>()
        .SelectMany(e => e.Entity.DomainEvents)
        .ToList();

    var result = await base.SaveChangesAsync(ct);

    foreach (var e in events)
        await _mediator.Publish(e, ct);  // dispatch via MediatR

    return result;
}

Q100: What is the Outbox Pattern?

Guarantees that domain events are reliably published even if the message broker is down. Store events in the same database transaction as the domain changes; a background worker polls and publishes them.

C#
// 1. Outbox table
CREATE TABLE OutboxMessages (
    Id UUID PRIMARY KEY,
    Type VARCHAR(500),
    Content JSONB,
    CreatedAt TIMESTAMPTZ,
    ProcessedAt TIMESTAMPTZ
);

// 2. Write event and data in same transaction
await using var tx = await _context.Database.BeginTransactionAsync();
_context.Orders.Add(order);
_context.OutboxMessages.Add(new OutboxMessage(
    Guid.NewGuid(), typeof(OrderPlacedEvent).Name,
    JsonSerializer.Serialize(orderPlacedEvent)));
await _context.SaveChangesAsync();
await tx.CommitAsync();

// 3. Background job polls and publishes
var unpublished = await _context.OutboxMessages
    .Where(m => m.ProcessedAt == null)
    .Take(100).ToListAsync();
foreach (var msg in unpublished)
{
    await _messageBus.PublishAsync(msg.Type, msg.Content);
    msg.ProcessedAt = DateTime.UtcNow;
}
await _context.SaveChangesAsync();

Performance and Memory

Q101: What causes memory leaks in .NET?

.NET has a GC, but memory leaks still happen:

  1. Static event subscriptions: event source holds reference to subscriber → subscriber never GC'd
  2. Long-lived caches: Dictionary<K,V> grows unbounded → use MemoryCache with expiration
  3. Unmanaged resources not disposed: HttpClient, DbConnection, FileStream not disposed
  4. Captured closures: lambda captures large objects, preventing GC
  5. Singleton holding scoped services: singleton holds reference to a request-scoped object
C#
// Memory leak: event never unsubscribed
publisher.DataReceived += Handler;  // publisher is long-lived
// Fix:
publisher.DataReceived -= Handler;  // or use WeakReference

// Memory leak: growing dictionary
private static readonly Dictionary<string, byte[]> _cache = new();
// Fix: use IMemoryCache with expiration

Q102: What is ArrayPool<T> and MemoryPool<T>?

Renting arrays from a pool instead of allocating new ones — reduces GC pressure in hot paths.

C#
var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(1024);  // get array >= 1024 bytes from pool
try
{
    // use buffer
}
finally
{
    pool.Return(buffer);  // return to pool — DON'T use it after this!
}

Q103: What is the GC.Collect() controversy?

Calling GC.Collect() forces a full garbage collection. Almost never appropriate in production:

  • GC knows better when to collect (it tracks allocations, pressure)
  • Forces Gen 0, 1, and 2 collection simultaneously — expensive pause
  • Can temporarily improve memory but hurts throughput

The only legitimate use: benchmarking (to start from a known GC state).


Q104: How do you profile memory in a .NET application?

  • dotnet-counters: live metrics (dotnet-counters monitor --process-id <pid>)
  • dotnet-dump: dump heap analysis
  • Visual Studio Diagnostic Tools: heap snapshots, allocation tracking
  • JetBrains dotMemory: commercial, powerful
  • PerfView: Microsoft's free, comprehensive performance tool

Q105: What is Span<T> vs Memory<T> vs ArraySegment<T>?

  • ArraySegment<T>: managed, can be stored in fields, not zero-copy
  • Span<T>: zero-allocation slice over any memory. Stack-only. Can't cross await.
  • Memory<T>: like Span but heap-compatible. Can be stored and used across await.
  • ReadOnlySpan<T> / ReadOnlyMemory<T>: immutable versions

Security

Q106: How do you prevent SQL injection in .NET?

Always use parameterized queries. In EF Core, all LINQ queries are parameterized automatically.

C#
// UNSAFE — never do this
var query = $"SELECT * FROM users WHERE email = '{email}'";
_context.Users.FromSqlRaw(query);  // SQL injection!

// SAFE — parameterized
_context.Users.FromSqlInterpolated($"SELECT * FROM users WHERE email = {email}");
// EF Core sends: SELECT * FROM users WHERE email = @p0 (with @p0 = email)

Q107: What is HTTPS enforcement and HSTS?

C#
// Redirect HTTP to HTTPS
app.UseHttpsRedirection();

// HSTS: tell browsers to always use HTTPS for this domain
app.UseHsts();
// Adds: Strict-Transport-Security: max-age=31536000; includeSubDomains

Q108: How do you store passwords securely in .NET?

Use the built-in PasswordHasher<TUser> (ASP.NET Core Identity) or BCrypt.Net.

C#
// ASP.NET Core Identity password hasher
var hasher = new PasswordHasher<User>();
string hash = hasher.HashPassword(user, password);
var result = hasher.VerifyHashedPassword(user, hash, inputPassword);
// Result: Succeeded, Failed, SuccessRehashNeeded

// Or BCrypt
string hash = BCrypt.Net.BCrypt.HashPassword(password, workFactor: 12);
bool valid = BCrypt.Net.BCrypt.Verify(password, hash);

Never: MD5, SHA1, SHA256 for passwords — these are fast hashes, easy to brute-force. Always use slow hashes: BCrypt, Argon2, PBKDF2.


Microservices and Architecture

Q109: What is the difference between REST and gRPC?

| | REST | gRPC | |--|-----|------| | Protocol | HTTP/1.1 | HTTP/2 | | Format | JSON (text) | Protobuf (binary) | | Performance | Lower | 5-10x faster | | Browser support | Native | Requires proxy (grpc-web) | | Streaming | Limited | Native (bidirectional) | | Contract | OpenAPI/Swagger | .proto file | | Use when | Public APIs, simplicity | Internal microservices, performance |


Q110: What is the Circuit Breaker pattern?

Prevents cascading failures when a downstream service is unhealthy. After N failures, the circuit "opens" — calls fail fast without hitting the broken service. After a timeout, it enters "half-open" to test recovery.

C#
// Polly library
services.AddHttpClient<IPaymentService, PaymentService>()
    .AddResilienceHandler("payment", builder =>
    {
        builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromSeconds(10),
            BreakDuration = TimeSpan.FromSeconds(30),
        });
        builder.AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromMilliseconds(200),
            BackoffType = DelayBackoffType.Exponential,
        });
    });

Q111: What is a Saga pattern?

A sequence of local transactions where each step publishes an event. If a step fails, compensating transactions undo the previous steps.

C#
// Order saga steps:
// 1. CreateOrder → OrderCreated event
// 2. ReserveInventory → InventoryReserved event (or InventoryFailed → compensate)
// 3. ProcessPayment → PaymentProcessed (or PaymentFailed → release inventory)
// 4. ShipOrder → OrderShipped

// MassTransit saga state machine
public class OrderStateMachine : MassTransitStateMachine<OrderState>
{
    public State Pending { get; } = null!;
    public State InventoryReserved { get; } = null!;
    public State Completed { get; } = null!;

    public OrderStateMachine()
    {
        Initially(
            When(OrderCreated)
                .PublishAsync(ctx => ctx.Init<ReserveInventoryCommand>(new { ctx.Data.OrderId }))
                .TransitionTo(Pending));

        During(Pending,
            When(InventoryReserved)
                .PublishAsync(ctx => ctx.Init<ProcessPaymentCommand>(new { ctx.Data.OrderId }))
                .TransitionTo(InventoryReserved));
    }
}

Q112: What is health check and why is it important in microservices?

Kubernetes and load balancers use health check endpoints to determine if a service should receive traffic:

  • Liveness: is the app alive? (failing → restart)
  • Readiness: is the app ready to handle requests? (failing → remove from load balancer rotation)
  • Startup: has the app finished starting? (protects from premature liveness checks)

Senior-Level Questions (Q113–Q250)

Q113: Design a rate limiter. What are the algorithms?

  • Fixed window: count requests per time window. Simple but allows burst at window boundaries.
  • Sliding window: rolling time window. No burst problem.
  • Token bucket: tokens accumulate at a fixed rate, consumed per request. Allows controlled bursts.
  • Leaky bucket: queue requests and process at fixed rate. Smooths burst traffic.

.NET 7+ built-in AddRateLimiter supports fixed window and sliding window.


Q114: What is the Actor model and how does it apply to .NET?

Actors are independent units of state and behavior that communicate only via messages. Microsoft Orleans implements the actor model with virtual actors ("grains") — scalable, fault-tolerant actor frameworks.

Benefits: natural distributed system model, no shared state, easy scaling.


Q115: How do you implement idempotency in an API?

C#
// Client sends an Idempotency-Key header with each request
// Server stores results keyed by idempotency key
// Second request with same key returns cached result

public class IdempotencyFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext ctx, ActionExecutionDelegate next)
    {
        var key = ctx.HttpContext.Request.Headers["Idempotency-Key"].ToString();
        if (string.IsNullOrEmpty(key)) { await next(); return; }

        if (_cache.TryGetValue(key, out var cached))
        {
            ctx.Result = new OkObjectResult(cached);
            return;
        }

        var result = await next();
        _cache.Set(key, result.Result, TimeSpan.FromHours(24));
    }
}

Q116: What is event sourcing?

Instead of storing current state, store every event that led to the current state.

C#
// Events (immutable, append-only)
public record OrderCreated(int OrderId, int CustomerId, DateTime At);
public record OrderItemAdded(int OrderId, int ProductId, int Qty, decimal Price);
public record OrderShipped(int OrderId, string TrackingNumber, DateTime At);

// Rehydrate aggregate from events
var order = new Order();
foreach (var e in eventStore.GetEvents(orderId))
    order.Apply(e);

// Benefits: full audit trail, time travel, replay projections
// Trade-off: complex, eventual consistency, storage growth

Q117: What is the CAP theorem and how does it affect .NET microservices?

You can only guarantee two of: Consistency, Availability, Partition Tolerance.

In microservices: choose AP (availability + partition tolerance) with eventual consistency for most scenarios. Use CP (consistency + partition) for financial transactions.


Q118: How do you implement distributed caching?

C#
// IDistributedCache with Redis
builder.Services.AddStackExchangeRedisCache(options =>
    options.Configuration = builder.Configuration["Redis:ConnectionString"]);

// Usage
public async Task<CustomerDto?> GetCustomerAsync(int id, CancellationToken ct)
{
    var key = $"customer:{id}";
    var cached = await _cache.GetStringAsync(key, ct);
    if (cached is not null)
        return JsonSerializer.Deserialize<CustomerDto>(cached);

    var customer = await _db.Customers.FindAsync(id, ct);
    if (customer is null) return null;

    var dto = _mapper.Map<CustomerDto>(customer);
    await _cache.SetStringAsync(key, JsonSerializer.Serialize(dto),
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
        }, ct);
    return dto;
}

Cache invalidation strategies: TTL (time-to-live), event-driven invalidation, write-through.


Q119: What is the Strangler Fig pattern?

Incrementally migrate a monolith to microservices. Route new features to new services while old functionality stays in the monolith. Over time, "strangle" the monolith by replacing pieces.

Browser → Nginx (reverse proxy)
         ↓
   /api/orders → New Orders Microservice
   /api/products → Legacy Monolith
   /api/users → New Users Microservice

Q120: How does Azure Service Bus compare to RabbitMQ?

| | Azure Service Bus | RabbitMQ | |--|------------------|---------| | Hosting | Fully managed SaaS | Self-hosted (or CloudAMQP) | | Protocols | AMQP, HTTP | AMQP, STOMP, MQTT | | Dead-letter queue | Built-in | Built-in | | Ordering | Sessions (guaranteed) | Limited | | Max message size | 1MB / 100MB (Premium) | Configurable | | Azure integration | Native | Via custom code |


Q121–Q160: Expert-level rapid fire

Q121: What is IAsyncEnumerable<T>? Async stream — produces values asynchronously one at a time. Great for streaming large datasets without loading all into memory.

C#
public async IAsyncEnumerable<Order> StreamOrdersAsync([EnumeratorCancellation] CancellationToken ct)
{
    await foreach (var chunk in _db.Orders.AsAsyncEnumerable().WithCancellation(ct))
        yield return chunk;
}

Q122: What is IHostedService vs BackgroundService? BackgroundService is an abstract base that implements IHostedService with a ExecuteAsync(CancellationToken) lifecycle method. Easier to use.

Q123: How do you implement a job queue in .NET? Use Hangfire (mature, SQL-backed) or Quartz.NET for scheduled jobs. For simple background work, use Channel<T> with a BackgroundService consumer.

Q124: What is the Repository and Unit of Work pattern? Repository: abstracts data access per aggregate. Unit of Work: groups multiple repository operations into a single transaction. EF Core's DbContext implements Unit of Work.

Q125: What is AutoMapper and when should you avoid it? Library that maps between objects (e.g., Entity → DTO). Avoid for complex mappings (hidden bugs), deeply nested graphs (N+1), or when projection with Select() is cleaner and more performant.

Q126: What is Polly? Resilience library for .NET. Provides retry, circuit breaker, timeout, fallback, bulkhead isolation policies.

Q127: What is Minimal API vs Controllers performance difference? Minimal APIs have slightly lower overhead (no controller activation, no action descriptor overhead). For most apps, the difference is negligible. Matters in extremely high-throughput scenarios.

Q128: What is the Result<T> pattern? Represent success/failure without exceptions. Reduces try/catch noise.

C#
public sealed class Result<T>
{
    public T? Value { get; }
    public string? Error { get; }
    public bool IsSuccess => Error is null;
    public static Result<T> Ok(T value) => new(value, null);
    public static Result<T> Fail(string error) => new(default, error);
}

Q129: What is FluentValidation and how does it work with ASP.NET Core? FluentValidation builds validators using a fluent API. When registered with AddFluentValidationAutoValidation(), invalid requests automatically get a 400 response with error details.

Q130: What is OpenTelemetry in .NET? Standardized observability framework. Instruments HTTP requests, DB calls, and custom spans. Exports to Jaeger, Zipkin, Azure Monitor, Datadog.

C#
builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddAspNetCoreInstrumentation().AddEntityFrameworkCoreInstrumentation())
    .WithMetrics(m => m.AddAspNetCoreInstrumentation());

Q131: What is ILogger<T> category and why does it matter? The generic type parameter sets the log category (used for filtering). Configure minimum log level per category:

JSON
"Logging": { "LogLevel": { "OrderFlow.Services": "Debug", "Default": "Warning" }}

Q132: What is structured logging? Logs with named properties that can be indexed and queried. Use Serilog or Seq for structured log storage.

C#
_logger.LogInformation("Order {OrderId} placed by {CustomerId}", orderId, customerId);
// Stored as: { OrderId: 42, CustomerId: 7, Message: "Order 42 placed by 7" }

Q133: What is distributed tracing? Tracking a request as it flows through multiple microservices. Each service adds a span to a trace (identified by a trace ID). Visualized in Jaeger or Zipkin.

Q134: What is IHttpContextAccessor and when should you avoid it? Provides access to the current HttpContext outside of controllers/middleware. Avoid in domain services — it couples them to HTTP. Pass user context explicitly instead.

Q135: What is the Anchor Pattern in DDD? Domain events or value objects serve as "anchors" for invariants — ensuring the domain always transitions through consistent states.

Q136: How do you implement pagination with EF Core efficiently? Use keyset (cursor) pagination for large datasets instead of OFFSET.

C#
// Keyset: efficient regardless of depth
var orders = await _context.Orders
    .Where(o => o.Id > lastSeenId)
    .OrderBy(o => o.Id)
    .Take(pageSize)
    .ToListAsync();

Q137: What is IQueryable composition and why does it matter? LINQ queries on IQueryable compose — adding Where(), Skip(), Take() build up an expression tree. The entire query is sent to the database as a single SQL statement. Never use AsEnumerable() before filtering.

Q138: What is Azure Managed Identity? Allows Azure resources (App Service, VM) to authenticate to other Azure services (Key Vault, SQL) without storing credentials. The identity is managed by Azure AD.

Q139: What is OWASP Top 10 and how does it apply to .NET? Top web security risks: Injection, Broken Auth, XSS, IDOR, Security Misconfiguration, Vulnerable Components, Logging failures. .NET mitigations: EF Core prevents SQL injection, ASP.NET Core uses anti-forgery tokens, Content Security Policy headers.

Q140: What is CancellationTokenSource.CreateLinkedTokenSource? Creates a token that's cancelled when EITHER of two tokens is cancelled (e.g., request cancellation OR global shutdown).

C#
using var linked = CancellationTokenSource.CreateLinkedTokenSource(requestCt, shutdownCt);
await DoWorkAsync(linked.Token);

Q141: What is the IOptions validation? Configure validators to run at startup — fail fast instead of discovering config errors at runtime.

C#
builder.Services.AddOptions<JwtSettings>()
    .BindConfiguration("Jwt")
    .ValidateDataAnnotations()
    .ValidateOnStart();

Q142: What is hot reload in .NET 6+? Applies code changes to a running app without restarting. Works for most code changes in development (dotnet watch). Not for structural changes (new types, method signature changes).

Q143: How does Kestrel handle high concurrency? Kestrel is event-loop based (similar to Node.js). Uses libuv or managed sockets. Async I/O means one thread handles thousands of connections. Thread count = CPU count (for compute), not connection count.

Q144: What is the difference between ValueTask and Task in terms of allocation? Task always allocates on the heap. ValueTask is a struct — can be returned without allocation if the result is synchronous. Use ValueTask for methods that are commonly synchronous (e.g., in-memory cache hit).

Q145: What is unsafe code in C#? Code that bypasses the CLR's safety guarantees and allows pointer arithmetic. Used in performance-critical code (binary serialization, interop). Requires /unsafe compiler flag.

Q146: What is native AOT (Ahead-of-Time) compilation? Compiles .NET to native code at publish time — no JIT at runtime. Benefits: faster startup, smaller memory footprint, no .NET runtime required. Trade-offs: no dynamic code generation, limited reflection.

Q147: What is the GC generations (Gen 0, 1, 2)? GC divides objects into generations. Short-lived objects (Gen 0) are collected frequently and cheaply. Long-lived objects (Gen 2) are collected rarely. Design for short-lived objects — avoid promoting objects to higher generations.

Q148: What is System.Text.Json vs Newtonsoft.Json? System.Text.Json: built-in, higher performance, allocation-efficient (Utf8JsonWriter). Less feature-rich. Newtonsoft.Json: more features, better compatibility, larger ecosystem. Slower. For new projects: prefer System.Text.Json. Migrate only if specific features are needed.

Q149: What is gRPC streaming in .NET?

C#
// Server-side streaming: one request, many responses
rpc StreamOrders (GetOrdersRequest) returns (stream OrderDto);

// Bidirectional streaming: many requests, many responses
rpc Chat (stream ChatMessage) returns (stream ChatMessage);

Q150: What is SignalR? Real-time bidirectional communication between server and browser. Uses WebSockets with fallback to Long Polling/Server-Sent Events.

C#
// Hub
public class OrderHub : Hub
{
    public async Task NotifyOrderStatus(int orderId, string status)
        => await Clients.All.SendAsync("OrderStatusChanged", orderId, status);
}

Q151: What is Blazor? .NET UI framework that runs C# in the browser (WebAssembly) or on the server (Blazor Server). Build interactive UIs without JavaScript.

Q152: What is Minimal API route grouping?

C#
var orders = app.MapGroup("/api/v1/orders").RequireAuthorization();
orders.MapGet("/", GetAll);
orders.MapGet("/{id}", GetById);
orders.MapPost("/", Create);

Q153: What is the ProblemDetails standard? RFC 7807 standard for HTTP API error responses. ASP.NET Core uses it by default.

JSON
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  "title": "Not Found", "status": 404, "detail": "Order 42 not found" }

Q154: What is the difference between GetRequiredService and GetService? GetService<T>(): returns null if not registered. GetRequiredService<T>(): throws if not registered. Prefer this — fail fast.

Q155: How do you implement feature flags in .NET? Use Microsoft.FeatureManagement. Flags read from configuration, updated without restart.

C#
if (await _featureManager.IsEnabledAsync("NewCheckout"))
    return await HandleNewCheckoutAsync();

Q156: What is IEndpointFilter in minimal APIs? Similar to action filters for controller-based APIs but for minimal API endpoints.

Q157: What is WebApplicationFactory<T> in testing? Creates an in-memory test server for integration tests — no need to deploy.

C#
public class OrdersTests : IClassFixture<WebApplicationFactory<Program>>
{
    [Fact]
    public async Task CreateOrder_Returns201()
    {
        var response = await _client.PostAsJsonAsync("/api/orders", request);
        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    }
}

Q158: What is FluentAssertions? A testing library for more readable assertions.

C#
result.Should().Be(42);
list.Should().HaveCount(3).And.Contain(x => x.Id == 1);
action.Should().ThrowAsync<NotFoundException>();

Q159: What is Moq and how does it work? A mocking library using dynamic proxies to create fake implementations of interfaces.

C#
var mockRepo = new Mock<IOrderRepository>();
mockRepo.Setup(r => r.GetByIdAsync(It.IsAny<int>(), default))
    .ReturnsAsync(new Order { Id = 1 });
var sut = new OrderService(mockRepo.Object, /* ... */);

Q160: What is the test pyramid for .NET?

  • Unit tests (many): test individual classes, mock dependencies
  • Integration tests (some): test with real database (testcontainers), in-memory ASP.NET Core
  • E2E tests (few): Playwright/Selenium against a running environment

Q161–Q250: Staff/Principal Level

Q161: How do you design a .NET system for 1 million requests per day? Scale stateless API horizontally (multiple pods). Use Redis for caching (avoid DB per request). Use async throughout. DB read replicas for analytics. CDN for static assets. Rate limiting per user.

Q162: How do you handle database migrations safely in production? Backward-compatible migrations: add columns as nullable, deploy new code, backfill, add NOT NULL. Never drop columns in the same deployment as removing the code.

Q163: What is the Specification Pattern? Encapsulate query criteria as reusable objects.

C#
public class ActiveCustomerSpec : Specification<Customer>
{
    public override Expression<Func<Customer, bool>> ToExpression()
        => c => c.IsActive && !c.IsDeleted;
}
var customers = await _repo.ListAsync(new ActiveCustomerSpec());

Q164: What is REPR pattern (Request-Endpoint-Response)? Each endpoint is a class with a handler. One class per endpoint — very explicit, easy to find. Used by Ardalis.ApiEndpoints.

Q165: What is Wolverine vs MediatR? Wolverine is a newer message bus + CQRS framework with convention-based handler discovery, automatic outbox, and better performance than MediatR.

Q166: What is Aspire (.NET Aspire)? A .NET stack for building observable, production-ready distributed applications. Provides orchestration, service defaults, and dashboard — simplifies microservice local development.

Q167: What is the Vertical Slice Architecture? Each feature owns its entire stack (endpoint + handler + query + models) instead of horizontal layers. Features are self-contained, making it easier to understand and modify them in isolation.

Q168: How do you handle eventual consistency in .NET microservices? Design for idempotency, use the Outbox Pattern for reliable publishing, use sagas for compensating transactions, accept that reads may be stale and design UX accordingly.

Q169: What is IDistributedLock and when do you need it? When multiple instances of a service may run the same background job simultaneously. Redis-based distributed lock (Redlock) ensures only one instance executes at a time.

Q170: How do you implement backpressure in a .NET message consumer? Use Channel with a bounded capacity — producers block when the channel is full. Or use Service Bus topic subscriptions with max concurrent calls setting.

Q171–Q250: Remaining Questions Summary

Q171: What is the IStartupFilter? Pre-configures middleware before Configure runs. Q172: What is PipeWriter/PipeReader? System.IO.Pipelines — zero-copy I/O for network protocols. Q173: What is Kestrel's connection limit? Configurable via MaxConcurrentConnections. Default: unlimited. Q174: What is HTTP/3 support in .NET? .NET 6+ supports HTTP/3 via QUIC protocol. Enable with UseQuic(). Q175: What is .NET Workers service? A general-purpose hosted service that runs as a Windows service or systemd daemon. Q176: What is the difference between int.MaxValue overflow in checked vs unchecked context? Checked: throws OverflowException. Unchecked: wraps around to int.MinValue. Q177: What is Source Generators in C# 9+? Generate C# code at compile time based on attributes — used by EF Core, System.Text.Json for AOT-friendly serialization. Q178: What is interceptors in C# 12? Allows intercepting and replacing specific method calls at compile time. Q179: What is primary constructors (C# 12)? Constructor parameters available as class-level fields/properties without manual assignment. Q180: What is collection expressions (C# 12)? var list = [1, 2, 3] — unified syntax for creating arrays, lists, spans. Q181: What is IExceptionHandler in .NET 8? New interface for global exception handling, replacing the UseExceptionHandler lambda approach. Q182: What is keyed services in .NET 8? Register multiple implementations of the same interface, inject specific ones by key. AddKeyedScoped<IEmailSender, SmtpSender>("smtp"). Q183: What is short-circuit routing in .NET 8 minimal APIs? app.MapGet("/health").ShortCircuit() — bypass the full middleware pipeline for specific routes. Q184: What is HybridCache in .NET 9? Combines IMemoryCache and IDistributedCache with stampede protection (only one request rebuilds the cache on miss). Q185: How do you benchmark .NET code? Use BenchmarkDotNet — the gold standard. Handles warmup, multiple iterations, GC stats.

C#
[MemoryDiagnoser]
public class SerializationBenchmarks
{
    [Benchmark] public string JsonSerialize() => JsonSerializer.Serialize(data);
    [Benchmark] public string NewtonsoftSerialize() => JsonConvert.SerializeObject(data);
}

Q186–Q250: These questions cover Kubernetes deployment of .NET services, Azure Durable Functions (orchestration), SignalR scale-out with Redis backplane, OpenTelemetry custom metrics, Dapr integration, .NET Aspire service defaults, native AOT constraints, incremental Roslyn analyzers, Windows Communication Foundation migration to gRPC, Orleans virtual actors, Rebus message bus, Wolverine HTTP, Azure Service Bus sessions (ordered processing), Service Mesh with .NET (Istio), mTLS in microservices, .NET health check circuits, Application Performance Management with Application Insights, structured logging to Azure Monitor, chaos engineering with Chaos Monkey for .NET, blue-green deployment strategies for .NET on Azure App Service, database connection resilience (EF Core retry policies), CQRS with separate read/write databases, event-carried state transfer, zero-downtime schema migrations, saga orchestration vs choreography, distributed transaction alternatives, Domain-Driven Design aggregates in .NET, value object equality, domain service vs application service, hexagonal architecture implementation in C#, clean architecture testing strategy, vertical slice feature structure, C# source generator practical examples, incremental compilation with .NET SDK, multi-tenant .NET applications (schema-per-tenant, row-level security), and rolling back failed deployments in Azure App Service.