Learnixo
Back to blog
Backend Systemsintermediate

.NET / C# Interview Questions: Mid Level (Q61–Q160)

100 mid-level .NET interview questions with detailed answers — LINQ internals, async/await patterns, EF Core performance, DI lifetimes, authentication, caching, clean architecture, and system design basics.

LearnixoJune 3, 202622 min read
.NETC#InterviewMid-LevelEF CoreasyncClean Architecture
Share:𝕏

About This Guide

These questions target developers with 2–5 years of .NET experience. Mid-level interviews expect you to explain why — reasoning behind choices, tradeoffs, and consequences of decisions.

Don't just say what something is. Say when you'd use it, when you wouldn't, and what goes wrong if you pick wrong.


LINQ and Collections

Q61: What is the difference between IEnumerable<T> and IQueryable<T>?

IEnumerable<T> runs in-process using delegates. Filtering happens after data is loaded. IQueryable<T> uses expression trees — providers (EF Core) translate them to SQL. Filtering happens in the database.

Calling .AsEnumerable() or .ToList() early converts to IEnumerable, loading all rows before filtering. This is a common performance mistake.

C#
// Loads all products, then filters in C# — BAD
dbContext.Products.AsEnumerable().Where(p => p.Price > 100);

// Filters in SQL — GOOD
dbContext.Products.Where(p => p.Price > 100);

Q62: What is deferred execution and when does it cause bugs?

LINQ operators like Where, Select, and OrderBy don't execute immediately — they build a query. Execution happens when you iterate or call ToList, Count, etc.

Bug scenario: multiple iterations re-execute EF Core queries. Or the source collection is mutated after defining the query.

C#
IQueryable<Order> query = dbContext.Orders.Where(o => o.Status == "Pending");

var count = query.Count();       // one DB call
var list  = query.ToList();      // second DB call — fetches data again

// Fix — materialize once
var pending = query.ToList();
var count   = pending.Count;     // no second DB call

Q63: What is an expression tree and why does EF Core need it?

An expression tree is a data structure representing code as inspectable objects. When a lambda targets IQueryable<T>, C# compiles it to an expression tree instead of a delegate. EF Core walks the tree and emits equivalent SQL.

If you use a delegate (e.g. by calling .AsEnumerable() first), EF can't see inside it — it can't generate SQL.


Q64: When would you use GroupBy vs ToLookup?

Both group elements by a key. GroupBy is deferred (lazy), so it can run in SQL when on IQueryable. ToLookup is immediate — it materialises all groups into a lookup structure for O(1) key access.

Use GroupBy in EF queries. Use ToLookup when you need fast repeated access to groups after loading data.


Q65: Why is Any() better than Count() > 0 for existence checks?

Any() short-circuits at the first match. Count() always scans the full sequence. In EF Core, Any() generates SELECT TOP 1, while Count() generates SELECT COUNT(*). For large tables, the difference is significant.


Q66: What does SelectMany do?

Flattens a sequence of sequences into one sequence. Equivalent to a nested foreach that yields each inner element.

C#
// Each order has many items — flatten all items
var allItems = orders.SelectMany(o => o.Items);

In EF Core, it generates a JOIN or subquery depending on context.


async/await

Q67: How does the compiler transform an async method?

It generates a state machine struct. Each await is a state. On the first call, the state machine starts. When the awaited task completes, the state machine is resumed from the saved state. Local variables become fields on the struct — they live on the heap.


Q68: What is ConfigureAwait(false) and when should you use it?

Tells the runtime not to capture the current SynchronizationContext when resuming after await. The continuation may run on any thread pool thread.

Use it in library code and background services — you don't need to resume on a specific context, and it prevents deadlocks when callers block synchronously. Don't use it in UI code where you must resume on the UI thread.


Q69: Explain an async deadlock and how to prevent it.

In contexts with a SynchronizationContext (WPF, classic ASP.NET), blocking an async method with .Result or .Wait() deadlocks: the block holds the context, but the continuation needs the context to resume.

Prevention: go async end-to-end, or use ConfigureAwait(false) in the awaited method so it doesn't need the context to resume.

ASP.NET Core has no SynchronizationContext, so this deadlock doesn't occur there — but it does in unit test frameworks.


Q70: What is ValueTask and when should you use it?

A struct alternative to Task that avoids heap allocation when the operation completes synchronously. Use it for high-frequency methods with a common synchronous fast path (cache hits, in-memory returns).

