As .NET Core continues to be a leading framework for building high-performance, scalable applications in 2025, recruiters must identify developers who can effectively work within its ecosystem. With its cross-platform capabilities, modular architecture, and cloud-native support, .NET Core is a preferred choice for modern web and enterprise applications.
This resource, "100+ .NET Core Interview Questions and Answers," is designed to help recruiters evaluate candidates efficiently. It covers essential topics ranging from fundamentals to advanced concepts such as dependency injection, middleware, asynchronous programming, microservices, and integration with cloud services.
Whether hiring junior developers or experienced engineers, this guide enables you to assess a candidate’s:
For a streamlined assessment process, consider platforms like WeCP, which allow you to:
✅ Create tailored .NET Core assessments based on job roles.
✅ Include real-world coding challenges and scenario-based questions.
✅ Conduct remote proctored tests to maintain assessment integrity.
✅ Leverage AI-powered analysis for quick and accurate evaluation.
Save time, improve hiring efficiency, and confidently recruit .NET Core developers who can build robust, scalable applications from day one.
.NET Core is a cross-platform, high-performance framework for building modern cloud-based, internet-connected applications. Unlike the .NET Framework, which runs only on Windows, .NET Core supports Windows, macOS, and Linux, making it more versatile for cross-platform development.
You can download the .NET Core SDK from the official .NET website. Follow the installation guide for your specific operating system (Windows, macOS, or Linux).
The dotnet new command creates a new .NET Core project with a specific template (e.g., console app, web app, etc.). For example, dotnet new console creates a new console application.
.NET Core is designed to be platform-independent. It uses a runtime (CoreCLR) that can run on different operating systems. The same .NET Core application can run on Windows, macOS, and Linux without modification.
The CLR is the runtime environment in .NET Core that manages the execution of .NET applications, providing services like memory management, exception handling, and garbage collection.
A typical .NET Core project contains files like Program.cs (entry point), Startup.cs (configuration), and appsettings.json (configuration settings). It also includes directories like Controllers, Views, wwwroot, and dependencies listed in the csproj file.
Run the command dotnet new mvc -n ProjectName to create a new ASP.NET Core MVC project. Then, navigate to the project directory and run dotnet run to launch the project.
Kestrel is a cross-platform web server for ASP.NET Core applications. It is used to serve HTTP requests and can run behind other web servers like IIS or Nginx, or as a standalone server.
.NET Core has built-in support for dependency injection (DI) through the IServiceCollection and IServiceProvider interfaces. You can register services in the Startup.cs file using methods like AddSingleton, AddScoped, and AddTransient.
Middleware is software that handles requests and responses in an ASP.NET Core application. Middleware components are executed in a pipeline, where each component can either pass the request to the next component or terminate the pipeline.
Routing in ASP.NET Core MVC maps incoming requests to controller actions. Routes are defined in the Startup.cs file, typically using the UseEndpoints or MapControllerRoute methods.
API controllers are specifically used for building Web APIs. They return data (e.g., JSON) rather than views. API controllers use the [ApiController] attribute to enable features like automatic model validation.
appsettings.json is a configuration file used in .NET Core applications to store settings like database connection strings, environment settings, and more. You can access these settings through the IConfiguration interface in .NET Core.
Entity Framework Core (EF Core) is an Object-Relational Mapping (ORM) framework that allows developers to interact with databases using .NET objects, eliminating the need for most SQL queries.
Add a connection string to appsettings.json, then configure it in Startup.cs using the AddDbContext method. You can then inject the DbContext into your application to interact with the database.
The IConfiguration interface provides access to configuration settings, allowing you to read key-value pairs from files like appsettings.json, environment variables, or command-line arguments.
NuGet is a package manager for .NET. NuGet packages are libraries that you can add to your project to use third-party or shared code. You can install them via the dotnet add package command.
The try-catch block is used to handle exceptions. You place the code that might throw an exception inside the try block and handle the exception in the catch block.
Program.cs is the entry point of a .NET Core application. It contains the Main method, which starts the application by calling CreateHostBuilder and setting up the host.
Startup.cs configures the services and the request pipeline for the application. It defines the ConfigureServices method for DI configuration and Configure method for request processing pipeline configuration.
To enable HTTPS, configure your project to use HTTPS in launchSettings.json. Also, add middleware like UseHttpsRedirection in the Startup.cs file.
.NET Core provides built-in logging support through the ILogger interface. You can inject ILogger into controllers or services and log information using methods like LogInformation, LogWarning, and LogError.
Model binding is the process of mapping incoming request data (such as form data or query parameters) to action method parameters.
Data annotations are attributes applied to model properties to enforce validation. For example, [Required], [Range], and [StringLength] are used for validation. These attributes are checked automatically by the model binder.
A RESTful API follows REST principles like stateless communication, resource representation, and HTTP methods. You can create a RESTful API in ASP.NET Core by creating controllers that return data (usually JSON) and use HTTP methods like GET, POST, PUT, and DELETE.
You can use HttpClient to consume a Web API. Create an instance of HttpClient, send a request using GetAsync or PostAsync, and process the response.
You can manage cookies using the HttpContext.Response.Cookies.Append method to set cookies and HttpContext.Request.Cookies to read them.
Filters allow you to run code before or after an action method executes. Examples include authorization filters, resource filters, action filters, and exception filters.
ASP.NET Core supports authentication through Identity, JWT, or OAuth. You configure authentication in Startup.cs and use middleware to handle login, registration, and token validation.
Razor is a templating engine used to generate dynamic HTML in MVC views. It allows you to embed C# code into HTML using the @ symbol.
Use the Use Static Files middleware in Startup. cs to serve static files from the www root folder.
A View Component is a reusable component that renders part of a page. It is similar to partial views but allows for more complex logic.
Return a JSON result from an action method using return J son(object) or return Ok(object) in an API controller.
Use the I Form File interface to handle file uploads. In the controller, read the uploaded file, process it, and save it to a specified location.
Tag Helpers are server-side components that generate HTML and provide a way to interact with Razor views. Examples include asp-for, asp-controller, and asp-action.
Use the dotnet publish command to compile and package the application for deployment. You can also specify the target runtime (e.g., Windows, Linux) during the publishing process.
Global exception handling can be implemented using middleware. You create custom middleware that catches exceptions and handles them centrally, ensuring that all unhandled exceptions are captured.
Dependency Injection (DI) is a design pattern used to implement IoC (Inversion of Control), allowing you to manage the dependencies of classes. In .NET Core, DI is a first-class citizen, and the framework provides built-in support for it.
To implement DI in .NET Core:
Register services: You define the service in the Startup.cs file, inside the ConfigureServices method. You can register services with different lifetimes like Transient, Scoped, or Singleton.
Example:
public void ConfigureServices(IServiceCollection services)
{
// Register IMyService with MyService as a dependency
services.AddTransient<IMyService, MyService>();
}
Inject services: You inject the registered services into a class, typically via the constructor.
Example:
public class MyController : Controller
{
private readonly IMyService _myService;
// Constructor Injection
public MyController(IMyService myService)
{
_myService = myService;
}
public IActionResult Index()
{
var result = _myService.DoSomething();
return View(result);
}
}
Resolve services: The framework automatically resolves and provides the dependency at runtime based on what’s registered in the service container.
This approach reduces tight coupling, improves testability (by injecting mock services in unit tests), and makes your code cleaner and more maintainable.
In ASP.NET Core, services can be registered with different lifetimes, depending on how they should behave during the application's lifecycle. There are three main lifetimes:
Transient:
Example:
services.AddTransient<IMyService, MyService>();
Use case: For services that maintain a global state, such as configuration settings or logging mechanisms.
Choosing the right lifetime depends on the service's behavior and how it manages state. For instance, services that don’t need to share state should be transient, while services that manage state for the application should be singletons.
In ASP.NET Core, service registration is handled within the Configure Services method in the Startup. cs file. You use the I Service Collection interface to register your dependencies. There are three common types of service lifetimes: Transient, Scoped, and Singleton (as described above), and each type is registered by calling a corresponding method on I Service Collection.
Example of registering services:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Register a transient service
services.AddTransient<IEmailService, EmailService>();
// Register a scoped service
services.AddScoped<IUserService, UserService>();
// Register a singleton service
services.AddSingleton<ILogger, Logger>();
// Register framework services
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Configure the middleware pipeline
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Steps:
This approach ensures that your application’s dependencies are centralized and easy to manage, while supporting the core principles of DI and IoC.
Both IActionResult and ActionResult are used as return types for ASP.NET Core controller actions, but there are some key differences:
IActionResult:
Example:
public IActionResult MyAction()
{
return View(); // ViewResult
}
Action Result:
Example:
public ActionResult MyAction()
{
return Ok(); // Returns a 200 OK status with a response body.
}
When to use which?
Model validation is the process of ensuring that incoming data from HTTP requests conforms to the rules defined in your data models. ASP.NET Core provides a built-in mechanism to validate models using data annotations.
Defining validation rules: Use data annotations to apply validation rules to your model properties:
public class UserModel
{
[Required(ErrorMessage = "Username is required")]
[StringLength(50, MinimumLength = 3,
ErrorMessage = "Username must be between 3 and 50 characters")]
public string Username { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid email format")]
public string Email { get; set; }
}
Handling validation in the controller: In your controller action, you can check if the model is valid using ModelState.IsValid:
public IActionResult Register(UserModel userModel)
{
if (ModelState.IsValid)
{
// Process the valid model
return RedirectToAction("Success");
}
// If the model is invalid, return the same view with validation messages
return View(userModel);
}
Displaying validation messages in the view: In the Razor view, use HTML helpers to display validation messages:
<form asp-action="Register">
<input asp-for="Username" />
<span asp-validation-for="Username" class="text-danger"></span>
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span>
<button type="submit">Register</button>
</form>
Custom validation attributes: If the built-in validation attributes are not sufficient, you can create custom validation attributes by inheriting from Validation Attribute:
public class CustomValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid
(object value, ValidationContext validationContext)
{
// Custom logic
return ValidationResult.Success;
}
}
Model validation ensures that data is clean and reliable before it is processed by the application, helping to prevent errors and inconsistencies.
Middleware in ASP.NET Core is software that sits between the incoming HTTP request and the outgoing HTTP response. It is responsible for handling and processing HTTP requests, and each piece of middleware can either:
Middleware components are executed in the order they are registered in the Startup.cs file, and each one has the option to terminate the request by generating its own response or delegate the request to the next middleware.
Example:
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Middleware to log requests
app.Use(async (context, next) =>
{
Console.WriteLine("Incoming Request: " + context.Request.Path);
await next.Invoke(); // Pass to the next middleware
Console.WriteLine("Outgoing Response: " + context.Response.StatusCode);
});
// Static files middleware
app.UseStaticFiles();
// Routing middleware
app.UseRouting();
// Endpoint middleware
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
In this example, the first middleware logs the request and response. It calls next.Invoke() to pass control to the next middleware in the pipeline (static files, routing, etc.).
Middleware is highly customizable and plays a central role in configuring the HTTP request pipeline in ASP.NET Core.
Routing in ASP.NET Core is a mechanism to map incoming HTTP requests to the appropriate controller actions or endpoints. Routing can be convention-based or attribute-based.
Convention-based routing: You define routing patterns in the Startup.cs file using UseRouting() and UseEndpoints(). The routing pattern can include parameters for controllers, actions, and route variables.
Example:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
This routing pattern maps requests to the appropriate controller and action, with an optional id parameter.
Attribute routing: You can use attributes to define routing directly on controllers or actions. This allows for more fine-grained and flexible routing.
Example:
[Route("products")]
public class ProductsController : Controller
{
[Route("details/{id}")]
public IActionResult Details(int id)
{
// Return product details based on id
return View();
}
}
This route will match /products/details/{id}, where id is a route parameter.
Routing is a key component in directing incoming requests to the right controller or action, and ASP.NET Core allows you to mix both convention-based and attribute-based routing in the same application.
ASP.NET Core provides several mechanisms for handling exceptions: Developer Exception Page: For development environments, you can use the developer exception page to display detailed error information.
This is useful for debugging.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
Exception Handling Middleware: In production environments, you use exception handling middleware to catch and handle exceptions globally. You can redirect to a custom error page, log the error, or return a generic response.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
}
In this case, any unhandled exceptions will be caught and the user will be redirected to the /Home/Error route.
Custom exception handling middleware: You can also create custom middleware to handle exceptions and log them.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
// Log exception
await context.Response.WriteAsync("An error occurred.");
}
});
Global Exception Filters: In MVC, you can handle exceptions globally by using exception filters.
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
context.Result = new RedirectToActionResult("Error", "Home", null);
}
}
This filter can be applied globally to handle exceptions in all actions and controllers.
Exception handling ensures that errors are properly logged, and users are presented with user-friendly error messages instead of seeing raw exception details.
In ASP.NET Core, both Use and Run are methods used to add middleware to the HTTP request pipeline, but they serve different purposes.
Use:
Example:
app.Use(async (context, next) =>
{
// Perform some logic
Console.WriteLine("Inside Use Middleware");
await next(); // Pass control to the next middleware
});
Run:
Example:
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello from Run Middleware");
});
In this case, no other middleware registered after Run will be executed, as Run terminates the pipeline.
Key difference:
Filters in ASP.NET Core MVC are used to execute custom logic before or after certain stages in the request pipeline, such as before an action is executed or after a result is returned. Filters provide a way to implement cross-cutting concerns like logging, authorization, caching, and exception handling.There are several types of filters:
Authorization Filters:
Custom Authorization Filter:
public class CustomAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// Custom authorization logic
}
}
Action Filters:
Example:
public class CustomActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context) { }
}
Exception Filters:
Example:
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
context.Result = new RedirectToActionResult("Error", "Home", null);
}
}
Result Filters:
Example:
public class CustomResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context) { }
public void OnResultExecuted(ResultExecutedContext context) { }
}
Filters provide a way to inject custom behavior at different stages of the request processing pipeline, making them useful for cross-cutting concerns like authentication, caching, or logging.
Tag Helpers in ASP.NET Core are used to extend the functionality of HTML elements in Razor views. They allow you to add server-side processing logic to HTML tags in a clean and readable way, making your Razor views more expressive and maintainable.Tag Helpers are processed on the server, and they help you build dynamic content while still working with familiar HTML syntax.
Example:
<a asp-controller="Home" asp-action="Index">Home</a>
In this example:
ASP.NET Core includes many built-in Tag Helpers, such as:
You can also create custom Tag Helpers by inheriting from the TagHelper base class.
Creating a custom Tag Helper:
public class EmailTagHelper : TagHelper
{
public string Address { get; set; }
public string Display { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "a"; // Replaces <email> with <a> tag
output.Attributes.SetAttribute("href", $"mailto:{Address}");
output.Content.SetContent(Display);
}
}
Then, in your Razor view:
<email address="info@domain.com" display="Contact Us"></email>
Tag Helpers are a powerful feature of ASP.NET Core that provide a clean way to generate dynamic content within Razor views, helping to separate presentation from logic.
Cross-Origin Resource Sharing (CORS) is a security feature implemented by web browsers to prevent unauthorized access to resources from different origins (domains). ASP.NET Core provides built-in support for configuring CORS policies to allow or restrict cross-origin requests.
Example:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder => builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("AllowSpecificOrigin"); // Apply the policy
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
In this example:
Allowing CORS for All Origins: To allow requests from any origin, use the following:
builder.AllowAnyOrigin();
Applying CORS Globally or to Specific Controllers: You can apply CORS globally or only to specific controllers/actions using the [EnableCors] attribute.
[EnableCors("AllowSpecificOrigin")]
public class MyController : Controller
{
// Action methods
}
CORS is essential for securing web applications and ensuring that only trusted domains can access your resources.
In ASP.NET Core MVC, a View Model is a class that represents the data and logic required specifically for the view. It is designed to transfer data between the controller and the view, and is often a combination of multiple models or properties tailored for the view's needs. Difference between View Model and Model:
Model:
ViewModel:
Example: If you have a User model:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
But your view needs both User information and some additional data like Role, you create a ViewModel:
public class UserViewModel
{
public User User { get; set; }
public string Role { get; set; }
}
This UserViewModel provides the data required by the view, combining user information with role data.
ViewModels help decouple the view from the business logic, ensuring that only the necessary data is passed to the view, improving maintainability and security.
Kestrel is a cross-platform web server for ASP.NET Core. It is the default web server included with ASP.NET Core and provides high performance for handling HTTP requests.
Key features of Kestrel:
When to use Kestrel:
Example of configuring Kestrel in Program. cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel(); // Configures Kestrel as the web server
webBuilder.UseStartup<Startup>();
});
}
Kestrel provides fast and lightweight server capabilities, making it ideal for serving ASP.NET Core applications.
ASP.NET Core includes built-in support for logging to help developers capture and monitor events, errors, and application behavior. It provides a flexible and extensible logging framework that supports different logging providers.
Basic Logging Setup: Logging is configured in the Startup.cs file, and you can inject ILogger<T> into your classes to log messages.
Example:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Executing Index action");
return View();
}
}
Logging Levels: ASP.NET Core provides several logging levels that indicate the severity of a log message:
Logging Providers: ASP.NET Core supports multiple logging providers to log data to different outputs:
Configure Logging in appsettings.json: You can configure logging settings (such as logging levels) in the appsettings.json file.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}
ASP.NET Core provides a powerful and extensible logging framework that makes it easy to log messages, capture errors, and monitor application health using different logging providers.
Dependency Injection (DI) is a design pattern used in ASP.NET Core to achieve Inversion of Control (IoC), where the creation of dependencies (objects) is managed by an external component rather than the class that uses them. DI allows for better code maintainability, testability, and flexibility by promoting loose coupling between classes.ASP.NET Core has built-in support for DI, making it easy to inject dependencies into controllers, services, and other components.Types of Dependency Injection:
Example of Constructor Injection:
Service Interface:
public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}
Service Implementation:
public class EmailService : IEmailService
{
public void SendEmail(string to, string subject, string body)
{
// Email sending logic
}
}
Registering Service in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IEmailService, EmailService>();
services.AddControllers();
}
Injecting Service in Controller:
public class HomeController : Controller
{
private readonly IEmailService _emailService;
public HomeController(IEmailService emailService)
{
_emailService = emailService;
}
public IActionResult Index()
{
_emailService.SendEmail("example@domain.com", "Subject", "Body");
return View();
}
}
Lifetime of Services:
The Configure Services method in ASP.NET Core's Startup. cs file is used to configure the Dependency Injection (DI) container. It is where services are registered for use in the application. ASP.NET Core uses this method to register services such as MVC, custom services, middleware components, authentication services, database contexts, and more.
Example of Configure Services method:
public void ConfigureServices(IServiceCollection services)
{
// Register MVC services
services.AddControllersWithViews();
// Register a custom service
services.AddScoped<IEmailService, EmailService>();
// Register a database context
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// Add identity services for authentication
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
}
In this example:
Once services are registered in ConfigureServices, they can be injected and used throughout the application via Dependency Injection.
The Configure method in ASP.NET Core's Startup.cs file is used to define how the application will respond to incoming HTTP requests. It configures the middleware components that make up the HTTP request pipeline.
The order in which middleware components are added in the Configure method is critical because they are executed sequentially as the request flows through the pipeline.
Example of Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
In this example:
The Configure method sets up the middleware components that handle different stages of the request and response process.
In ASP.NET Core, service lifetimes define how long instances of services are kept in memory. When registering services in the DI container, you can specify one of three lifetimes: AddScoped, AddTransient, or AddSingleton.
AddTransient:
Example:
services.AddTransient<IEmailService, EmailService>();
AddScoped:
Example:
services.AddScoped<IUserService, UserService>();
AddSingleton:
Example:
services.AddSingleton<ILogger, Logger>();
Key differences:
Choosing the correct service lifetime is crucial for optimizing memory usage and ensuring the correct behavior of your services.
Authentication and Authorization are essential aspects of securing ASP.NET Core applications.
Example of Cookie Authentication:
In Startup .cs, configure authentication in Configure Services:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Account/Login";
});
services.AddControllersWithViews();
}
In Configure, use app. Use Authentication() and app. Use Authorization():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Example of Role-Based Authorization:
[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
public IActionResult Index()
{
return View();
}
}
In this example, only users who belong to the "Admin" role can access the AdminController.
ASP.NET Core provides several built-in authorization mechanisms, including:
By combining authentication and authorization, you can effectively manage access control in ASP.NET Core applications.
The IApplicationBuilder interface is used to configure the HTTP request pipeline in ASP.NET Core applications. It provides methods to define middleware components that process requests and responses as they pass through the pipeline.
The IApplicationBuilder instance is available in the Configure method of Startup.cs, and it is used to register middleware components such as routing, authentication, and static file serving.
Key methods of I Application Builder:
Example of I Application Builder in the Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
In this example, IApplicationBuilder is used to configure middleware components like error handling, HTTPS redirection, static file serving, routing, authentication, and authorization.
Middleware in ASP.NET Core is software that is assembled into the application's request pipeline to handle requests and responses. Each middleware component can either process an incoming request and pass it to the next middleware in the pipeline or terminate the request by generating a response.
Middleware is executed in the order it is registered in the Configure method, which makes the order of middleware components critical.
Common ASP.NET Core middleware components:
Creating Custom Middleware: You can create custom middleware by implementing a class that takes an HttpContext object, processes the request, and either generates a response or passes the request to the next middleware component.
Example of custom middleware:
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Log the incoming request
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
// Call the next middleware in the pipeline
await _next(context);
// Log the response
Console.WriteLine($"Response: {context.Response.StatusCode}");
}
}
To use this middleware, it is added to the request pipeline in the Configure method:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<RequestLoggingMiddleware>();
}
Middleware in ASP.NET Core is an essential component for building modular and flexible request pipelines that can handle tasks like authentication, routing, and error handling.
The IHostingEnvironment (or IWebHostEnvironment in later versions of ASP.NET Core) interface is used to provide information about the hosting environment of the application. This is helpful for configuring different settings or behaviors based on whether the application is running in Development, Staging, or Production.
Key properties of IWebHostEnvironment:
ASP.NET Core uses environment variables (like ASPNETCORE_ENVIRONMENT) to determine the current environment. By default, the environment is set to "Production" unless specified otherwise.
Example of using IWebHostEnvironment in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage(); // Only show detailed errors in Development
}
else
{
app.UseExceptionHandler("/Home/Error"); // Use generic error page in Production
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
}
Configure different settings for different environments:
Example of loading different configurations based on the environment:
public void ConfigureServices(IServiceCollection services)
{
if (env.IsDevelopment())
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DevConnection")));
}
else
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ProdConnection")));
}
}
Using IWebHostEnvironment allows you to configure your application differently based on the environment, which is essential for debugging, testing, and deploying in production.
UseEndpoints is a middleware component in ASP.NET Core that is used to define and configure the endpoints that the application will respond to. It works in conjunction with endpoint routing, which allows you to map incoming requests to specific controllers, actions, or Razor Pages.
Endpoint routing is a feature that helps manage how requests are routed to controllers and Razor Pages, making routing more flexible and extensible.
How UseEndpoints works:
Example of using UseEndpoints in the request pipeline:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
In this example, UseRouting enables routing to endpoints, and UseEndpoints defines the endpoints (in this case, mapping requests to controllers and actions).
Common Endpoint Mapping Methods:
Endpoint routing is a powerful feature of ASP.NET Core that helps you manage how HTTP requests are routed and handled in a structured and efficient way.
ASP.NET Core can serve static files (such as HTML, CSS, JavaScript, images, and fonts) directly from the file system without needing to pass through the MVC pipeline. The static files are typically stored in the wwwroot folder, which acts as the web root for the application.
To enable static file serving in ASP.NET Core, you need to use the UseStaticFiles middleware.
Example of serving static files:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles(); // Enable static file serving
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
By default, static files are served from the wwwroot directory. You can place static assets like CSS, JavaScript, and images inside wwwroot and reference them in your views.
Example:
<link rel="stylesheet" href="/css/site.css" />
<img src="/images/logo.png" alt="Logo" />
If you want to serve static files from a different directory or configure caching for static files, you can use additional options:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider
(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
In this example, static files are served from the "MyStaticFiles" directory, and they are accessed via the /StaticFiles URL.
Handling static files in ASP.NET Core is simple and efficient, allowing for direct file serving with optional caching and customization options.
Here are the next five intermediate-level .NET Core interview questions and their detailed answers:
Kestrel is a cross-platform, high-performance, open-source web server built into ASP.NET Core. It is the default web server used to host ASP.NET Core applications.
Kestrel is designed to be lightweight and fast, making it suitable for serving applications directly in both development and production environments. However, for security reasons, it is often used behind a reverse proxy such as Nginx or IIS in production deployments.
Why Kestrel is used:
How to configure Kestrel in Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel(); // Configures the app to use Kestrel
webBuilder.UseStartup<Startup>();
});
}
You can configure Kestrel-specific options like SSL certificates, request limits, and port numbers in the ConfigureWebHostDefaults section.
Example of configuring Kestrel:
webBuilder.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5001); // Bind to a specific port
options.Listen(IPAddress.Loopback, 5002, listenOptions =>
{
listenOptions.UseHttps
("path-to-certificate.pfx", "password"); // HTTPS configuration
});
});
In production environments, Kestrel is typically used behind a reverse proxy for better security and scalability.
The appsettings.json file is used in ASP.NET Core to store configuration settings in a JSON format. This file is a convenient way to store settings like connection strings, API keys, application behavior, and environment-specific settings. It is a replacement for the web.config file used in previous ASP.NET versions.
Key features of appsettings.json:
Example of an appsettings.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=myServer;Database=myDB;
User Id=myUser;Password=myPassword;"
},
"AppSettings": {
"ApiKey": "12345",
"ApiUrl": "https://api.example.com"
}
}
Using appsettings.json in code: To access the settings, you inject the IConfiguration interface into your classes.
Example:
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Index()
{
var apiKey = _configuration["AppSettings:ApiKey"];
var apiUrl = _configuration["AppSettings:ApiUrl"];
return View();
}
}
In this example, the ApiKey and ApiUrl values from appsettings.json are accessed using IConfiguration.
appsettings.json provides a flexible, JSON-based way to manage configurations, and ASP.NET Core automatically loads and binds it to the IConfiguration object.
ASP.NET Core supports environment-specific configurations through additional configuration files, such as appsettings.Development.json or appsettings.Production.json. These files override the values defined in the default appsettings.json based on the current environment.
The current environment is determined by the ASPNETCORE_ENVIRONMENT environment variable. The common environment names are Development, Staging, and Production.
Steps to read environment-specific settings:
Example of environment-specific appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Debug"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;
Database=myDevDB;User Id=myDevUser;Password=myDevPassword;"
}
}
In the Startup class, the environment-specific file is automatically loaded and merged with appsettings.json:
Example of setting environment in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile
("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile
($"appsettings.{env.EnvironmentName}.json",
optional: true, reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
When the application is running in the Development environment, ASP.NET Core will automatically load appsettings.Development.json in addition to the default appsettings.json.
This mechanism allows you to easily manage settings that change between environments, like database connection strings, API keys, or logging levels.
A ViewComponent in ASP.NET Core is a reusable component that encapsulates rendering logic, similar to a controller action but with the purpose of rendering a specific part of a view. ViewComponents are ideal for building complex UI components that may include their own logic and models, independent of the main page or view model.
Key features of ViewComponent:
Differences between ViewComponent and PartialView:
Example of a ViewComponent:
Create the ViewComponent class:
public class RecentPostsViewComponent : ViewComponent
{
private readonly IPostService _postService;
public RecentPostsViewComponent(IPostService postService)
{
_postService = postService;
}
public async Task<IViewComponentResult> InvokeAsync(int count)
{
var posts = await _postService.GetRecentPostsAsync(count);
return View(posts);
}
}
Create the View for the ViewComponent (e.g., Views/Shared/Components/RecentPosts/Default.cshtml):
<ul>
@foreach (var post in Model)
{
<li>@post.Title</li>
}
</ul>
Invoke the ViewComponent from a View:
@await Component.InvokeAsync("RecentPosts", new { count = 5 })
3.In this example, the RecentPostsViewComponent fetches recent posts and passes them to a view for rendering.
Filters in ASP.NET Core are used to execute code before or after certain stages in the request pipeline. They provide a way to inject cross-cutting concerns, such as logging, error handling, or authorization, into your application's action methods.
Types of filters in ASP.NET Core:
Example of an Action Filter:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// Code that runs before the action method
Console.WriteLine("Action executing: " + context.ActionDescriptor.DisplayName);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Code that runs after the action method
Console.WriteLine("Action executed");
}
}
You can apply this filter globally, on a controller, or on an individual action method.
Applying the filter globally:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(new LogActionFilter());
});
}
Applying the filter to a controller or action:
[LogActionFilter]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Filters allow you to centralize logic that applies across multiple actions or controllers, making your code more modular and maintainable.
Here are the next five intermediate-level .NET Core interview questions and their detailed answers:
Dependency Injection (DI) is a design pattern used in ASP.NET Core to decouple the instantiation of classes from their dependencies. This enables better maintainability, testing, and flexibility of the application. DI allows objects to declare their dependencies through constructors, and the DI framework takes care of providing the necessary dependencies.
ASP.NET Core provides built-in support for DI, making it easy to implement throughout the application.
How DI works in ASP.NET Core:
Services (dependencies) are registered in the Startup.cs class, usually inside the ConfigureServices method.
Services can be registered with different lifetimes:
Example of configuring DI in ASP.NET Core:
Registering services in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopedService, MyScopedService>();
services.AddTransient<IMyTransientService, MyTransientService>();
}
1. Injecting services into a controller:
public class HomeController : Controller
{
private readonly IMySingletonService _singletonService;
private readonly IMyScopedService _scopedService;
private readonly IMyTransientService _transientService;
public HomeController(IMySingletonService singletonService,
IMyScopedService scopedService,
IMyTransientService transientService)
{
_singletonService = singletonService;
_scopedService = scopedService;
_transientService = transientService;
}
public IActionResult Index()
{
// Use the injected services
return View();
}
}
2. Benefits of Dependency Injection:
DI is a core feature of ASP.NET Core, simplifying application architecture and enhancing flexibility.
IServiceProvider is the interface used to resolve services (dependencies) that have been registered in the DI container. It is the core part of ASP.NET Core’s dependency injection mechanism. The IServiceProvider is responsible for providing instances of services when they are requested by controllers, middleware, or other components.
How IServiceProvider works:
Example of manually using IServiceProvider:
public class MyClass
{
private readonly IServiceProvider _serviceProvider;
public MyClass(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void UseService()
{
var myService = _serviceProvider.GetService<IMyService>();
// Use the resolved service
}
}
In this example, IServiceProvider is injected into the class, and the service IMyService is resolved dynamically at runtime.
Common methods of IServiceProvider:
In most scenarios, you rely on constructor injection for services, but IServiceProvider is useful when you need dynamic or late-bound service resolution.
ASP.NET Core includes built-in support for logging via the Microsoft.Extensions.Logging library. This system allows you to record log messages of varying severity to various outputs, such as the console, files, or third-party services like Azure or Serilog.
Key features of ASP.NET Core logging:
Log levels: Logging supports different severity levels:
Logging providers: ASP.NET Core includes several logging providers, such as Console, Debug, EventSource, and EventLog. You can also add third-party providers like Serilog or NLog.
How to configure logging:
In appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Information",
"System": "Error"
}
}
}
In Startup.cs:
public void Configure
(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
logger.LogInformation("Application is starting");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
logger.LogDebug("In development mode");
}
}
In this example, logging levels are defined in appsettings.json, and the ILogger is injected into the Startup.cs class for use.
Injecting the ILogger in a controller:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Home page accessed.");
return View();
}
}
Adding third-party logging providers (e.g., Serilog):
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, configuration) =>
{
configuration
.WriteTo.Console()
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Logging in ASP.NET Core is flexible, allowing developers to capture useful information during development and in production for debugging and auditing purposes.
IConfiguration is an interface in ASP.NET Core used to access configuration settings from various sources like JSON files, environment variables, command-line arguments, and user secrets. It is part of the Microsoft.Extensions.Configuration namespace and provides a unified way to handle configurations in the application.
Key features of IConfiguration:
Common configuration sources:
How IConfiguration works:
In Startup.cs, it is injected into the constructor:
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
var mySetting = _configuration["MySettingKey"];
}
}
1. Accessing nested settings:
var connectionString = _configuration["ConnectionStrings:DefaultConnection"];
2. Example of binding configuration to a strongly-typed model:
public class MySettings
{
public string ApiKey { get; set; }
public string ApiUrl { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MySettings>(_configuration.GetSection("MySettings"));
}
Reading environment variables:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=myServer;Database=myDB;"
}
}
The IConfiguration interface in ASP.NET Core makes it easy to manage settings in a flexible, environment-friendly way, allowing developers to centralize and configure application settings without hardcoding them.
The IOptions<T> pattern in ASP.NET Core is used to access strongly-typed configuration settings. It allows you to bind configuration sections from appsettings.json or other sources directly to C# classes, making it easier to manage and access configuration values throughout the application.
How IOptions<T> works:
Example:
Create a settings class:
public class MySettings
{
public string ApiUrl { get; set; }
public string ApiKey { get; set; }
}
1.Configure the settings in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MySettings>(_configuration.GetSection("MySettings"));
}
2. Access the settings using IOptions<T> in a controller:
public class HomeController : Controller
{
private readonly MySettings _mySettings;
public HomeController(IOptions<MySettings> mySettings)
{
_mySettings = mySettings.Value;
}
public IActionResult Index()
{
var apiUrl = _mySettings.ApiUrl;
var apiKey = _mySettings.ApiKey;
return View();
}
}
IOptionsSnapshot<T> and IOptionsMonitor<T> are advanced variations that allow for runtime updates of configuration settings.The IOptions<T> pattern helps maintain clean and organized code by separating configuration management into its own class, simplifying access to application settings.
In ASP.NET Core, Middleware is a component that processes HTTP requests and responses in a pipeline. Each middleware component can:
The request flows through the middleware pipeline, and each component can modify the request or response at various stages.How Middleware works:
Example of the middleware pipeline:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Creating custom middleware:
To create custom middleware, you can define a class that processes the request and calls the next middleware in the pipeline.
Steps:
Example of custom middleware:
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Custom logic before the next middleware
Console.WriteLine("Before Request");
// Call the next middleware in the pipeline
await _next(context);
// Custom logic after the next middleware
Console.WriteLine("After Request");
}
}
Register custom middleware in Startup.cs:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<CustomMiddleware>();
app.UseRouting();
}
Middleware is a fundamental concept in ASP.NET Core, allowing for modular and reusable components that process HTTP requests.
In ASP.NET Core, session management is used to store temporary data specific to a user across multiple HTTP requests. Unlike cookies, session data is stored server-side, and a session ID is passed to the client via a cookie.
How session management works:
Configuring session management:
Add the required session services in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache(); // Required for session
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
}
1. Use session middleware in the request pipeline:
public void Configure(IApplicationBuilder app)
{
app.UseSession();
}
2. Store and retrieve session data in a controller:
public IActionResult Index()
{
HttpContext.Session.SetString("Username", "JohnDoe");
string username = HttpContext.Session.GetString("Username");
return View();
}
3. Session storage options:
Session management in ASP.NET Core is useful for maintaining user-specific data across multiple requests in a secure and scalable way.
Both Use() and Run() are used to add middleware components to the ASP.NET Core middleware pipeline, but they serve different purposes:
IApplicationBuilder.Use():
Example of Use():
app.Use(async (context, next) =>
{
// Logic before the next middleware
Console.WriteLine("Before next middleware");
await next(); // Call the next middleware
// Logic after the next middleware
Console.WriteLine("After next middleware");
});
IApplicationBuilder.Run():
Example of Run():
app.Run(async context =>
{
await context.Response.WriteAsync("Request handled completely");
});
Key difference:
UseEndpoints() and UseMvc() are methods in ASP.NET Core used to configure request handling, but they differ in how they manage routing and endpoints.
UseMvc():
Example:
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
UseEndpoints():
Example:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
Key difference:
Endpoint routing is the recommended approach in ASP.NET Core 3.x and later, as it simplifies and unifies routing across different types of requests (MVC, Razor Pages, SignalR, etc.).
In ASP.NET Core, exception handling can be managed using middleware that catches unhandled exceptions and returns appropriate responses, typically through custom error pages or JSON responses for API endpoints.Key techniques for exception handling:
Developer Exception Page (for development environments):
Example:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
Exception Handling Middleware (for production environments):
Example:
public void Configure(IApplicationBuilder app)
{
app.UseExceptionHandler("/Home/Error"); // Custom error page for exceptions
}
Custom Exception Handling Middleware:
Example of custom exception handling middleware:
public class CustomExceptionMiddleware
{
private readonly RequestDelegate _next;
public CustomExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
// Handle exception
context.Response.StatusCode = 500;
await context.Response.WriteAsync("An error occurred.");
}
}
}
// Register the middleware in Startup.cs
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<CustomExceptionMiddleware>();
}
Logging exceptions:
By setting up proper exception handling middleware, you can ensure that unhandled exceptions are captured, logged, and returned in a user-friendly way based on the application type (MVC, API, etc.).
CQRS Pattern: CQRS is a pattern that separates read and write operations into different models. The idea is to allow for optimization of both sides independently, making the system more scalable and maintainable.
Implementation in .NET Core:
Example:
public class CreateProductCommand : IRequest<Product>
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class CreateProductCommandHandler : IRequestHandler
<CreateProductCommand, Product>
{
public async Task<Product> Handle
(CreateProductCommand request, CancellationToken cancellationToken)
{
// Logic to create a product
}
}
// Query
public class GetProductQuery : IRequest<Product>
{
public int Id { get; set; }
}
public class GetProductQueryHandler : IRequestHandler
<GetProductQuery, Product>
{
public async Task<Product> Handle
(GetProductQuery request, CancellationToken cancellationToken)
{
// Logic to get a product by ID
}
}
Handling Distributed Transactions: In microservice architecture, handling distributed transactions can be complex. Here are some approaches:
Event Sourcing Pattern: Event sourcing stores the state of a system as a sequence of events. Instead of storing just the current state, you store every change that has occurred.
Implementation Steps:
Example:
public class ProductCreatedEvent
{
public int ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Store events in a database
public void SaveEvent(Event @event)
{
// Logic to save the event
}
// Replay events to get the current state
public Product GetProductById(int id)
{
var events = _eventStore.GetEventsForAggregate(id);
// Logic to replay events
}
Saga Pattern: The Saga pattern is a way to manage distributed transactions by breaking them down into a series of smaller, manageable transactions that can be completed independently. It can be implemented in two ways:
Implementation Steps:
Integrating .NET Core with Docker:
Example Dockerfile:
# Use the official .NET SDK image
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o out
# Use the official .NET runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "YourApp.dll"]
Benefits of Using Docker:
Centralized Logging: Centralized logging collects logs from various microservices into a single location, facilitating monitoring and analysis.Implementation Steps:
Example using Serilog:
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
{
AutoRegisterTemplate = true
})
.CreateLogger();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // Using Serilog for logging
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
Polly Overview: Polly is a resilience library for .NET that helps manage transient faults by implementing policies such as retries, circuit breakers, and timeouts.
Implementation Steps:
Example of a Retry Policy:
var retryPolicy = Policy
.Handle<HttpRequestException>()
.RetryAsync(3); // Retries 3 times
await retryPolicy.ExecuteAsync(async () =>
{
// Your HTTP call
});
gRPC Integration Steps:
Example Proto File:
// product.proto
syntax = "proto3";
service ProductService {
rpc GetProduct (ProductRequest) returns (ProductResponse);
}
message ProductRequest {
int32 id = 1;
}
message ProductResponse {
string name = 1;
double price = 2;
}
Startup Configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(); // Add gRPC services
}
// In the Configure method
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<ProductService>(); // Map the gRPC service
});
Performance Optimization Best Practices:
Circuit Breaker and Retry Patterns: You can create a combined policy using Polly for circuit breaking and retries.
Example Implementation:
var retryPolicy = Policy
.Handle<HttpRequestException>()
.RetryAsync(3); // Retry 3 times
var circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync
(2, TimeSpan.FromMinutes(1)); // Break after 2 failures for 1 minute
var combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
await combinedPolicy.ExecuteAsync(async () =>
{
// Your HTTP call here
});
Managing Caching at Scale:
Example using Redis:
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
Deployment Steps:
Example YAML for Deployment and Service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dotnet-app
spec:
replicas: 3
selector:
matchLabels:
app: my-dotnet-app
template:
metadata:
labels:
app: my-dotnet-app
spec:
containers:
- name: my-dotnet-app
image: my-dotnet-app:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-dotnet-app
spec:
selector:
app: my-dotnet-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
API Versioning Implementation:
Example:
services.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
});
// Define versioned controllers
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ControllerBase
{
// Actions for version 1.0
}
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ControllerBase
{
// Actions for version 2.0
}
OAuth2 and OpenID Connect Integration:
Example:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = "https://your-auth-server";
options.Audience = "your-api-audience";
});
app.UseAuthentication(); // Enable authentication middleware
Hybrid Transactions Handling: Hybrid transactions can be complex and may involve several strategies:
Microservices Security:
Example Configuration:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://your-identity-server";
options.Audience = "your-api-audience";
});
GraphQL Overview: GraphQL is a query language for APIs that allows clients to request only the data they need.
Implementation Steps:
Example:
services.AddGraphQLServer()
.AddQueryType<Query>() // Define your Query class
.AddMutationType<Mutation>(); // Define your Mutation class
Scaling with AKS:
Example HPA Configuration:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: my-dotnet-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-dotnet-app
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
Message-Driven Microservices:
Example Publisher:
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare
(queue: "queue_name", durable: false, exclusive: false, autoDelete: false, arguments: null);
var body = Encoding.UTF8.GetBytes("Hello World");
channel.BasicPublish(exchange: "", routingKey: "queue_name", basicProperties: null, body: body);
}
Distributed Tracing: Distributed tracing helps track requests across microservices.
Implementation Steps:
Example Configuration:
services.AddOpenTelemetryTracing(builder =>
{
builder.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("YourServiceName"))
.AddJaegerExporter();
});
Zero-Downtime Deployment Strategies:
Example Steps:
Working with Azure Functions:
Example HTTP Trigger Function:
[FunctionName("HttpTriggerFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
return new OkObjectResult("Hello, Azure Functions!");
}
Handling Large Files:
Example Configuration:
services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
});
File Upload Example:
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file.Length > 0)
{
var path = Path.Combine("uploads", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok();
}
return BadRequest();
}
File Download Example:
[HttpGet("download/{fileName}")]
public IActionResult Download(string fileName)
{
var path = Path.Combine("uploads", fileName);
var fileStream = new FileStream(path, FileMode.Open);
return File(fileStream, "application/octet-stream", fileName);
}
Scaling Strategies:
Optimizing SQL Queries with Dapper:
Example Query:
using (var connection = new SqlConnection(connectionString))
{
var sql = "SELECT Id, Name FROM Products WHERE CategoryId = @CategoryId";
var products = await connection.QueryAsync<Product>(sql, new { CategoryId = 1 });
}
Concurrency Handling:
Example Model Configuration:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; } // Concurrency token
}
Handling Concurrency Exception:
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
// Handle conflict (e.g., notify user, reload entity, etc.)
}
Global Exception Handling:
Example Middleware:
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync("An unexpected error occurred.");
}
}
Register Middleware in Startup:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<ExceptionHandlingMiddleware>();
// Other middleware registrations...
}
Authentication Steps:
Example Configuration:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
app.UseAuthentication(); // Enable authentication middleware
Securing with HTTPS:
Example Kestrel Configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443; // Redirect to this port
});
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
// Other middleware...
}
Integrating ElasticSearch:
Example Configuration:
services.AddSingleton<IElasticClient>
(new ElasticClient(new ConnectionSettings(new Uri("http://localhost:9200"))));
Example Search Query:
var searchResponse = await elasticClient.SearchAsync<Product>(s => s
.Index("products")
.Query(q => q
.Match(m => m
.Field(f => f.Name)
.Query("search term")
)
)
);
API Gateway Overview: An API Gateway acts as a single entry point for microservices, handling requests, routing, and aggregation.
Implementation Steps:
Example ocelot.json:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/products",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [ "GET" ]
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000"
}
}
Startup Configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(); // Add Ocelot services
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Map controllers
});
app.UseOcelot().Wait(); // Use Ocelot middleware
}
Overview of Orleans: Orleans is a framework for building distributed applications, leveraging virtual actors for scalability and fault tolerance.
Implementation Steps:
Example Configuration:
services.AddOrleansClient(clientBuilder =>
{
clientBuilder.UseLocalhostClustering();
});
Example Grain Interface:
public interface IMyGrain : IGrainWithStringKey
{
Task<string> GetGreeting(string name);
}
Implement Grain:
public class MyGrain : Grain, IMyGrain
{
public Task<string> GetGreeting(string name)
{
return Task.FromResult($"Hello, {name}!");
}
}
JWT Refresh Tokens:
Example Token Generation:
var refreshToken = GenerateRefreshToken();
await SaveRefreshTokenToDatabase(user.Id, refreshToken);
Token Refresh Endpoint:
[HttpPost("refresh-token")]
public async Task<IActionResult> RefreshToken(string token)
{
var principal = ValidateToken(token);
if (principal == null) return Unauthorized();
var newToken = GenerateAccessToken(principal);
return Ok(new { AccessToken = newToken });
}
Memory Management Optimization:
Profiling Tools: Utilize tools like the .NET Memory Profiler or Visual Studio's Diagnostic Tools to analyze memory usage.
Multitenancy Approaches:
Example Shared Database Model:
public class Tenant
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int TenantId { get; set; } // Multitenancy key
}
Load Balancing in Kubernetes:
Example Service Configuration:
apiVersion: v1
kind: Service
metadata:
name: my-dotnet-app
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 5000
selector:
app: my-dotnet-app
Monitoring Techniques:
Example Health Check Configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app)
{
app.UseHealthChecks("/health");
}
Implementing WebSockets:
Example Configuration:
public void Configure(IApplicationBuilder app)
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await HandleWebSocketAsync(webSocket);
}
else
{
await next();
}
});
}
WebSocket Handler Example:
private async Task HandleWebSocketAsync(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync
(new ArraySegment<byte>(buffer), CancellationToken.None);
// Handle received messages...
} while (!result.CloseStatus.HasValue);
await webSocket.CloseAsync
(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
Integrating Azure Service Bus:
Example Configuration:
var connectionString = "YourConnectionString";
var queueClient = new QueueClient(connectionString, "YourQueueName");
Sending Messages Example:
var message = new Message
(Encoding.UTF8.GetBytes("Hello, Azure Service Bus!"));
await queueClient.SendAsync(message);
Receiving Messages Example:
var messageHandlerOptions = new MessageHandlerOptions
(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
queueClient.RegisterMessageHandler
(ProcessMessagesAsync, messageHandlerOptions);
Architecting Microservices:
Example Architecture Diagram: Create an architecture diagram that shows microservices, API gateways, databases, and message queues.