The Rapid-Fire ASP.NET Core Interview Questions

Poster
Posted by in .NET Core category on for Intermediate level | Points: 250 | Views : 661 red flag

Here are 10 rapid fire ASP.NET Core Interview questions that every developer should know. This is the part 1 of the Rapid Fire round.

1. IMiddleware vs Convention-based Middleware

Think of it like: A freelancer (convention) vs a full-time employee (IMiddleware).

Convention-based middleware is a simple class relying on a specific method name (InvokeAsync) to run. You must manually manage its dependencies, which can lead to messy code. IMiddleware forces you to implement a strict interface, allowing ASP.NET Core’s Dependency Injection (DI) system to handle it cleanly. Think of convention-based as a freelancer you have to micromanage, while IMiddleware is a structured, full-time employee who plugs right into your system automatically.

  • Convention-based — just a class with an InvokeAsync method. You wire it up manually.
  • IMiddleware — implements an interface, gets injected automatically by DI.
// Convention-based - you manage everything
public class MyMiddleware {
    public async Task InvokeAsync(HttpContext context, RequestDelegate next) { }
}

// IMiddleware - DI does the heavy lifting
public class MyMiddleware : IMiddleware {
    public async Task InvokeAsync(HttpContext context, RequestDelegate next) { }
}
Remember: IMiddleware = cleaner + DI-friendly.

2. Singleton vs Scoped vs Transient

Think of it like a coffee shop:

Service lifetimes control how long an object lives in memory. Singleton creates one instance for the entire app's lifespan (like a single coffee machine for a whole office). Scoped creates one instance per HTTP request (like a fresh cup of coffee for a single customer visit). Transient creates a brand-new instance every single time it is requested (like a brand new paper cup every time you ask for water). Mixing them improperly causes bugs like "captive dependencies."

Lifetime Analogy When to use
Singleton One coffee machine for the whole shop — forever Shared config, caching
Scoped Fresh cup per customer visit (per HTTP request) DB context, unit of work
Transient New cup every single time you ask Lightweight, stateless services

Gotcha: Never inject a Scoped service into a Singleton — the Scoped one gets "trapped" and never refreshed. This is the captive dependency trap.

3. Not awaiting an async action

Think of it like: Sending a letter and walking away before checking if it was delivered.

When you call an asynchronous method without using the await keyword, you trigger a "fire-and-forget" scenario. The application fires the task and immediately moves to the next line of code without waiting for a result. If that background task fails or throws an exception, the application will never catch it, causing errors to silently vanish. Always use await to ensure errors are handled and the task finishes properly.

// ? Fire and forget — exceptions silently disappear
public async Task<IActionResult> DoWork() {
    SomeAsyncMethod(); // no await!
    return Ok();
}

// ? Proper awaiting
public async Task<IActionResult> DoWork() {
    await SomeAsyncMethod();
    return Ok();
}
Remember: No await = no error handling. The request finishes, but your task runs in the dark.

4. Minimal APIs vs Controllers

Think of it like: A food truck (Minimal APIs) vs a full restaurant (Controllers).

Minimal APIs are a lightweight, streamlined way to build HTTP APIs with practically zero boilerplate code. They allow you to map endpoints directly in your main application file, making them perfect for small microservices or fast performance. Traditional Controllers offer a structured, class-based approach with built-in features like automatic model validation and filters. Choose Minimal APIs for small, fast routes, and Controllers for complex, heavy architectures.

Feature Minimal APIs Controllers
Setup 3 lines Boilerplate class
Best for Small APIs, microservices Complex apps with filters, validation
Model validation ? Not automatic ? Built-in
// Minimal API — dead simple
app.MapGet("/hello", () => "Hello World!");

// Controller — more structure
[ApiController]
[Route("[controller]")]
public class HelloController : ControllerBase {
    [HttpGet] public string Get() => "Hello World!";
}
Remember: Small & fast ? Minimal. Complex & structured ? Controllers.

5. IActionFilter vs Middleware

Think of it like: Airport security (Middleware) vs your flight attendant (Filter).

Middleware is broad; it sits at the very front of your application pipeline and processes every single incoming request—even requests for static images or CSS files. It knows nothing about your specific C# methods. An IActionFilter is specific to MVC and Web APIs; it executes right before and after your Controller actions run. Use Middleware for global tasks like logging, and Filters when you need to inspect controller parameters or model state.

  • Middleware — runs for every request, even static files. It's the pipeline.
  • IActionFilter — runs only for MVC/API actions. Knows about controllers, actions, model state.
// Middleware - broad, knows nothing about MVC
app.Use(async (context, next) => { await next(); });