Rules: only await once, don't store and await later, don't await from multiple callers.


Q71: What is the difference between Task.WhenAll and Task.WhenAny?

WhenAll waits for all tasks to complete and returns all results. If any task throws, the exception is observed after all tasks finish.

WhenAny returns when the first task completes. Useful for timeouts (race against a delay task) or taking the fastest of multiple sources.


Q72: How do you run async work with bounded concurrency?

Use SemaphoreSlim with WaitAsync to limit concurrent operations:

C#
var semaphore = new SemaphoreSlim(10);
var tasks = items.Select(async item =>
{
    await semaphore.WaitAsync(ct);
    try   { return await ProcessAsync(item, ct); }
    finally { semaphore.Release(); }
});
await Task.WhenAll(tasks);

Or Parallel.ForEachAsync with MaxDegreeOfParallelism in .NET 6+.


Q73: What is IAsyncEnumerable<T> and when would you use it?

An async equivalent of IEnumerable<T> for streaming data. Producers yield return items asynchronously; consumers use await foreach. Use it when results are large or arrive incrementally — avoids loading everything into memory before processing begins.


EF Core

Q74: What is the N+1 query problem and how do you fix it?

Loading a list then accessing a navigation property for each item generates 1 query for the list + N queries for the relations.

Fix with Include (eager loading) or explicit loading. For projections, Select can avoid it entirely.

C#
// N+1 — bad
var orders = await dbContext.Orders.ToListAsync();
foreach (var o in orders)
    Console.WriteLine(o.Customer.Name); // query per order

// Fix — eager loading
var orders = await dbContext.Orders
    .Include(o => o.Customer)
    .ToListAsync();

Q75: What is the difference between Include and ThenInclude?

Include loads a direct navigation property. ThenInclude chains from the included navigation to load nested relations.

C#
dbContext.Orders
    .Include(o => o.Customer)
        .ThenInclude(c => c.Address)   // nested relation
    .Include(o => o.Items)
        .ThenInclude(i => i.Product)
    .ToListAsync();

Q76: What is the difference between eager, explicit, and lazy loading?

  • Eager — data loaded with the main query via Include. Best for known access patterns.
  • Explicit — manually load after the fact with Entry(entity).Collection(x => x.Items).LoadAsync(). Useful when you don't always need the related data.
  • Lazy — auto-loaded on access via proxies. Dangerous in web apps — generates silent N+1 queries.

Q77: What is a migration and what does dotnet ef migrations add do?

A migration is a C# class describing schema changes (create table, add column, etc.). migrations add compares your current model to the last snapshot and generates the diff. database update applies pending migrations as SQL.


Q78: What is the difference between SaveChanges and SaveChangesAsync?

Both flush tracked changes to the database. SaveChangesAsync is non-blocking — it awaits the database roundtrip, freeing the thread while waiting. Always use the async version in async web apps.


Q79: How does EF Core track changes?

The ChangeTracker records the original values of all loaded entities. On SaveChanges, it generates UPDATE statements only for changed properties. You can check tracking state with dbContext.Entry(entity).State.

To avoid tracking overhead for read-only queries, use .AsNoTracking().


Q80: What is AsNoTracking and when should you use it?

Disables change tracking for a query. EF Core doesn't store original values or monitor the entities for changes. Reduces memory and CPU overhead. Use for read-only operations (GET endpoints, reports) where you won't call SaveChanges on the result.


Dependency Injection

Q81: Explain the three DI lifetimes: Singleton, Scoped, Transient.

  • Singleton — one instance for the app lifetime. Shared across all requests and threads. Must be thread-safe.
  • Scoped — one instance per request (or DI scope). DbContext is always scoped.
  • Transient — new instance every time it's resolved. For lightweight, stateless services.

Q82: What is a captive dependency bug?

A Singleton that holds a Scoped dependency. The Scoped service is captured in the Singleton's constructor and never released — it outlives the request, causing stale data or resource leaks.

C#
// BAD — Singleton captures Scoped DbContext
public class CacheService // Singleton
{
    public CacheService(AppDbContext db) { } // DbContext is Scoped — captive!
}

// FIX — inject IServiceScopeFactory and create a scope explicitly
public class CacheService
{
    private readonly IServiceScopeFactory _scopeFactory;
    public CacheService(IServiceScopeFactory f) => _scopeFactory = f;

    public async Task RefreshAsync()
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        // use db safely
    }
}

Q83: What is the difference between AddSingleton, AddScoped, AddTransient?

