.NET Core & ASP.NET Core Interview Questions and Answers (2026)
/ 24 min read
Updated:Found this post helpful?
Buy me a coffeeTable of Contents
A while back I talked to folks from the #100devs boot camp, which got me thinking about the software development interviewing process. It had me wondering what questions I’d expect to get during a technical interview for an ASP.NET Core position. As a former hiring manager, I can tell you it was always challenging to gauge anyone’s skill set from a set of questions and interviews. So if you’re a candidate, your most significant investment is to help inform your interviewers of your skills long before entering the interview room. You can do that by learning in public, sharing ideas, and building demo projects. While I’m aware this isn’t possible for everyone, being able to do so can help you stand out.
For those going into an interview and wanting practice questions, I’ve gathered and expanded a comprehensive set of questions for a hypothetical .NET Core and ASP.NET Core position, along with how I might answer each one. None of these are meant to be “gotcha” questions but instead focus on foundational concepts I’d expect developers to know, mixed with some opinions I’d expect developers to have formed along the way.
OK, potential future employee, please have a seat, and let’s get started.
Updated: October 2026 — This post now covers .NET Core, ASP.NET Core, EF Core, and C# fundamentals, organized by topic so you can jump straight to the category you’re prepping for.
Table of Contents
- ASP.NET Core Fundamentals
- Dependency Injection
- Routing, MVC, and Minimal APIs
- Middleware, Filters, and Configuration
- Entity Framework Core & Data Access
- Async, Performance, and C# Fundamentals
- Security & Auth
- Debugging & Practical Scenarios
- Common .NET Interview Questions (FAQ)
ASP.NET Core Fundamentals
Question
“Why do you think ASP.NET Core is ideal for building web applications? If not, in your opinion, what’s missing?”
The hosting model of ASP.NET Core gives me several options and approaches for solving problems. I have several techniques to choose from, like ASP.NET Core MVC, Razor Pages, and Minimal APIs to solve any particular issue I may come upon. Hosting them in a singular solution also makes it easy to change my mind without throwing away the majority of my current progress.
Follow-up
“Can you name two or more approaches to handling a user request using ASP.NET Core? Which do you prefer and why?”
The most common approach to handling a user’s request will typically be utilizing an ASP.NET Core MVC action. For example, a user’s request might be parameters on a query string or part of a form body. In the case of MVC, model binding is generally transparent, and I can work with an instance of a strongly-typed request object. In addition, with ASP.NET Core MVC, I can reply with HTML, JSON, or other response types.
If I were starting a solution today, I might look into using Minimal APIs, as it lets me respond to a user’s request without the overhead of the ASP.NET Core MVC request pipeline. Minimal APIs bind from route values, the query string, headers, and the request body out of the box. Complex types are read from the JSON body by default, so binding other content types like form data sometimes takes a little more explicit wiring on my part. I could respond with any content type, but Minimal APIs favor JSON.
Question
“What’s the difference between .NET, .NET Core, and .NET Framework?”
.NET Framework is the original, Windows-only runtime that Microsoft no longer actively evolves beyond maintenance. .NET Core was the cross-platform rewrite, and starting with .NET 5, Microsoft dropped the “Core” branding and just calls it “.NET” with yearly version bumps (.NET 8, .NET 9, and so on). When people say “.NET Core” today in casual conversation or job postings, they almost always mean modern .NET, which is cross-platform, open source, and under active development, as opposed to legacy .NET Framework. Microsoft’s introduction to .NET is the canonical reference if you want the official framing.
Question
“What is the ASP.NET Core middleware pipeline, and how does it work?”
The middleware pipeline is an ordered sequence of components that each get a chance to inspect, modify, short-circuit, or pass along an incoming HTTP request and its outgoing response. I register middleware with app.Use... calls in the order I want them to execute, and each middleware can call the next one in line or stop the pipeline entirely, like returning a 404 before the request ever reaches my endpoints. Order matters a lot here: authentication has to run before authorization, and exception handling middleware usually needs to be registered first so it can catch errors from everything downstream. I walk through the mechanics in ASP.NET Core middleware registration.
Question
“Kestrel vs. IIS — what’s the relationship, and why does it matter?”
Kestrel is the cross-platform web server built into ASP.NET Core, and in most setups it’s what actually processes HTTP requests regardless of your operating system. I think of IIS (or Nginx, or Apache) as an optional reverse proxy that sits in front of Kestrel to handle things like TLS termination, process management, and serving multiple sites on one box.
There’s one nuance worth knowing. With IIS in-process hosting, the app runs inside the IIS worker process and uses the IIS HTTP Server (IISHttpServer) instead of Kestrel. With out-of-process hosting, IIS sits in front of a Kestrel process via the ASP.NET Core Module. You can run Kestrel directly in production now, but most teams still put a reverse proxy in front for hardening and because it’s a familiar operational pattern. The Kestrel web server docs go deep on the configuration knobs.
Question
“What is the generic host, and why did ASP.NET Core adopt it?”
The generic host (WebApplication.CreateBuilder under the hood) is the unified startup model that gives any app, web or otherwise, access to dependency injection, configuration, logging, and lifetime management from a single builder. Before this, web apps and background services had separate hosting models with duplicated plumbing. Now a console worker service and an ASP.NET Core API can share almost identical startup code, which I appreciate because it means less context-switching between project types. I dug into the modern builder in Adding Clarity To .NET Minimal Hosting APIs.
Dependency Injection
Question
“ASP.NET Core has dependency injection to manage services; are you aware of the different lifetimes? What are they, and what does each mean?”
ASP.NET Core has three lifetimes of Singleton, Scoped, and Transient.
ASP.NET Core services container will create services registered as a singleton only once for the duration of the application’s lifetime. Singletons are helpful for expensive services or services with little to no internal state.
As the name suggests, with regard to scoped services, they are created within a scope. The scope is typically the lifetime of an HTTP request, but not necessarily always. I, as a developer, might create custom scopes in code, but anyone should be careful to use this technique sparingly.
Finally, ASP.NET Core creates transient services each time a dependent instance asks for them. I often reach for transient as a sensible default because each consumer gets its own fresh instance, which sidesteps a lot of accidental shared-state bugs. It won’t save you from everything, though. If the service itself touches shared state (a static field, a singleton it depends on, a database), you can still hit races or deadlocks, and creating many short-lived instances costs performance and memory.
Each lifetime has its use, and it depends on the dependency we are registering. I broke these down with examples in ASP vNext Dependency Injection Lifecycles.
Question
“What problem does dependency injection actually solve?”
Dependency injection removes the responsibility of constructing dependencies from the class that uses them, which decouples my code from concrete implementations and makes it far easier to test and swap behavior. Instead of a class new-ing up its own database connection or HTTP client, it declares what it needs in its constructor, and the container figures out how to build it. This is also why I’m a fan of keeping constructors simple — if a class needs ten dependencies, that’s usually a sign it’s doing too much.
Question
“How do you register and resolve services in ASP.NET Core, and what’s a ‘captive dependency’?”
I register services against an interface or type in Program.cs using AddSingleton, AddScoped, or AddTransient, and the framework resolves them automatically when a constructor asks for that type. A captive dependency happens when a longer-lived service (like a singleton) holds onto a shorter-lived one (like a scoped DbContext) — the scoped instance gets “captured” past its intended lifetime, which can cause stale data or threading bugs. Injecting a scoped service into a singleton is the classic offender (the container even throws by default in development when it detects this); injecting a transient can be fine, but only if that transient doesn’t itself capture something scoped. When a singleton genuinely needs scoped work, I inject IServiceScopeFactory and create a fresh scope on demand.
Question
“What are keyed services in .NET 8, and when would you use them?”
Keyed services let me register multiple implementations of the same interface under different string keys and resolve the right one with [FromKeyedServices("key")] or GetRequiredKeyedService. I’d reach for this when I have, say, three different INotificationSender implementations for email, SMS, and push, and I want the container to pick the right one without me hand-rolling a factory pattern. Before .NET 8 I would have solved this with a dictionary of named strategies or a custom factory; keyed services make the intent more explicit. The .NET dependency injection docs cover the keyed APIs in full.
Routing, MVC, and Minimal APIs
Question
“You can map routes to endpoints explicitly or through convention; which do you prefer and why?”
I prefer explicitly registering routes, as they are visible in the codebase and often easier to rationalize and debug. There is a potential drawback though, as the more routes an application has, the more it can impact route resolution and performance of an application. Performance degradation can happen in applications when an extreme amount of route registrations occur, but many folks shouldn’t worry about it until they notice a drop in performance.
Follow-up
“ASP.NET Core MVC is a design pattern; what classes and files might you create if you built an application vertical to manage vehicles?”
Given that we’re talking about Vehicles as a resource, I would start by creating a Vehicle model, representing my data source. I’d likely then move on to creating a VehiclesController with actions for listing vehicles, getting a single vehicle, creating and updating a vehicle, and finally removing a vehicle. If this is an HTML app, I will generate view models with my Razor views. If this is an HTTP API, then response models to be serialized into JSON responses. There may be more classes to handle any additional necessary data to make the development experience easier for my team members and me.
Question
“MVC vs. Razor Pages vs. Minimal APIs — when would you reach for each?”
I reach for MVC when I’m building a traditional multi-controller HTML application with a lot of shared layout and cross-cutting behavior across actions. Razor Pages suits page-focused scenarios better, like a form, a dashboard, or a wizard, where I want the markup and its handler colocated instead of scattered across a controller and separate views. For lightweight HTTP APIs and microservices, I pick Minimal APIs: no full MVC pipeline, lowest overhead to get a JSON endpoint running.
Question
“What is model binding, and how does it differ between MVC and Minimal APIs?”
Model binding is the process ASP.NET Core uses to take raw request data (route values, query strings, form fields, JSON bodies) and populate a strongly-typed object my code can work with directly. In MVC, binding is transparent and works across many content types and sources out of the box, with attributes like [FromBody], [FromQuery], and [FromRoute] available when I need to be explicit. Minimal APIs support most of the same binding sources, but historically favored JSON bodies more heavily, so other content types often need more explicit handling. If you want to bridge the two worlds, I showed how in Using ASP.NET Core MVC Value Providers With Minimal APIs.
Question
“Attribute routing vs. conventional routing — what’s the practical difference?”
Attribute routing means I decorate each action or endpoint with its route template directly, like [HttpGet("/vehicles/{id}")], so the route is defined right next to the code that handles it. Conventional routing defines a pattern once, typically {controller}/{action}/{id}, and lets every matching controller and action fall into that template automatically. I lean toward attribute routing for APIs because it’s explicit and easy to grep for, while conventional routing can save repetition in larger MVC apps with very regular URL structures. I walked through a concrete example in How To Map A Route in an ASP.NET Core MVC application.
Middleware, Filters, and Configuration
Question
“What other libraries or frameworks might you use with ASP.NET Core to build your application, and for what purposes?”
Since most applications need to store data, I’d likely reach for Entity Framework Core or Dapper for data access. In addition, I’m also a big fan of FluentValidation to make validating user input easier to understand. Finally, I might use a front-end library like HTMX to leverage Razor’s HTML-generating capability to provide a compelling client-side user experience.
Follow-up
“You have configuration values needed to access your application resources. Which configuration providers do you prefer for development, and which do you prefer for production?”
Many cloud providers and docker hosting platforms support environment variables, so environment variables make much sense for production environments. However, I prefer the user secrets configuration provider for local development as there’s no way to add sensitive secrets to source control mistakenly. Finally, I like JSON configuration since it’s one of the default configuration options, and it’s easy to add and manage into source control. I’ve written in more depth about reading configuration safely with the IOptions pattern if you want the full breakdown.
Question
“What’s the difference between middleware and filters, and when would you choose one over the other?”
Middleware operates at the HTTP pipeline level and, when registered in the main pipeline, typically runs for every request, though pipeline order or branching (UseWhen, Map) can short-circuit or bypass it. Filters (action filters, exception filters, and so on) hook into stages of the MVC action-invocation pipeline, like before and after a controller action runs. I use middleware for cross-cutting, app-wide concerns like logging, exception handling, or compression, and filters when I need something scoped to specific controllers or actions, like custom model validation or authorization checks tied to MVC conventions. Minimal APIs have their own analog in endpoint filters, so the “filters are MVC-only” line isn’t quite true anymore. I covered that newer surface in ASP.NET Core Minimal API Endpoint Filters.
Question
“How do ASP.NET Core’s configuration providers resolve precedence when multiple sources define the same key?”
Configuration providers are layered in the order I register them, and later providers override earlier ones for any key they both define. The typical default order is appsettings.json, then appsettings.{Environment}.json, then user secrets in development, then environment variables, then command-line arguments. That’s also roughly the order of least to most likely to be an intentional override at deploy or runtime. It lets me set a sane default in JSON and override just what’s different per environment without duplicating the whole file. I rounded up the providers I reach for most in Top Five Configuration Options For .NET Core.
Question
“What’s theIOptionspattern, and why not just injectIConfigurationeverywhere?”
The IOptions pattern binds a section of configuration into a strongly-typed C# class, so my services depend on a typed options object instead of string-keyed lookups scattered throughout the codebase. Injecting raw IConfiguration everywhere works, but it pushes magic strings and parsing logic into business code and makes it harder to spot what configuration a given class actually needs. I go into the tradeoffs between IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T> in my post on ASP.NET Core IOptions configuration.
Entity Framework Core & Data Access
Question
“What is EF Core, and what is ‘change tracking’?”
EF Core is Microsoft’s object-relational mapper (ORM) that lets me work with database rows as C# objects instead of hand-writing SQL for every operation. Change tracking is the mechanism EF Core uses to notice which loaded entities have been modified since they were retrieved, so that calling SaveChanges only issues UPDATE statements for the fields that actually changed. It’s convenient, but it isn’t free, since tracking has real memory and CPU overhead on larger result sets. If you’re wiring it up for the first time, I have a walkthrough on how to use Entity Framework Core in ASP.NET Core.
Question
“What doesAsNoTracking()do, and when should you use it?”
AsNoTracking() tells EF Core not to bother tracking the entities returned by a query, since I don’t intend to modify and save them back. I reach for it on any read-only query — list pages, reports, API responses — because skipping the change-tracking overhead can meaningfully cut memory use and improve throughput. I’ve measured this directly and written up how disabling object tracking improved EF Core performance if you want the numbers.
Question
“Lazy loading vs. eager loading vs. explicit loading — what’s the difference?”
Eager loading uses .Include() to fetch related data up front, which is my default choice because it avoids surprise round-trips later. By default EF Core builds this as a single query with JOINs, but I can opt into split queries (AsSplitQuery()) so related collections come back through multiple SQL queries — handy for avoiding the “cartesian explosion” of row duplication when including several one-to-many relationships. Lazy loading defers fetching related data until a navigation property is actually accessed, which reads nicely but is also the classic source of the N+1 query problem if I’m not careful in a loop. Explicit loading sits in between: I load the main entity first and then deliberately call .Load() on a specific navigation property only when I know I need it. The EF Core guide to loading related data lays out all three strategies side by side.
Question
“What’s the N+1 problem, and how do you spot it?”
The N+1 problem happens when I run one query to get a list of N records, and then — because of lazy loading or a loop calling the database per item — EF Core fires off N additional queries to fetch related data, turning one intended query into N+1. I spot it by watching the SQL EF Core actually generates (via logging or a tool like MiniProfiler) and looking for a suspicious number of near-identical queries firing in a tight loop. The fix is almost always to eager-load the related data up front with .Include() instead of touching navigation properties inside a loop.
Question
“What are EF Core migrations, and how do they fit into a deployment pipeline?”
Migrations are code-generated, versioned diffs that EF Core produces by comparing my current DbContext model against the model snapshot it stored from the previous migration — not by inspecting the live database — letting me evolve the database in lockstep with my C# code instead of hand-writing schema change scripts. I generate one with dotnet ef migrations add, review the generated SQL before trusting it blindly, and apply it with dotnet ef database update or by running pending migrations at startup. In a real deployment pipeline I prefer generating a SQL script with dotnet ef migrations script and running that explicitly, rather than letting the app apply migrations automatically on boot. The EF Core migrations docs cover the full command surface.
Question
“What’s the typical lifetime of aDbContext, and why?”
DbContext is usually registered as Scoped, matching the lifetime of a single HTTP request, because it’s a lightweight, short-lived unit of work that tracks changes for that one request’s worth of operations. By default EF Core opens a database connection only when it needs one — to execute a query or save changes — and closes it again right after, rather than holding a connection open for the whole request. I avoid holding onto a DbContext across requests or sharing one instance across threads, since it isn’t thread-safe, and long-lived contexts accumulate tracked entities and become a memory and correctness problem. When the registration itself trips people up, I wrote up how to fix “Unable to resolve DbContextOptions” for EF Core.
Async, Performance, and C# Fundamentals
Question
“Why is async/await so important in ASP.NET Core web applications specifically?”
Async/await lets a thread get released back to the thread pool while waiting on I/O — a database call, an HTTP request, a file read — instead of sitting blocked and doing nothing. In a web server handling many concurrent requests, that difference is what lets a relatively small thread pool serve a large number of simultaneous connections without exhausting threads under load. I treat async all the way through the call stack as the default for anything that touches the network or disk.
Question
“What doesawaitactually do under the hood?”
await doesn’t block the calling thread; instead, it schedules the rest of the method as a continuation to run once the awaited task completes, and returns control to the caller immediately. The compiler rewrites an async method into a state machine that resumes at the right point when the awaited operation finishes, usually back on a thread-pool thread. That’s also why mixing .Result or .Wait() with async code is dangerous: it defeats the whole point and can cause deadlocks. Microsoft’s asynchronous programming guide is the best deep dive on the model.
Question
“What’s a common cause of deadlocks in async .NET code, and how do you avoid it?”
The classic deadlock comes from blocking synchronously on an async call — calling .Result or .Wait() on a task — in a context that has a captured synchronization context, like older ASP.NET or UI apps, where the continuation needs that same context to resume but it’s stuck waiting. The real fix is to keep async code async end to end and never block on a task with .Result/.Wait() in the first place. ConfigureAwait(false) helps in library code by not capturing the context, but it’s not a reliable rescue for a synchronous block — any await further up the chain that does capture the context can still deadlock — so I treat it as good library hygiene, not a license to block. Modern ASP.NET Core doesn’t have a synchronization context by default, which removes a lot of this class of bug, but it’s still a common interview gotcha because of how often it bites people on older codebases.
Question
“How would you go about finding and fixing a slow endpoint in production?”
I’d start with whatever APM or tracing tool is already wired into the app — Application Insights, OpenTelemetry traces, or even structured logs with timing — to see where the time is actually going before guessing. Usually it’s one of three things: a database query without the right index or doing an N+1, a synchronous call blocking on I/O, or a dependency on an external service with no timeout. Once I know which layer is slow, I profile that specific layer rather than trying to optimize the whole request path at once.
Question
“What caching options does ASP.NET Core give you, and how do you choose?”
In-memory caching (IMemoryCache) is my default for single-instance apps or data that’s fine being recomputed per-instance, since it’s fast and requires no extra infrastructure. Distributed caching (IDistributedCache backed by Redis or SQL Server) is what I reach for once an app is scaled out across multiple instances and needs a shared cache so users don’t get inconsistent results depending on which instance they hit. Response caching and output caching sit at a different layer entirely, caching whole HTTP responses, and are great for public, rarely-changing content. The caching overview lays out when each one fits.
Security & Auth
Question
“What’s the difference between authentication and authorization?”
Authentication answers “who are you” — verifying identity, typically through a login flow, token, or cookie. Authorization answers “what are you allowed to do” and happens after authentication has already established identity, checking roles, claims, or policies against the resource being accessed. I’ve seen candidates conflate the two, and it’s a quick way to tell whether someone has actually implemented security versus just read about it. I go deeper on the policy side in Customize The Authorization Pipeline In ASP.NET Core.
Question
“How is authentication typically wired up in an ASP.NET Core app?”
I register one or more authentication schemes (cookies, JWT bearer, an external OIDC provider) with AddAuthentication() in the service container, then add the UseAuthentication() and UseAuthorization() middleware in the right order in the pipeline. From there, I decorate endpoints or controllers with [Authorize], optionally with policies or roles, to enforce access rules, and ASP.NET Core handles challenging unauthenticated users automatically based on the configured scheme. For a concrete end-to-end example, I wrote up adding GitHub OpenID auth for ASP.NET Core apps.
Question
“Where should application secrets live, and where shouldn’t they?”
Secrets should never live in appsettings.json committed to source control, which is one of the fastest ways to leak credentials. Locally, I use the .NET user secrets tool, which keeps secrets outside the repo entirely; in production, I favor environment variables or a managed secret store like Azure Key Vault or AWS Secrets Manager, depending on the hosting platform. Microsoft’s guide to safe storage of app secrets in development covers the user secrets workflow in detail.
Debugging & Practical Scenarios
Question
“You have a page with a form, but when you submit, nothing occurs. How would you go about debugging the issue?”
I always find it’s best to start at the beginning. In my case, I clicked a submit button on a form. But, first, I would open the dev tools in my browser and make sure the client page made a network call to the backend ASP.NET Core app.
If the page didn’t make a request, I would ensure that my form has an action attribute pointing at a known endpoint in my application and that the method matches what the app expects on the endpoint.
If the client made a request, before leaving the dev tools of the client, I would read any responses sent from the server to pinpoint the exact point of failure. A “not found” response would lead me to believe the endpoint isn’t registered in my app correctly. If the endpoint does exist, I will look at any route constraints, filters, or exceptions that may stop the request from getting to my endpoint. The typical response, in this case, is a “bad request” response. An excellent place to look is the output window of my IDE to see if any error messages are visible.
Methodically starting from the origin of the problem and working backward is an excellent way to fix issues and quickly move on to more work.
Common .NET Interview Questions (FAQ)
What are the most common .NET interview questions?
Expect questions on dependency injection and its lifetimes, the middleware pipeline, the difference between .NET, .NET Core, and .NET Framework, EF Core’s change tracking, async/await mechanics, and at least one practical debugging scenario. Most interviewers, myself included, mix a few conceptual questions with one or two “walk me through how you’d solve this” scenarios.
Which is better, ASP.NET or ASP.NET Core?
ASP.NET Core is the better choice for essentially all new development — it’s cross-platform, faster, actively developed, and has a leaner, more modular pipeline than the older ASP.NET (also called ASP.NET Framework). Classic ASP.NET still shows up in legacy codebases that haven’t migrated yet, but Microsoft’s ongoing investment is entirely in ASP.NET Core.
What is the difference between .NET Core and .NET Framework?
.NET Core is cross-platform (Windows, macOS, Linux), open source, and under active development with yearly releases, while .NET Framework is Windows-only and in maintenance mode with no new feature development. If you’re starting a new project today, .NET Core (now just called “.NET”) is the right choice unless you have a hard dependency on a Windows-only legacy API that hasn’t been ported.
What are common C# .NET interview questions?
Beyond the ASP.NET Core-specific questions above, expect C# fundamentals like value types vs. reference types, the difference between IEnumerable and IQueryable, how pattern matching and records work, nullable reference types, and when to use async/await correctly. These tend to come up regardless of whether the role is ASP.NET Core, a console app, or a class library.
Conclusion
The questions and answers in this post are some I might expect in hiring a .NET Core or ASP.NET Core developer. Some are technical pieces of knowledge a developer might run into during a day of development. Others explore opinions that might form from using libraries and tools in the ecosystem, and a handful dig into EF Core and C# fundamentals that show up regardless of which flavor of .NET role you’re interviewing for. While there are certainly no “gotcha” questions, in my opinion, yours might be different. So, what questions have you been asked during an interview, or what are some questions you might ask another developer to gauge their understanding and expertise?
Follow me on Mastodon, share this post with your question, and I’ll see if I can respond to them. As always, thanks for reading and sharing my posts with friends and colleagues.