// ActionFilter - MVC-aware, knows the action being called
public class MyFilter : IActionFilter {
    public void OnActionExecuting(ActionExecutingContext context) { }
    public void OnActionExecuted(ActionExecutedContext context) { }
}
Remember: Need model state or action name? Use Filter. Otherwise, Middleware.

6. IHostedService

Think of it like: A background worker at a restaurant washing dishes — customers never see them, but they keep things running.

IHostedService allows you to run long-running background tasks inside your ASP.NET Core application without requiring an external windows service or cron job. It starts up when the application launches and runs silently in the background, independently of incoming HTTP requests. It is perfect for handling internal maintenance tasks like clearing out old database logs, polling a message queue, or running scheduled data syncs.

public class MyBackgroundService : BackgroundService {
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
            // Do background work (cleanup, polling, queue processing)
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
}

// Register it
builder.Services.AddHostedService<MyBackgroundService>();
Remember: IHostedService = background tasks that run alongside your app, not triggered by HTTP requests.

7. appsettings.json Override Order

Think of it like: Layers of a sandwich — the top layer wins.

ASP.NET Core loads configuration settings in hierarchical layers, where the last layer loaded overrides anything before it. It starts with the base appsettings.json file, then overlays the environment-specific file (like appsettings.Production.json). Next, it applies Environment Variables, and finally, Command-line arguments. Think of it like building a sandwich: whatever layer you place on top covers up and replaces what was directly underneath it.

appsettings.json              ? base (bottom)
appsettings.Production.json   ? overrides base
Environment Variables         ? overrides file
Command-line args             ? wins over everything (top)
// Reads in this exact order — last one wins
builder.Configuration
    .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{env}.json")
    .AddEnvironmentVariables()
    .AddCommandLine(args);
Remember: Think "layers" — environment-specific always beats the base file.

8. ProblemDetails

Think of it like: A standard error receipt — every shop gives you the same format so you always know what went wrong.

ProblemDetails is a standardized machine-readable format (based on the RFC 7807 internet standard) for returning errors from an API. Without it, developers write custom error formats, making it frustrating for front-end clients to handle API failures consistently. By using ProblemDetails, your API guarantees that every error response—whether a 400 Bad Request or a 500 Server Error—comes back in the exact same structured format every time.

// ? Consistent error response
return Problem(
    title: "Validation Failed",
    detail: "Email is required",
    statusCode: 400
);

// What the client gets back:
// { "type": "...", "title": "Validation Failed", 
//   "status": 400, "detail": "Email is required" }
Remember: ProblemDetails = your API's standard error receipt. Clients always know what to expect.

9. Preventing Overposting

Think of it like: A hotel check-in form — the guest should only fill in name and dates, not their room price.

Overposting occurs when a malicious user sends extra form fields or JSON data that your application wasn't expecting, which automatically bind to your database models (e.g., passing IsAdmin = true on a profile update page). To prevent this security risk, you should never bind HTTP requests directly to your internal database entities. Instead, use Data Transfer Objects (DTOs) to strictly whitelist only the specific fields a user is allowed to change.

// ? Dangerous — binds everything including IsAdmin
public IActionResult Update(User user) { }

// ? Option 1 — Use a DTO (only expose what you want)
public IActionResult Update(UpdateUserDto dto) { }

// ? Option 2 — [Bind] whitelist
public IActionResult Update([Bind("Name,Email")] User user) { }
Remember: Never bind directly to your DB model. Use a DTO — it's your bouncer.

10. UseRouting vs UseEndpoints

Think of it like: A GPS (UseRouting) vs actually driving the route (UseEndpoints).

In ASP.NET Core, request processing is split into two distinct phases. UseRouting acts as the GPS—it looks at the incoming URL and calculates exactly which piece of code (endpoint) matches the request. UseEndpoints is the driver—it actually executes that matched endpoint. They are kept separate so that security middleware, like authentication and authorization, can inspect the destination route before the code is allowed to execute.

  • UseRouting — figures out which endpoint matches the request.
  • UseEndpoints — executes that matched endpoint.
// Classic .NET 5 style — explicit separation
app.UseRouting();
app.UseAuthorization(); // ? must go between these two!
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });

// .NET 6+ — simplified, but same concept under the hood
app.MapControllers(); // routing + endpoint in one
Remember: The reason they're separate is so middleware like Authorization can run after routing but before execution. In .NET 6+, it's mostly automatic.
Page copy protected against web site content infringement by Copyscape

About the Author

Poster
Full Name: Ms poster
Member Level:
Member Status: Member
Member Since: 8/29/2007 10:18:25 AM
Country:


-- Hobby for DotNet

Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)