They register a service with different lifetimes. Add<Interface, Implementation>() registers a concrete type behind an interface. You can also use Add<T>(factory) for custom construction logic.


Q84: How do you register multiple implementations of the same interface?

C#
services.AddScoped<INotificationSender, EmailSender>();
services.AddScoped<INotificationSender, SmsSender>();

// Inject IEnumerable<INotificationSender> — gets all registered implementations
public class NotificationService
{
    public NotificationService(IEnumerable<INotificationSender> senders) { }
}

Q85: What is IOptions<T> and how does it work?

Binds a configuration section to a strongly-typed class and injects it via DI.

C#
public class EmailSettings { public string SmtpHost { get; set; } = ""; }

// Register
builder.Services.Configure<EmailSettings>(
    builder.Configuration.GetSection("Email"));

// Inject
public class EmailService
{
    public EmailService(IOptions<EmailSettings> opts)
    {
        var settings = opts.Value;
    }
}

Authentication and Authorization

Q86: What is the difference between authentication and authorisation?

Authentication: verifying identity (who are you?). Authorisation: verifying permissions (what are you allowed to do?). In ASP.NET Core: UseAuthentication() runs first, then UseAuthorization().


Q87: How does JWT authentication work in ASP.NET Core?

  1. Client sends credentials → server validates → returns a signed JWT
  2. Client attaches JWT in Authorization: Bearer <token> header on subsequent requests
  3. ASP.NET Core middleware validates the signature and expiry
  4. Claims from the JWT are available in HttpContext.User

Q88: What are claims in a JWT?

Key-value pairs inside the JWT payload describing the user: sub (user ID), email, role, custom claims. Available as ClaimsPrincipal in your controller after authentication.


Q89: What is the difference between [Authorize] and [AllowAnonymous]?

[Authorize] requires the caller to be authenticated (and optionally authorised by role/policy). [AllowAnonymous] bypasses all authorisation for that action or controller — useful for login endpoints.


Q90: What is a policy-based authorisation?

Named requirements registered at startup and applied with [Authorize(Policy = "...")]. More flexible than role checks.

C#
builder.Services.AddAuthorization(opts =>
{
    opts.AddPolicy("AdminOnly", p => p.RequireRole("Admin"));
    opts.AddPolicy("MinAge18",  p => p.RequireClaim("age", "18+"));
});

Caching

Q91: What is the difference between in-memory and distributed caching?

In-memory cache lives in the process — fast, but lost on restart and not shared across instances. Distributed cache (Redis) is shared across all instances and survives restarts — required in multi-instance deployments.


Q92: What is cache-aside pattern?

  1. Check cache
  2. On miss: load from source (DB), store in cache, return
  3. On hit: return cached value
C#
var product = await _cache.GetAsync<Product>(key);
if (product is null)
{
    product = await _db.Products.FindAsync(id);
    await _cache.SetAsync(key, product, TimeSpan.FromMinutes(10));
}
return product;

Q93: What is cache invalidation and why is it hard?

Deciding when to remove or update cached data so stale data isn't served. It's hard because data changes may come from multiple sources, and invalidating too aggressively removes the performance benefit while not invalidating enough serves wrong data.

Common strategies: TTL-based expiry, event-driven invalidation (invalidate on write), versioned keys.


Q94: What is the difference between IMemoryCache and IDistributedCache?

IMemoryCache is process-local — fast, no serialization. IDistributedCache is external — requires serialization but is shared across instances. Use IDistributedCache whenever you have more than one app instance.


Clean Architecture

Q95: What are the layers in Clean Architecture?

Domain → Application → Infrastructure → Presentation.

  • Domain — entities, value objects, domain events, domain interfaces
  • Application — use cases, commands/queries (CQRS), application services, DTOs
  • Infrastructure — EF Core, external APIs, file system, message queues
  • Presentation — controllers, minimal API endpoints, SignalR hubs

Dependencies point inward — outer layers depend on inner layers, never the reverse.


Q96: What is the Dependency Inversion Principle in practice?

High-level modules don't depend on low-level modules — both depend on abstractions. In practice: your Application layer defines interfaces (IOrderRepository, IEmailService). Infrastructure implements them. Application never imports EF Core or SMTP libraries directly.


Q97: What is CQRS?

Command Query Responsibility Segregation — separates write operations (Commands) from read operations (Queries). Enables different models, performance optimisations, and scaling strategies for reads vs writes. Often implemented with MediatR in .NET.


