Master these 31 carefully curated interview questions to ace your next Dotnet interview.
.NET is a cross-platform framework. CLR (Common Language Runtime) manages memory, security, and execution of .NET programs.
.NET supports C#, F#, VB.NET. CLR provides: garbage collection, JIT compilation (IL to native code), type safety, exception handling, thread management. .NET Core → .NET 5+ is cross-platform. NuGet is the package manager.
ASP.NET Core is a cross-platform web framework for building APIs, web apps, and microservices with C#.
Features: middleware pipeline, dependency injection (built-in), Razor Pages, MVC, Web API, Blazor (WebAssembly), SignalR (real-time). Kestrel web server. Configuration: appsettings.json, environment variables. Modern: minimal APIs (.NET 6+), native AOT compilation (.NET 8).
EF Core is .NET's ORM mapping C# classes to database tables with LINQ queries and automatic migrations.
Code-first: define C# models, generate migrations. DbContext: database session. LINQ: type-safe queries. Supports: SQL Server, PostgreSQL, SQLite, MySQL. Features: lazy/eager/explicit loading, change tracking, migrations, raw SQL. Alternatives: Dapper (micro-ORM, faster for simple queries).
DI is built into ASP.NET Core: register services in Program.cs, inject via constructors with Scoped/Transient/Singleton lifetimes.
Lifetimes: Transient (new each time), Scoped (per request), Singleton (app lifetime). Register: builder.Services.AddScoped<IService, Service>(). Inject: constructor injection. Benefits: testability, loose coupling, configurable. AutoFac/Scrutor for advanced scenarios. Interface-based for abstraction.
async/await enables non-blocking I/O operations, freeing threads to handle other requests while waiting for results.
async marks method as asynchronous, await suspends until Task completes without blocking thread. Task/Task<T> represent async operations. ConfigureAwait(false) for library code. Avoid async void (except event handlers). Parallel: Task.WhenAll for concurrent operations. IAsyncEnumerable for streaming. Deadlock risk with .Result in sync context.
Use controllers or minimal APIs, EF Core for data, JWT authentication, middleware pipeline, and Swagger documentation.
Architecture: (1) Minimal API or controllers. (2) EF Core + repository pattern. (3) DTOs with AutoMapper. (4) JWT auth with ASP.NET Identity. (5) Middleware: logging, exception handling, CORS. (6) Swagger/OpenAPI documentation. (7) Rate limiting. (8) Health checks. (9) Docker deployment.
.NET compiles to IL (Intermediate Language), then JIT compiles to native code on each platform via platform-specific runtimes.
.NET 5+ unified: Windows, Linux, macOS, mobile (MAUI). CoreCLR: cross-platform runtime. RyuJIT: JIT compiler. Native AOT (.NET 8): compile directly to native code, no JIT needed. Mono runtime for mobile/WebAssembly. Single codebase, multiple platforms.
.NET Framework is Windows-only legacy; .NET Core is cross-platform; .NET 5+ unified both into one cross-platform platform.
.NET Framework: Windows-only, mature, WinForms/WPF/ASP.NET. .NET Core: cross-platform, open-source, high-performance rewrite. .NET 5+: unified platform combining Framework and Core — cross-platform, open-source. .NET 6 LTS, .NET 7, .NET 8 LTS are latest. Migration: new projects should use .NET 8+. ASP.NET Core replaces ASP.NET MVC/Web API. Entity Framework Core replaces EF6. MAUI replaces Xamarin for mobile.
Value types store data directly on stack (int, struct); reference types store a reference to heap-allocated data (class, string).
Value types: int, double, bool, char, struct, enum — stored on stack, copied on assignment, no null (unless nullable int?). Reference types: class, string, array, delegate, interface — stored on heap, variable holds reference, can be null. Boxing: value type → object (heap allocation). Unboxing: object → value type. struct vs class: struct is value type (small, immutable data), class is reference type. string is immutable reference type with value-type semantics. Span<T> and stackalloc for stack-allocated arrays.
LINQ (Language Integrated Query) provides SQL-like query syntax for collections, databases, XML, and other data sources.
Method syntax: list.Where(x => x.Age > 18).Select(x => x.Name).OrderBy(x => x). Query syntax: from x in list where x.Age > 18 select x.Name. Deferred execution: query isn't executed until enumerated (ToList(), foreach). Providers: LINQ to Objects (in-memory), LINQ to Entities (EF/database), LINQ to XML. Common: Where, Select, OrderBy, GroupBy, Join, Any, All, Count, Sum, First, Single, Skip, Take. Performance: avoid multiple enumerations, use AsNoTracking() for read-only EF queries. IEnumerable vs IQueryable: IQueryable translates to SQL.
ASP.NET Core has built-in DI container — register services in Program.cs, inject via constructors with three lifetimes.
Registration: builder.Services.AddTransient<IService, Service>() — new instance every time. AddScoped — one per HTTP request. AddSingleton — one for app lifetime. Injection: constructor injection (preferred), [FromServices] in action methods. Keyed services (.NET 8): multiple implementations of same interface. Options pattern: IOptions<T> for configuration. Hosted services: IHostedService for background tasks. Validation: ValidateOnStart() catches missing registrations early. Avoid: service locator anti-pattern (don't resolve from IServiceProvider manually).
EF Core is an ORM that maps C# classes to database tables, enabling LINQ queries translated to SQL with change tracking.
Code-First: define C# classes → generate database schema via migrations. DbContext: unit of work + repository. DbSet<T>: represents table. LINQ translates to SQL. Change tracking: tracks entity modifications, SaveChanges() generates INSERT/UPDATE/DELETE. Migrations: dotnet ef migrations add, dotnet ef database update. Loading: eager (Include), lazy (proxies), explicit (Load). Performance: AsNoTracking for read-only, compiled queries, bulk operations. Relationships: navigation properties, Fluent API configuration. Interceptors for cross-cutting concerns (logging, soft delete).
async/await enables non-blocking I/O operations on the same thread pool; threading creates separate OS threads for parallel execution.
async Task<T> MethodAsync(): marks method as asynchronous. await: suspends method until operation completes, releases thread. Task: represents async operation. Task.Run: offloads CPU work to thread pool. async void: only for event handlers (no error propagation). ConfigureAwait(false): avoid capturing synchronization context in libraries. ValueTask: avoids allocation for synchronous completions. Channels: async producer-consumer. IAsyncEnumerable: async streaming. Deadlock trap: .Result/.Wait() in sync context — use await instead. Cancellation: CancellationToken for cooperative cancellation.
Middleware are components in the request pipeline that process HTTP requests/responses in sequence, each calling the next.
Pipeline: app.Use(async (context, next) => { /* before */ await next(); /* after */ }). app.Map: branch pipeline by path. app.UseWhen: conditional middleware. Built-in: UseRouting, UseAuthentication, UseAuthorization, UseStaticFiles, UseCors, UseExceptionHandler. Order matters: exception handling first, auth before authorization. Terminal middleware: app.Run (doesn't call next). Custom: implement IMiddleware or use convention-based. Short-circuit: respond without calling next() (e.g., auth failure). Endpoint middleware: MapGet/MapPost minimal APIs.
Delegates are type-safe function pointers; events are delegate-based notifications; lambdas are inline anonymous functions.
Delegate: delegate int Operation(int a, int b). Built-in: Func<T,TResult> (returns value), Action<T> (void), Predicate<T> (returns bool). Events: event EventHandler<TArgs> OnChanged — publisher-subscriber pattern, only declaring class can invoke. Lambda: (x, y) => x + y — concise anonymous function. Expression trees: Expression<Func<T,bool>> — code as data (EF Core translates to SQL). Multicast delegates: combine multiple handlers. Local functions: nested named functions (closures). Top-level statements (C# 9+): simpler program entry.
.NET uses generational mark-and-sweep GC with three generations (0, 1, 2) and a Large Object Heap for objects over 85KB.
Generations: Gen 0 (short-lived, collected frequently), Gen 1 (buffer), Gen 2 (long-lived, collected rarely). LOH: objects >= 85KB, collected with Gen 2. GC modes: Workstation (low latency) vs Server (high throughput, per-processor heaps). Background GC: concurrent Gen 2 collection. Finalization: ~Destructor runs on finalizer thread — unpredictable timing. IDisposable: deterministic cleanup with using statement. GC.Collect(): force collection (avoid in production). Span<T>, stackalloc, ArrayPool<T>: reduce heap allocations. Memory profiling: dotMemory, PerfView, VS Diagnostic Tools.
SignalR provides real-time bidirectional communication over WebSockets with automatic fallback to SSE and long polling.
Server: Hub class with methods clients can call. Client: connection.on('ReceiveMessage', handler). Transport negotiation: WebSocket → Server-Sent Events → Long Polling (automatic fallback). Groups: add clients to named groups for targeted broadcasting. Strongly-typed hubs: interface defines client methods. Scaling: Redis backplane or Azure SignalR Service. Authentication: JWT or cookie-based. Streaming: IAsyncEnumerable for server-to-client streams. Use cases: chat, notifications, live dashboards, collaborative editing, gaming. MessagePack protocol for smaller payloads.
Use Identity framework for user management, JWT for API auth, policy-based authorization, and role/claims-based access control.
Authentication: ASP.NET Core Identity (cookie-based), JWT Bearer tokens (API), OAuth2/OpenID Connect (external providers). Setup: AddAuthentication().AddJwtBearer(). Authorization: [Authorize] attribute, policy-based: AddPolicy('Admin', p => p.RequireClaim('role', 'admin')), resource-based with IAuthorizationHandler. Claims: key-value pairs in token/cookie. Roles: [Authorize(Roles='Admin')]. Custom: IAuthorizationRequirement + handler. Identity Server / Duende for OAuth2 server. Refresh tokens for long-lived sessions.
Use response caching, async I/O, minimal APIs, output caching, efficient serialization, and proper EF Core queries.
Steps: (1) Async everywhere: async controllers, async EF queries. (2) Response caching: [ResponseCache]. (3) Output caching (.NET 7+): server-side response cache. (4) EF Core: AsNoTracking, compiled queries, projection (Select), avoid N+1 (Include). (5) Minimal APIs for high-throughput endpoints. (6) System.Text.Json over Newtonsoft (faster). (7) Connection pooling for databases. (8) Memory: ArrayPool, Span<T>, avoid LOH. (9) gzip/Brotli compression. (10) Rate limiting middleware. (11) Health checks and monitoring. Profile with: dotnet-counters, dotnet-trace, Application Insights.
Minimal APIs allow defining HTTP endpoints with minimal code using top-level statements, MapGet/MapPost, without controllers.
Syntax: app.MapGet('/users/{id}', (int id, UserService svc) => svc.GetUser(id)). Features: automatic parameter binding (route, query, body, DI), filters (endpoint filters), grouping (MapGroup), OpenAPI support. vs Controllers: less ceremony, better for microservices and simple APIs, no model binding attributes needed. Validation: use FluentValidation or custom filters. Auth: RequireAuthorization(). Performance: slightly faster than controllers (less middleware). TypedResults: return Results.Ok(user) for OpenAPI schema generation. Ideal for microservices and small APIs.
ASP.NET Core offers in-memory cache, distributed cache (Redis), response caching, and output caching for different scenarios.
IMemoryCache: in-process, fast, lost on restart. IDistributedCache: Redis, SQL Server, NCache — shared across instances. Response caching: HTTP cache headers (Cache-Control), CDN-friendly. Output caching (.NET 7+): server-side, tag-based invalidation, more control than response caching. Cache patterns: cache-aside (check cache, if miss fetch and store), cache stampede prevention (SemaphoreSlim). Hybrid cache (.NET 9): combines L1 (memory) + L2 (distributed). Expiration: absolute (fixed time), sliding (resets on access). Always plan for cache invalidation — the hardest problem in CS.
Records are immutable reference types with value equality; pattern matching enables concise conditional logic based on shape and type.
Records (C# 9): record Person(string Name, int Age) — value equality, with-expressions for copies, positional deconstruction. record struct (C# 10): value type records. Pattern matching: is pattern (obj is string s), switch expressions (shape switch { Circle c => c.Radius, _ => 0 }), property patterns ({ Name: 'John', Age: > 18 }), relational patterns (> 0 and < 100), list patterns (C# 11: [1, 2, ..rest]). Combined with records: powerful for domain modeling, state machines, command handling.
Use Exception Handling Middleware, ProblemDetails for API errors, IExceptionHandler (.NET 8), and logging with Serilog.
Approaches: (1) app.UseExceptionHandler for global catch. (2) ProblemDetails (RFC 7807): standardized error response format. (3) builder.Services.AddProblemDetails() auto-maps exceptions. (4) IExceptionHandler (.NET 8): typed exception handling. (5) Custom middleware for logging and transformation. (6) Result pattern: avoid exceptions for expected failures (FluentResults). (7) Logging: Serilog with structured logging, correlation IDs. (8) ModelState validation: automatic 400 with ValidationProblemDetails. Best practice: throw exceptions for unexpected failures, return Result<T> for expected ones.
IEnumerable executes queries in-memory (LINQ to Objects); IQueryable translates queries to the data source (e.g., SQL via EF).
IEnumerable<T>: in-memory iteration, all data loaded first, LINQ methods execute in C#. IQueryable<T>: extends IEnumerable, builds expression tree, translated to provider-specific query (SQL). Example: dbContext.Users.Where(u => u.Age > 18) as IQueryable generates WHERE SQL. As IEnumerable loads ALL users then filters in memory. Performance: use IQueryable to push filtering to database. ToList()/ToArray() materializes query. AsEnumerable() switches from IQueryable to in-memory. Return IEnumerable from repositories, keep IQueryable internal.
gRPC is a high-performance RPC framework using Protocol Buffers for service-to-service communication, faster than REST for internal APIs.
Protocol Buffers: strongly-typed schema (.proto files), binary serialization (smaller than JSON). Code generation: proto files → C# client/server code. Streaming: unary, server-streaming, client-streaming, bidirectional. HTTP/2: multiplexing, header compression. ASP.NET Core: Grpc.AspNetCore package. Benefits: type safety, performance, streaming, generated clients. Limitations: no browser support (use gRPC-Web), harder to debug (binary), not human-readable. Use for: microservice communication, real-time streaming, high-performance internal APIs. Use REST for: public APIs, browser clients.
.NET uses a layered configuration system with appsettings.json, environment variables, user secrets, and the Options pattern.
Sources (priority order): command line > env variables > user secrets > appsettings.{Environment}.json > appsettings.json. builder.Configuration binds from all sources. Options pattern: services.Configure<SmtpSettings>(config.GetSection('Smtp')). Inject: IOptions<T> (singleton), IOptionsSnapshot<T> (scoped, reloads), IOptionsMonitor<T> (singleton, change notifications). Validation: ValidateDataAnnotations(), ValidateOnStart(). User secrets: dotnet user-secrets for dev credentials. Azure Key Vault, AWS Secrets Manager for production. Never store secrets in appsettings.json or source control.
Use IHostedService/BackgroundService for in-process tasks, or Hangfire/Quartz.NET for persistent job scheduling.
BackgroundService: override ExecuteAsync for long-running tasks (polling, queue consumers). IHostedService: StartAsync/StopAsync lifecycle. Channels: in-process async queue between request handler and background service. Timer-based: PeriodicTimer for scheduled work. Hangfire: persistent job storage (SQL), dashboard UI, retries, scheduled/recurring jobs. Quartz.NET: cron-based scheduling. Worker Service: standalone background service project. Considerations: graceful shutdown (CancellationToken), error handling, health checks, scaling. For heavy workloads: message queues (RabbitMQ, Azure Service Bus).
Blazor builds interactive web UIs with C# instead of JavaScript. Server model runs on server via SignalR; WASM runs in browser.
Blazor Server: C# runs on server, UI updates via SignalR WebSocket — fast initial load, requires persistent connection, server resources per user. Blazor WebAssembly: .NET runtime in browser via WASM — true SPA, offline capable, larger initial download. Blazor United (.NET 8): auto mode starts server-side, transitions to WASM after download. Components: .razor files with C# + HTML. State: cascading parameters, DI, browser storage. JS Interop: call JavaScript from C# and vice versa. Benefits: share code between server and client, C# everywhere, no JavaScript needed.
Source generators analyze code at compile time and generate additional C# source files, enabling compile-time metaprogramming.
Part of Roslyn compiler pipeline. ISourceGenerator/IIncrementalGenerator: inspect syntax/semantic model, output new .cs files. Use cases: JSON serialization (System.Text.Json), regex compilation, DI registration, mapping (Mapperly), validation. Benefits: no runtime reflection (faster startup, AOT-compatible), compile-time errors, generated code is debuggable. Examples: [JsonSerializable] generates serialization code, [GeneratedRegex] compiles regex at build time. vs Reflection: source generators are faster (no runtime cost), AOT-friendly, but more complex to write.
Task always allocates on heap; ValueTask avoids allocation when result is available synchronously, improving hot-path performance.
Task<T>: reference type, always heap-allocated. Fine for most async operations. ValueTask<T>: struct, avoids heap allocation when result is immediately available (cache hit, buffered read). Rules: (1) Default to Task<T>. (2) Use ValueTask when profiling shows allocation pressure. (3) ValueTask can only be awaited ONCE. (4) Cannot use .Result before completion. (5) Don't cache ValueTask. IValueTaskSource: custom pooled async state machine for high-performance scenarios. Performance: ValueTask matters in hot paths called millions of times (network I/O, database). In typical web apps, Task<T> is perfectly fine.
Ready to master Dotnet?
Start learning with our comprehensive course and practice these questions.