Let's tackle these ASP.NET Core questions topic by topic:
1. ASP.NET Core Basics
Q1: What are the key differences between ASP.NET and ASP.NET Core?
- Answer: ASP.NET Core is a cross-platform, high-performance framework, while ASP.NET (the older version) is Windows-only. ASP.NET Core is designed for cloud-based and internet-connected applications, with a modular and lightweight architecture. Key differences include:
- Cross-platform support (Windows, macOS, Linux).
- Built-in dependency injection.
- Unified framework for MVC and Web API.
- No longer depends on System.Web, resulting in better performance.
Q2: How does middleware work in the ASP.NET Core request pipeline?
- Answer: Middleware components are assembled into a pipeline that handles HTTP requests. Each middleware component:
- Can handle an incoming request.
- Can decide whether to pass the request to the next middleware in the pipeline.
- Can perform operations after the downstream middleware has completed processing.
Middleware is configured in
Startup.cs
via theConfigure
method.
Q3: What is the purpose of Startup.cs
in an ASP.NET Core application?
- Answer:
Startup.cs
is the entry point of an ASP.NET Core application and is responsible for:- Configuring services (via
ConfigureServices
method). - Configuring the HTTP request pipeline (via
Configure
method). It defines how the application behaves and responds to HTTP requests.
- Configuring services (via
Q4: How can you configure dependency injection in ASP.NET Core?
- Answer: Dependency injection is configured in the
ConfigureServices
method inStartup.cs
. Services are added usingAddSingleton
,AddScoped
, orAddTransient
methods depending on the desired lifetime (singleton, scoped, transient). Example:
1 | services.AddScoped<IMyService, MyService>(); |
Q5: Explain the role of appsettings.json
and how to read configuration values from it.
- Answer:
appsettings.json
is a file used to store configuration settings in JSON format, including database connections, API keys, and environment-specific configurations. These settings can be accessed using theIConfiguration
interface:
1 2 3 4 5 6 7 8 9 | public class MyClass { private readonly IConfiguration _config; public MyClass(IConfiguration config) { _config = config; } public void PrintConfigValue() { var myValue = _config["MyKey"]; } } |
2. Routing and Endpoints
Q1: How does attribute routing differ from conventional routing in ASP.NET Core?
- Answer: Attribute routing uses attributes on controller actions to define routes, while conventional routing is defined globally in the
Startup.cs
file. Attribute routing provides more control at the individual action level:
1 2 | [Route("products/{id}")] public IActionResult GetProduct(int id) { } |
Q2: How can you define custom route constraints in ASP.NET Core?
- Answer: Custom route constraints can be defined using regular expressions or by implementing
IRouteConstraint
. Example using a regular expression:
1 2 | [Route("products/{id:regex(^\\d{{4}}$)}")] public IActionResult GetProduct(int id) { } |
Q3: What is endpoint routing, and how does it improve routing in ASP.NET Core 3.0+?
- Answer: Endpoint routing decouples routing decisions from middleware execution. It improves performance by determining the endpoint before middleware is invoked, allowing middleware components to interact with the selected endpoint more efficiently.
Q4: How do you create and use route parameters in an MVC action method?
- Answer: Route parameters can be used to capture values from the URL and pass them to the action method. For example:
1 2 3 4 | [Route("products/{id}")] public IActionResult GetProduct(int id) { // id is captured from the route and passed as an argument } |
Q5: How can you handle route-specific errors in ASP.NET Core?
- Answer: Route-specific errors can be handled using exception filters, middleware, or custom error pages. For example, use
UseExceptionHandler
middleware for global exception handling:
1 | app.UseExceptionHandler("/Home/Error");
|
3. Model Binding and Validation
Q1: How does ASP.NET Core bind form data to controller action parameters?
- Answer: ASP.NET Core uses model binding to map form data (from query strings, route data, and form posts) to action method parameters. It binds data to simple types (int, string) and complex types (classes).
Q2: What are the different ways to perform input validation in ASP.NET Core?
- Answer: Input validation can be performed using:
- Data Annotations (e.g.,
[Required]
,[Range]
,[EmailAddress]
). - FluentValidation (a more flexible validation library).
- Custom validation attributes or logic in the controller.
- Data Annotations (e.g.,
Q3: How can you create custom validation attributes in ASP.NET Core?
- Answer: Custom validation attributes can be created by inheriting from
ValidationAttribute
and overriding theIsValid
method:
1 2 3 4 5 6 | public class CustomEmailAttribute : ValidationAttribute { protected override bool IsValid(object value) { var email = value as string; return email != null && email.EndsWith("@example.com"); } } |
Q4: Explain how you would use FluentValidation
in an ASP.NET Core project.
- Answer:
FluentValidation
is used to create validation logic in a more fluent and customizable way. Install theFluentValidation.AspNetCore
package, and create a validator:
1 2 3 4 5 | public class ProductValidator : AbstractValidator<Product> { public ProductValidator() { RuleFor(p => p.Name).NotEmpty().WithMessage("Name is required."); } } |
Q5: How does model binding handle complex objects and collections?
- Answer: ASP.NET Core can bind complex objects and collections by recursively binding properties. For example:
1 2 3 | public IActionResult CreateOrder(Order order) { // ASP.NET Core binds nested objects like Order.Customer automatically } |
4. Dependency Injection (DI)
Q1: What are the different service lifetimes (Scoped, Transient, Singleton) in ASP.NET Core DI?
- Answer:
- Singleton: The service is created once and shared across the entire application lifetime.
- Scoped: The service is created once per request (or scope) and shared within that request.
- Transient: A new instance of the service is created every time it is requested.
Q2: How would you inject a service into a middleware?
- Answer: To inject a service into middleware, use constructor injection or the
Invoke
method. The service must be registered inStartup.cs
. Example:
1 2 3 4 5 6 7 8 9 10 11 12 | public class MyMiddleware { private readonly RequestDelegate _next; private readonly IMyService _myService; public MyMiddleware(RequestDelegate next, IMyService myService) { _next = next; _myService = myService; } public async Task Invoke(HttpContext context) { // Use the service await _next(context); } } |
Q3: Explain how you can implement multiple constructors in a class using DI.
- Answer: ASP.NET Core's DI system selects the constructor with the most parameters that it can resolve. If you have multiple constructors, ensure that at least one of them has all dependencies resolvable by DI. You can manually select a constructor by modifying the service registration.
Q4: How do you handle circular dependencies in ASP.NET Core DI?
- Answer: Circular dependencies occur when two or more services depend on each other, leading to a deadlock. You can resolve this by:
- Refactoring the code to remove circular dependencies.
- Using lazy loading (
Lazy<T>
orFunc<T>
) to delay instantiation.
Q5: What is the benefit of using IServiceProvider
in ASP.NET Core?
- Answer:
IServiceProvider
is used to resolve services manually, typically in scenarios where DI isn't automatically handled (e.g., within middleware). It allows dynamic resolution of dependencies at runtime.
5. Middleware
Q1: How can you create a custom middleware component in ASP.NET Core?
- Answer: Custom middleware can be created by implementing a class with an
Invoke
orInvokeAsync
method, and registering it in theStartup.cs
file. Example:
1 2 3 4 5 6 7 8 9 10 | public class MyCustomMiddleware { private readonly RequestDelegate _next; public MyCustomMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { // Middleware logic here await _next(context); } } |
Q2: Explain the order of middleware execution in the ASP.NET Core pipeline.
- Answer: Middleware components execute in the order they are registered in the
Startup.Configure
method. Each middleware can either:- Handle the request and prevent further processing.
- Pass the request to the next middleware by calling
_next(context)
.
Q3: What are the benefits of using the built-in UseExceptionHandler
middleware?
- Answer:
UseExceptionHandler
allows centralized handling of exceptions in ASP.NET Core. It ensures that any unhandled exceptions are caught and can be logged or redirected to a custom error page, improving user experience and simplifying error management.
Q4: How does the Run
, Use
, and Map
methods differ in middleware configuration?
- Answer:
- Use: Registers a middleware and passes control to the next component in the pipeline.
- Run: Registers a terminal middleware, meaning it handles the request and doesn’t call the next middleware.
- Map: Branches the middleware pipeline based on the request path.
Q5: How would you implement middleware to handle request logging?
- Answer: Create custom middleware to log request details (e.g., URL, headers) and register it in the pipeline:
1 2 3 4 5 | public async Task Invoke(HttpContext context) { var request = context.Request; Console.WriteLine($"Request URL: {request.Path}"); await _next(context); // Call the next middleware } |