Q98: What is MediatR and what problem does it solve?

MediatR is an in-process mediator. Instead of controllers calling services directly, they send a Command or Query object. MediatR routes it to the registered handler. Decouples controllers from business logic and enables pipeline behaviours (logging, validation, caching).


Q99: What is the Repository pattern and when would you not use it?

An abstraction over data access providing a collection-like interface. Useful for decoupling business logic from the ORM and for testability.

You might skip it if you're using EF Core as your abstraction (DbContext + DbSet is already a repository of sorts), or in CQRS where queries go directly to the database for performance.


System Design Basics

Q100: What is horizontal vs vertical scaling?

Vertical (scale up): add more CPU/RAM to one server. Simple but has limits and is expensive. Horizontal (scale out): add more servers behind a load balancer. Requires stateless apps (no in-memory session). Scales indefinitely in theory.


Q101: What makes an API stateless?

No server-side session — the server doesn't store client state between requests. All state needed to serve a request is in the request itself (JWT, request body, URL). Required for horizontal scaling.


Q102: What is a load balancer?

Distributes incoming traffic across multiple server instances. Ensures no single instance is overwhelmed. Common algorithms: round-robin, least connections, IP hash.


Q103: What is rate limiting and how is it implemented in ASP.NET Core?

Restricts the number of requests a client can make in a time window. Prevents abuse and protects downstream services.

C#
builder.Services.AddRateLimiter(opts =>
{
    opts.AddFixedWindowLimiter("api", o =>
    {
        o.PermitLimit        = 100;
        o.Window             = TimeSpan.FromMinutes(1);
        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    });
});
app.UseRateLimiter();

Q104: What is eventual consistency?

In distributed systems, after a write, replicas may not immediately reflect the change — but they will eventually. Contrast with strong consistency where every read sees the latest write. Message queues and event sourcing embrace eventual consistency.


Q105: What is idempotency and why is it important in APIs?

A request is idempotent if making it multiple times has the same effect as making it once. GET, PUT, DELETE should be idempotent. POST is not by default.

Important for retry logic — if a client retries a timed-out request, idempotency prevents duplicate side effects.


Additional Mid-Level Questions (Q106–Q160)

Q106: What is the Outbox pattern? Writing events to a database table in the same transaction as your business entity, then publishing from the outbox. Guarantees no lost events on DB+publish failures.

Q107: What is the Saga pattern? Coordinating a distributed transaction across multiple services using a sequence of local transactions and compensating actions on failure.

Q108: What is optimistic concurrency in EF Core? Using a RowVersion or ConcurrencyToken to detect conflicting updates. SaveChanges throws DbUpdateConcurrencyException if the row was modified since you loaded it.

Q109: What is a soft delete? Marking rows as deleted (e.g., IsDeleted = true) instead of physically removing them. Preserves history. EF Core Global Query Filters can transparently exclude soft-deleted rows.

Q110: What is the Specification pattern? Encapsulating a query condition in a named class — ActiveProductsSpec, OverdueOrdersSpec. Makes business rules reusable and composable in repository calls.

Q111: What is the Decorator pattern used for in .NET? Wrapping a service with additional behaviour without modifying it — logging, caching, retry. Scrutor makes registering decorators easy in DI.

Q112: What is the Result pattern? Returning a discriminated union (Result<T> / OneOf<T, Error>) instead of throwing exceptions for expected failures. Makes error handling explicit and avoids exception-driven flow.

Q113: What is FluentValidation? A library for building strongly-typed validation rules outside of model attributes. Validators are separate classes, testable, and composable.

Q114: What is the difference between IHostedService and BackgroundService? IHostedService is the interface (StartAsync/StopAsync). BackgroundService is an abstract base class implementing it, exposing ExecuteAsync for your long-running loop.

Q115: What is Polly? A resilience library — retry, circuit breaker, timeout, bulkhead policies for outgoing calls. Integrated with IHttpClientFactory via AddPolicyHandler.

Q116: What is a circuit breaker? A Polly policy that stops sending requests to a failing service after a threshold, giving it time to recover. States: Closed (normal), Open (blocking), Half-Open (testing recovery).

Q117: What is response caching vs output caching? Response caching adds Cache-Control headers — the client or proxy caches the response. Output caching is server-side — ASP.NET Core caches the response body in memory or Redis and serves it without running the controller.

Q118: What is minimal API? An alternative to controller-based APIs in ASP.NET Core — routes and handlers defined in Program.cs with app.MapGet(...), app.MapPost(...). Less ceremony for simple endpoints.

Q119: What is the difference between ILogger and ILogger<T>? ILogger<T> is a generic wrapper that sets the category to the full name of T — useful for filtering logs by class. Use ILogger<T> in most cases.

Q120: What is Serilog's structured logging? Using message templates with named placeholders rather than string interpolation. Properties are stored as structured data alongside the message, enabling filtering and aggregation in log platforms.

Q121: What is a health check? An endpoint (/health) that reports service status. Built into ASP.NET Core — checks DB connectivity, external dependencies, disk space. Used by load balancers and orchestrators (Kubernetes) to route traffic.

Q122: What is feature flags (feature toggles)? Configuration-driven switches that enable/disable features at runtime without deploying. Useful for A/B testing, dark launches, canary rollouts. Azure App Configuration and LaunchDarkly are common providers.

Q123: What is API versioning? Supporting multiple versions of your API simultaneously. Strategies: URL path (/v1/orders), query string (?api-version=1.0), header (Api-Version: 1). Prevents breaking changes for existing clients.

Q124: What is YARP? Yet Another Reverse Proxy — a .NET library for building API gateways and reverse proxies. Routes incoming requests to downstream services, supports load balancing, transforms, and auth.

Q125: What is the difference between REST and gRPC? REST uses HTTP+JSON — human-readable, broad compatibility. gRPC uses HTTP/2 + Protocol Buffers — binary, strongly typed, ~5–10x faster serialization. gRPC is better for internal service-to-service communication where performance matters.

Q126: What is OpenAPI and why generate it from code? OpenAPI is a machine-readable description of your API endpoints, parameters, and schemas. Generating it from code (controllers or minimal API) keeps docs in sync with implementation, and enables client code generation.

Q127: What is CORS and when does it apply? Cross-Origin Resource Sharing — browser security policy blocking requests from one origin to another. Only applies to browser-based clients. Configure it in ASP.NET Core with AddCors + UseCors.

Q128: What is the difference between Transient fault and a Permanent fault in resilience? Transient faults (network blip, momentary timeout) recover on retry. Permanent faults (bad request, auth failure) won't. Retry policies should only trigger on transient faults.

Q129: What is the Unit of Work pattern? Groups multiple repository operations into one transaction — either all succeed or all roll back. EF Core's DbContext is a built-in Unit of Work.

Q130: What is a domain event? An immutable record of something that happened in the domain (OrderPlaced, PaymentFailed). Published after the fact, consumed by handlers that trigger side effects in the same or other services.

Q131: What is the difference between commands and queries in CQRS? Commands mutate state and return nothing (or a minimal result). Queries read state and return data without side effects. Separating them allows different models and optimisations for each path.

Q132: What is AutoMapper / Mapster? Object-to-object mapping libraries that eliminate manual property assignment between types (Domain → DTO). Mapster is generally faster; AutoMapper is more widely adopted.

Q133: What is EF Core's change tracker and what is its cost? The change tracker records original values of loaded entities to detect changes. It costs memory and CPU — for read-only queries, .AsNoTracking() skips it.

Q134: What is a compiled query in EF Core? Pre-compiled LINQ queries that skip expression tree parsing on each call. Significant speedup for queries called thousands of times per second.

Q135: What is connection resiliency in EF Core? Automatic retry on transient database failures (network blips, timeout). Configured with .EnableRetryOnFailure() in the SQL Server provider.

Q136: What is the difference between string.Format, interpolation, and FormattableString? Interpolation ($"...") compiles to string.Format. FormattableString preserves the format template and arguments, usable for parameterised queries. Interpolation with SQL is a SQL injection risk — use FormattableString or raw string params.

Q137: What is record vs struct vs class for DTOs? record gives value equality, immutability, and with expressions — best for DTOs and value objects. struct avoids heap allocation but has copy semantics and constraints. class is the default mutable reference type.

Q138: What is a Span<T> and when would you use it in a web API? A stack-allocated view over contiguous memory. Use it in hot parsing paths (request body parsing, binary protocols) to avoid allocating intermediate arrays or strings.

Q139: What are CancellationToken best practices? Accept in every async I/O method, pass to EF Core and HttpClient calls, check ct.ThrowIfCancellationRequested() in CPU loops, catch OperationCanceledException at the boundary (it's not an error — it's a normal cancellation).

Q140: What is the difference between Task.Delay and Thread.Sleep? Thread.Sleep blocks the thread for the duration. await Task.Delay releases the thread for the duration. In async code, always use Task.Delay.

Q141: What is a race condition? Two threads read-modify-write shared state simultaneously, resulting in lost updates or corrupt state. Fix with locking (lock, SemaphoreSlim, Interlocked for atomics) or immutable data structures.

Q142: What is Interlocked used for? Atomic operations on shared variables without explicit locks. Interlocked.Increment, Decrement, Add, CompareExchange are lock-free and faster than lock for simple counters.

Q143: What is SemaphoreSlim vs Mutex? SemaphoreSlim is async-aware — WaitAsync doesn't block a thread while waiting. Mutex is a kernel-level primitive for cross-process locking. In .NET async code, always prefer SemaphoreSlim.

Q144: What is the ArrayPool<T>? A pool of reusable arrays — avoids repeated large array allocations and GC pressure. Use ArrayPool<byte>.Shared.Rent(size) / .Return(array) for temporary buffers in hot paths.

Q145: What is a memory-mapped file? Maps a file (or shared memory) directly into the process's address space. Enables efficient large-file processing and inter-process shared memory. Available in .NET via MemoryMappedFile.

Q146: What is IDisposable and when do you implement it? Implement it when a class holds unmanaged resources (file handles, connections) or expensive managed resources (DbContext, HttpClient) that should be released deterministically. Use using or await using to dispose.

Q147: What is finalizer and when should you use one? A finalizer (~ClassName) is called by the GC before reclaiming memory. Use only for cleaning up unmanaged resources when Dispose wasn't called. Always implement IDisposable alongside it and call GC.SuppressFinalize(this) in Dispose.

Q148: What is GC pressure and how do you reduce it? Excessive short-lived allocations that frequently trigger garbage collection. Reduce with object pooling (ArrayPool, ObjectPool), Span<T>, StringBuilder, and avoiding LINQ chains in hot paths.

Q149: What is the difference between Gen0, Gen1, Gen2 GC? Gen0: short-lived objects, collected frequently (milliseconds). Gen1: medium-lived, collected less often. Gen2: long-lived (singletons, large objects), collected rarely — Gen2 collections pause all threads and are expensive.

Q150: What is IHttpClientFactory and why is it important? Manages the lifetime of HttpMessageHandler instances, preventing socket exhaustion from creating many HttpClient instances. Supports named and typed clients with pre-configured policies.

Q151: What is Refit? A type-safe REST client library. Define an interface with HTTP method attributes; Refit generates the implementation. Works with IHttpClientFactory and Polly.

Q152: What is content negotiation? The process by which client and server agree on the response format. The client sends Accept: application/json or Accept: application/xml; ASP.NET Core responds in the requested format if a formatter is registered.

Q153: What is problem details? RFC 7807 — a standard format for HTTP error responses with type, title, status, detail, instance fields. ASP.NET Core uses it by default with [ApiController]. Keeps error responses consistent.

Q154: What is the difference between BadRequest() and throwing an exception? BadRequest() is intentional — returns a 400 immediately. Throwing an exception in an API action returns 500 by default (unless caught by middleware). Use BadRequest() for validation failures; exceptions for unexpected errors.

Q155: What is an EF Core interceptor? Hooks into EF Core operations (query execution, connection open, SaveChanges). Use for: soft delete, audit logging, slow query logging, modifying commands before execution.

Q156: What is a global query filter? An EF Core filter applied to all queries for a type. Common use: IsDeleted == false for soft deletes, TenantId == currentTenantId for multi-tenancy. Can be disabled per-query with .IgnoreQueryFilters().

Q157: What is vertical slice architecture? Organising code by feature rather than by layer. Each feature (slice) contains its own command, handler, validator, query, DTO, and data access code in one folder. Reduces cross-cutting changes and makes features independently navigable.

Q158: What is modular monolith? A single deployable application divided into loosely coupled modules with explicit, minimal interfaces between them. Easier to operate than microservices while preserving the ability to extract services later.

Q159: What is event sourcing? Storing the sequence of domain events that led to current state, rather than the current state itself. The current state is derived by replaying events. Provides full audit history and temporal queries.

Q160: What is the REPR pattern (Request-Endpoint-Response)? Each API endpoint is a distinct class with its own request and response types. Encourages small, focused handlers. Used in Minimal API and FastEndpoints library for .NET.

Enjoyed this article?

Explore the Backend Systems learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.