ASP.NET Interview Questions with Answers
Most Asked ASP.NET Interview Questions for Backend and Full‑Stack Developers
Introduction
ASP.NET is a mature, cross‑platform web framework from Microsoft for building modern web applications, RESTful APIs, and real‑time services. This page collects the most frequently asked ASP.NET interview questions – from fundamental concepts like MVC and Web Forms to advanced patterns like microservices, JWT, and Blazor – essential for any backend or full‑stack developer.
Why ASP.NET?
- Cross‑platform – runs on Windows, Linux, and macOS
- High performance – built on .NET Core and Kestrel
- Rich ecosystem – MVC, Web API, Razor Pages, Blazor
- Enterprise‑ready – robust security, caching, and logging
- Massive community and tooling (Visual Studio, VS Code, Azure)
- Used by thousands of companies for mission‑critical applications
Most Asked ASP.NET Interview Questions
ASP.NET is a modern open-source web application framework created by Microsoft designed for compiling rich dynamic web solutions, RESTful API instances, and distributed microservice workloads using C# and .NET.
// Dynamic web API endpoint in ASP.NET Core (Minimal APIs)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Welcome to ASP.NET Core API!");
app.Run();The ecosystem has evolved from classical legacy modules into highly consolidated, open-source frameworks:
// Architectural ecosystem framework selection
// 1. ASP.NET Web Forms (Legacy event-driven)
// 2. ASP.NET MVC (Structured separation)
// 3. ASP.NET Web API (RESTful endpoint interfaces)
// 4. ASP.NET Core (Modern, high performance, cross-platform)Web Forms is a legacy event-driven ASP.NET development paradigm centered on visual desktop-like drag-and-drop server controls. It automatically handles viewstates to manage UI rendering behaviors across page interactions.
<!-- ASP.NET Web Forms Event-Driven server side control markup -->
<asp:Button ID="btnSubmit" runat="server" Text="Click Me" OnClick="btnSubmit_Click" />ASP.NET MVC splits complex logic structures into three isolated modules: the Model (data payload rules), the View (HTML presentation templates), and the Controller (processing logic routing pipelines).
// Model-View-Controller design architecture pattern
public class HomeController : Controller {
public IActionResult Index() {
return View(); // Routes control flow directly to the layout view
}
}ASP.NET Core is the modern, cloud-optimized, cross-platform successor framework to traditional ASP.NET. It runs identically on Windows, Linux, and macOS environments, completely free of legacy IIS server hosting dependencies.
// Program entry startup bootstrapping in ASP.NET Core
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.Run();Code-behind is a design pattern separating visual layout pages (.aspx) from server-side interaction files (.aspx.cs). It helps isolate presentation layouts from transactional back-end business logic.
// Code-behind mechanism: separation of layout and business processing logic
// Page.aspx markup binds to class code-behind file dynamically
public partial class UserDashboard : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
lblMessage.Text = "Welcome to Server Control logic!";
}
}ViewState is a system mechanism in classic Web Forms that retains control values across postback cycles. It serializes data states into a base64 string hidden inside HTML forms.
<!-- Hidden structural system state tracking payload inside generated browser DOM -->
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjg0Nzc0MD..." />A postback is the action of transmitting layout forms back to the host server for execution. Developers can check if the page is rendering for the first time or resolving a postback using the IsPostBack property.
// Handling user postbacks safely inside page transaction sequences
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
// Runs exclusively on the initial client landing request
PopulateDropdowns();
}
}Server Controls are server-side components in classic Web Forms that output HTML tags. They feature the runat="server" property, allowing them to be managed programmatically in code-behind files.
<!-- Server-side UI tag rendering elements inside dynamic engine compiler -->
<asp:TextBox ID="txtEmail" runat="server" CssClass="form-input" Required="true" />A Master Page defines a shared layout template (containing header, sidebar, navigation, and footer structures) that is inherited dynamically across child content pages.
<!-- Master layout structure containing shared template content boxes -->
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.Master.cs" %>
<div class="header">Main Application Layout Header</div>
<asp:ContentPlaceHolder ID="MainBodyContent" runat="server">
<!-- Child page unique body elements are injected directly here -->
</asp:ContentPlaceHolder>web.config is an XML configuration file used in classic ASP.NET to manage database connections, authentication rules, security restrictions, and framework behaviors.
<!-- XML application-level settings configuration parameters inside web.config -->
<configuration>
<appSettings>
<add key="ApplicationMode" value="Production" />
</appSettings>
<connectionStrings>
<add name="DbConn" connectionString="Server=SQLServer;Database=MyDB;Trusted_Connection=True;" />
</connectionStrings>
</configuration>Global.asax is an application-level file used in classic ASP.NET to intercept lifecycle events, such as application startup, session initiation, and unhandled system errors.
// Application-level lifecycle events inside global.asax
public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
// Runs on initial web app pool initiation sequences
RegisterRoutes(RouteTable.Routes);
}
}Session State retains user-specific data across multiple request steps. The state container runs as an in-memory dictionary, distributed SQL servers, or Redis cache backends.
// Saving user session state payload buffers across server steps
HttpContext.Session.SetString("UserEmail", "akash@example.com");
// Reading data dynamically from session storage buffers
var email = HttpContext.Session.GetString("UserEmail");Caching stores processed output data in high-performance memory buffers to serve future requests quickly without reprocessing logic or querying databases.
// Output caching strategy configurations on actions
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
public IActionResult GetCachedData() {
return Ok(new { Timestamp = DateTime.UtcNow });
}Authentication is the security process of verifying user identity credentials (e.g., matching database usernames and passwords or validating external OAuth access keys).
// Implementing cookie authentication configuration mechanisms
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
});Authorization validates security permissions to determine which restricted system resources, pages, or API endpoints an authenticated user is allowed to access.
// Managing user resource accessibility via role controls
[Authorize(Roles = "Admin, Manager")]
public class AdminController : Controller {
public IActionResult Dashboard() => View();
}Routing is the engine that maps incoming HTTP request URLs to the appropriate controller action methods, handling variable parameter extractions automatically.
// Mapping routing patterns dynamically in the startup configuration
app.MapControllerRoute(
name: "products",
pattern: "store/{category}/{id?}",
defaults: new { controller = "Products", action = "Details" });Dependency Injection (DI) is a design pattern used to decouple components. Instead of classes instantiating dependencies directly, the host container injects them at runtime.
// Registering and injecting constructor services via native DI containers
builder.Services.AddScoped<IUserRepository, UserRepository>();
public class UserController : Controller {
private readonly IUserRepository _repo;
public UserController(IUserRepository repo) {
_repo = repo; // Loose coupling architecture
}
}Middleware is software assembled into an application pipeline to handle requests and responses. Each component can inspect, redirect, block, or modify requests in transit.
// Dynamic pipeline middleware processing step delegates
app.Use(async (context, next) => {
// Process request operations prior to passing up pipeline
await next();
// Post-processing execution tasks before returning output
});Razor is a markup syntax used to embed C# code dynamically inside HTML views. It utilizes the @ prefix symbol to transition between markup and server-side code.
<!-- Structured Razor view layout programming loops -->
@model List<string>
<ul>
@foreach(var name in Model) {
<li class="user-item">User: @name</li>
}
</ul>A Partial View is a reusable layout block designed to render specific visual sub-modules within larger host view layouts, avoiding redundant UI markup.
<!-- Reusable isolated layout component injection -->
<div class="sidebar">
<partial name="_UserCard" model="Model.CurrentUser" />
</div>TempData is a dictionary container used to store temporary values that survive exactly one HTTP redirection step, automatically clearing itself after access.
// Temporary storage passing patterns surviving exactly one redirection step
public IActionResult UpdateSettings() {
TempData["AlertMessage"] = "System settings modified successfully!";
return RedirectToAction("Dashboard");
}ViewBag is a dynamic container property used to pass values from controllers to view templates. It utilizes dynamic properties that are validated only at runtime.
// Passing un-typed dynamic parameters cleanly from controller contexts down to views
public IActionResult Index() {
ViewBag.PageTitle = "System Administration Panel";
return View();
}Model Binding maps incoming HTTP query strings, route parameters, or JSON request bodies directly onto action parameters or strongly-typed object classes.
// Binding incoming query string payloads directly inside API methods
[HttpGet("search")]
public IActionResult FindProducts([FromQuery] string query, [FromQuery] int limit) {
return Ok($"Searching for: {query}, Limit: {limit}");
}ActionResult is the base class for controller action responses. It handles various HTTP response states, such as Ok() (200), NotFound() (404), or View() markup.
// Base controller return results managing REST protocols
public IActionResult GetSystemStatus(int id) {
if (id <= 0) return BadRequest("Invalid target ID specified.");
return Ok(new { Status = "Online", Code = 200 });
}Filters intercept controller action lifecycles. They permit running custom code blocks (e.g., performance logging, authorization checks, exception handlers) before or after execution.
// Building custom action filters handling horizontal security concerns
public class LogActionFilter : IActionFilter {
public void OnActionExecuting(ActionExecutingContext context) {
Console.WriteLine($"Initiating Action: {context.ActionDescriptor.DisplayName}");
}
public void OnActionExecuted(ActionExecutedContext context) {}
}Bundling combines multiple CSS or JS files into a single bundle to reduce browser requests, while Minification compresses code size by removing whitespace and comments.
// Bundle dynamic assets to reduce browser HTTP handshake limits
// ScriptBundle consolidates files while minimizing Whitespace and Comments
var scriptBundle = new ScriptBundle("~/bundles/corejs")
.Include("~/Scripts/jquery-{version}.js", "~/Scripts/bootstrap.js");Web API is a framework designed for creating RESTful HTTP services. It processes JSON or XML payloads to serve desktop applications, mobile apps, or JavaScript-based frontend clients.
// Fully fledged REST API controller configurations
[ApiController]
[Route("api/[controller]")]
public class InventoryController : ControllerBase {
[HttpGet("{id}")]
public IActionResult GetProduct(int id) => Ok(new { ProductId = id });
}SignalR is a real-time web library that enables bi-directional client-server communications over WebSockets, automatically falling back to polling techniques if needed.
// Dynamic server push real-time client sync hubs using SignalR
public class SystemAlertHub : Hub {
public async Task BroadcastAlert(string message) {
await Clients.All.SendAsync("ReceiveAlert", message);
}
}ASP.NET Core Identity is a comprehensive authentication system that manages users, credentials, roles, claims, security tokens, and Multi-Factor Authentication (MFA) setups.
// Complete secure user database schema handling using Microsoft Identity
builder.Services.AddDefaultIdentity<IdentityUser>(options => {
options.Password.RequiredLength = 8;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
}).AddEntityFrameworkStores<ApplicationDbContext>();The Middleware Pipeline defines the sequential execution order of middleware components. Each component decides whether to pass the request to the next step or short-circuit the pipeline.
// Sequence order of middleware layers inside the pipeline
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();Kestrel is the fast, cross-platform, internal HTTP web server engine used in ASP.NET Core applications. It is typically deployed behind a reverse proxy like IIS or Nginx.
// Configuring Kestrel microserver listening sockets in Program.cs
builder.WebHost.ConfigureKestrel(serverOptions => {
serverOptions.ListenAnyIP(5001, listenOptions => {
listenOptions.UseHttps(); // Secure SSL bindings
});
});REST is an architectural design pattern for building distributed web services. It leverages standard HTTP methods to interact with endpoints in a stateless manner.
// Standard REST structural mappings matching standard CRUD verbs
// GET -> api/users (Retrieve collections)
// POST -> api/users (Create record payload)
// PUT -> api/users/{id} (Update record parameters)
// DELETE -> api/users/{id} (Destroy targeted key)JSON Web Token (JWT) is a compact, URL-safe token format used for stateless authentication. The client sends the token in the HTTP Authorization header for authorization validation.
// Creating structural JSON Web Tokens (JWT) inside backend authentication servers
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("SuperSecretSecureKey123_DoNotDisclose");
var tokenDescriptor = new SecurityTokenDescriptor {
Subject = new ClaimsIdentity(new[] { new Claim("id", "101") }),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);Microservices is an architectural style that structures an application as a collection of small, autonomous, loosely-coupled services that communicate via lightweight protocols (e.g., HTTP REST or gRPC).
// Centralized HTTP client configurations interfacing internal microservice APIs
builder.Services.AddHttpClient("BillingService", client => {
client.BaseAddress = new Uri("https://billing.internal.local/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});The async and await keywords enable non-blocking, asynchronous programming. They improve application scalability by releasing threads to handle other requests while waiting for I/O operations to complete.
// Handling I/O operations non-blockingly via asynchronous threads
public async Task<IActionResult> FetchRecordsAsync() {
var data = await _dbContext.Users.ToListAsync(); // Releases work process thread during database wait
return Ok(data);
}The built-in DI container manages object lifecycles. It supports three service lifetimes: Transient (always recreated), Scoped (recreated per request), and Singleton (one global instance).
// Explicit service registration lifetimes in DI container builds
builder.Services.AddSingleton<ICacheService, MemoryCacheService>(); // Single global instance
builder.Services.AddScoped<IOrderService, OrderService>(); // Recreated per HTTP request context
builder.Services.AddTransient<ITransactionId, GuidGenerator>(); // Recreated on every injection pointCross-Origin Resource Sharing (CORS) is a browser security mechanism that restricts web applications from making requests to a domain different from the one that served the page.
// Configuring dynamic Cross-Origin Resource Sharing (CORS) rules securely
builder.Services.AddCors(options => {
options.AddPolicy("AllowSpecificApp", policy => {
policy.WithOrigins("https://dashboard.company.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});Swagger (OpenAPI) is a toolset that automatically generates interactive REST API documentation, allowing developers to test API endpoints directly from a web browser interface.
// Automated interactive API endpoint schema generators (Swagger)
builder.Services.AddSwaggerGen();
// ...
if (app.Environment.IsDevelopment()) {
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Core API v1"));
}ASP.NET Core features a built-in logging interface (ILogger) that writes diagnostics to multiple outputs (e.g., console, files, Azure Application Insights, or third-party tools like Serilog).
// Centralized logging infrastructure configurations in controllers
private readonly ILogger<PaymentController> _logger;
public PaymentController(ILogger<PaymentController> logger) {
_logger = logger;
}
public IActionResult ProcessPayment() {
_logger.LogInformation("Payment sequence initiated dynamically.");
return Ok();
}A basic ASP.NET API Controller demonstrating endpoint routing and returning structured JSON data:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase {
[HttpGet]
public IActionResult GetUser() {
return Ok(new { Name = "Akash", Role = "Administrator" });
}
}An example of dynamic markup rendering in a Razor view using C# conditionals:
@model WebApp.Models.UserModel
<div className="profile-wrapper">
<h1>Welcome, @Model.Name!</h1>
<p>Account Status: @(Model.IsActive ? "Active" : "Suspended")</p>
</div>An example of dynamic parameter extraction in attribute-based routing:
// Configuring attribute route configurations within standard controllers
[HttpGet("api/v1/departments/{deptId:int}/employees")]
public IActionResult GetEmployeesByDepartment(int deptId) {
return Ok($"Returning employees matching dept: {deptId}");
}How dependencies are resolved automatically via class constructor interfaces:
public interface IService {
string GetServiceData();
}
public class BusinessController : ControllerBase {
private readonly IService _service;
// Dependency Injection resolves the service instance automatically on initialization
public BusinessController(IService service) {
_service = service;
}
}An asynchronous endpoint demonstrating database queries and returning HTTP responses:
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProductById(int id) {
var product = await _dbContext.Products.FindAsync(id);
if (product == null) {
return NotFound(new { Message = "Target item not registered." });
}
return Ok(product);
}An example of storing and reading strings in the HTTP session cache:
// Writing and retrieving operational session properties
HttpContext.Session.SetString("name", "AK");
var cachedSessionUser = HttpContext.Session.GetString("name");An example of dynamic try-catch-finally blocks with structured error logging:
try {
var result = _paymentService.ProcessTransaction(payload);
}
catch (PaymentDeclinedException ex) {
_logger.LogWarning(ex, "Declined transaction processing sequence.");
return BadRequest(ex.Message);
}
catch (Exception ex) {
_logger.LogError(ex, "Unexpected crash inside the transaction processor.");
throw;
}A custom middleware component that executes logic during HTTP requests:
public class SimpleCustomMiddleware {
private readonly RequestDelegate _next;
public SimpleCustomMiddleware(RequestDelegate next) {
_next = next;
}
public async Task InvokeAsync(HttpContext context) {
// Run logic prior to executing down nested steps
context.Response.Headers.Add("X-Execution-Engine", "ASP.NET Core");
await _next(context);
}
}An example of reading connection strings and app settings at runtime:
// Programmatic configurations mappings inside core app pipelines
var databaseConnectionString = Configuration.GetConnectionString("DefaultConnection");
var thirdPartyApiKey = Configuration["ApiKeys:ExternalProvider"];An example of registering and enabling cross-origin policies in the startup pipeline:
// Program.cs setup configuration settings
builder.Services.AddCors(options => {
options.AddPolicy("AllowDashboard", p => p.WithOrigins("https://dash.com").AllowAnyMethod());
});
var app = builder.Build();
app.UseCors("AllowDashboard");Entity Framework Core is an ORM that maps database tables to .NET objects, simplifying data access.
// Entity Framework Core – Code First approach
public class AppDbContext : DbContext {
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
// Using DbContext in a controller
public class ProductsController : ControllerBase {
private readonly AppDbContext _db;
public ProductsController(AppDbContext db) => _db = db;
[HttpGet]
public async Task<IActionResult> Get() {
return Ok(await _db.Products.ToListAsync());
}
}Dapper is a lightweight micro-ORM that focuses on performance with raw SQL queries.
// Dapper – lightweight micro-ORM
using Dapper;
public class ProductRepo {
private readonly IDbConnection _conn;
public ProductRepo(IConfiguration config) {
_conn = new SqlConnection(config.GetConnectionString("Default"));
}
public async Task<IEnumerable<Product>> GetProducts() {
return await _conn.QueryAsync<Product>("SELECT * FROM Products");
}
}Background services allow running long-running tasks in the background, e.g., timed jobs or message processing.
// Background service using IHostedService
public class TimedBackgroundService : IHostedService, IDisposable {
private Timer _timer;
public Task StartAsync(CancellationToken cancellationToken) {
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
return Task.CompletedTask;
}
private void DoWork(object state) {
// Background logic
}
public Task StopAsync(CancellationToken cancellationToken) {
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose() => _timer?.Dispose();
}Health checks provide a simple way to monitor the availability of application dependencies (databases, external APIs).
// Health Checks – /health endpoint
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddUrlGroup(new Uri("https://api.external.com"), "External API");
app.MapHealthChecks("/health", new HealthCheckOptions {
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});Response compression reduces the size of HTTP responses by compressing the payload (gzip, brotli).
// Response Compression – reduce payload size
builder.Services.AddResponseCompression(options => {
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "image/svg+xml" });
});
app.UseResponseCompression();Distributed caching using Redis provides a shared cache across multiple servers, improving scalability.
// Distributed Caching with Redis
builder.Services.AddStackExchangeRedisCache(options => {
options.Configuration = "localhost:6379";
options.InstanceName = "SampleInstance";
});
// In a controller
public class CacheController : ControllerBase {
private readonly IDistributedCache _cache;
public CacheController(IDistributedCache cache) => _cache = cache;
public async Task SetAsync(string key, string value) {
await _cache.SetStringAsync(key, value);
}
}Rate limiting controls the number of requests a client can make in a given time window, protecting the API from abuse.
// Rate Limiting (using AspNetCoreRateLimit)
builder.Services.AddMemoryCache();
builder.Services.AddInMemoryRateLimiting();
builder.Services.Configure<IpRateLimitOptions>(options => {
options.GeneralRules = new List<RateLimitRule> {
new RateLimitRule {
Endpoint = "*",
Limit = 100,
Period = "1m"
}
};
});
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();API versioning allows managing multiple versions of an API simultaneously, enabling backward compatibility.
// API Versioning
builder.Services.AddApiVersioning(options => {
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
[ApiVersion("1.0")]
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class ProductsController : ControllerBase { }Globalization adapts an application for multiple cultures, while localization translates UI strings into different languages.
// Globalization and Localization
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.Configure<RequestLocalizationOptions>(options => {
var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("es") };
options.DefaultRequestCulture = new RequestCulture("en");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
app.UseRequestLocalization();SignalR groups allow sending messages to a subset of connected clients, useful for chat rooms or team notifications.
// SignalR Advanced – Groups
public class ChatHub : Hub {
public async Task JoinGroup(string groupName) {
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public async Task SendToGroup(string groupName, string message) {
await Clients.Group(groupName).SendAsync("ReceiveMessage", message);
}
}Blazor WebAssembly runs .NET code directly in the browser, enabling full-stack C# development without JavaScript.
// Blazor WebAssembly – Program.cs
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient {
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
await builder.Build().RunAsync();Blazor Server runs UI logic on the server and uses SignalR to update the client, providing a rich interactive experience.
// Blazor Server – Startup configuration
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");Razor Pages is a page-based programming model that simplifies building web UI in ASP.NET Core, with each page having its own handler methods.
// Razor Pages – Page Model
public class ContactModel : PageModel {
[BindProperty]
public ContactForm Input { get; set; }
public void OnGet() { }
public async Task<IActionResult> OnPostAsync() {
if (!ModelState.IsValid) return Page();
// Process form
return RedirectToPage("Success");
}
}Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor views, providing a more HTML‑friendly syntax.
// Tag Helpers – built-in and custom
<input asp-for="User.Email" class="form-control" />
<label asp-for="User.Email"></label>
// Custom Tag Helper
[HtmlTargetElement("email")]
public class EmailTagHelper : TagHelper {
public override void Process(TagHelperContext context, TagHelperOutput output) {
output.TagName = "a";
output.Attributes.SetAttribute("href", "mailto:test@example.com");
}
}View Components are reusable UI components that encapsulate logic and rendering, similar to partial views but with their own controller-like lifecycle.
// View Components
public class UserListViewComponent : ViewComponent {
private readonly AppDbContext _db;
public UserListViewComponent(AppDbContext db) => _db = db;
public async Task<IViewComponentResult> InvokeAsync(int count) {
var users = await _db.Users.Take(count).ToListAsync();
return View(users);
}
}
// In View
@await Component.InvokeAsync("UserList", new { count = 5 })Custom model binders allow fine‑grained control over how HTTP request data is converted into model objects.
// Custom Model Binder
public class CustomModelBinder : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
var value = bindingContext.ValueProvider.GetValue("id").FirstValue;
// Custom logic
bindingContext.Result = ModelBindingResult.Success(int.Parse(value));
return Task.CompletedTask;
}
}
// Usage
public IActionResult Get([ModelBinder(BinderType = typeof(CustomModelBinder))] int id)Custom validation attributes extend the built‑in validation system to enforce business rules.
// Custom Validator
public class CustomValidator : ValidationAttribute {
protected override ValidationResult IsValid(object value, ValidationContext context) {
if (value == null) return new ValidationResult("Field is required");
// Custom logic
return ValidationResult.Success;
}
}
// Model property
[CustomValidator]
public string Name { get; set; }Exception filters capture and handle exceptions thrown during action execution, providing a centralized error handling strategy.
// Exception Filter
public class CustomExceptionFilter : IExceptionFilter {
private readonly ILogger<CustomExceptionFilter> _logger;
public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger) => _logger = logger;
public void OnException(ExceptionContext context) {
_logger.LogError(context.Exception, "Unhandled exception");
context.Result = new ObjectResult("Internal Server Error") {
StatusCode = 500
};
context.ExceptionHandled = true;
}
}
// Register globally
builder.Services.AddControllers(options => {
options.Filters.Add<CustomExceptionFilter>();
});Result filters run after the action result is produced, allowing modification of the result before it is sent to the client.
// Result Filter – modify response after execution
public class CustomResultFilter : IResultFilter {
public void OnResultExecuting(ResultExecutingContext context) {
// Before result executed
}
public void OnResultExecuted(ResultExecutedContext context) {
// After result executed
}
}Authorization filters run before action execution to check if the user is permitted to access the resource.
// Authorization Filter – check permissions
public class CustomAuthFilter : IAuthorizationFilter {
public void OnAuthorization(AuthorizationFilterContext context) {
if (!context.HttpContext.User.Identity.IsAuthenticated) {
context.Result = new UnauthorizedResult();
}
}
}Action filters run before and after the action method executes, useful for logging, validation, and performance monitoring.
// Action Filter – before/after action method
public class CustomActionFilter : IActionFilter {
public void OnActionExecuting(ActionExecutingContext context) {
// Before action executes
}
public void OnActionExecuted(ActionExecutedContext context) {
// After action executes
}
}Resource filters wrap the entire execution pipeline (model binding, action, result), making them suitable for caching or cross‑cutting concerns.
// Resource Filter – wraps everything (model binding, action, result)
public class CustomResourceFilter : IResourceFilter {
public void OnResourceExecuting(ResourceExecutingContext context) { }
public void OnResourceExecuted(ResourceExecutedContext context) { }
}Middleware runs globally for every request, while filters are scoped to controllers/actions and have access to MVC context. Use middleware for cross‑cutting concerns and filters for action‑specific logic.
// Middleware vs Filters – conceptual difference
// Middleware: global pipeline, runs for every request
// Filters: scoped to controllers/actions, have access to MVC context
// Use middleware for cross-cutting concerns (auth, logging, compression)
// Use filters for action-specific logic (validation, caching, exception handling)HTTPS enforcement ensures all requests use a secure connection, typically by redirecting HTTP to HTTPS.
// HTTPS enforcement
app.UseHttpsRedirection();
// Or globally in Program.cs
builder.Services.AddHttpsRedirection(options => {
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = 5001;
});Anti‑CSRF tokens protect against cross‑site request forgery attacks by requiring a unique token for each state‑changing request.
// Anti-CSRF protection in Razor
@Html.AntiForgeryToken()
// In controller
[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult Submit(FormModel model) { }Cross‑site scripting (XSS) is prevented by automatically encoding output in Razor views and using safe methods like HtmlEncoder for untrusted input.
// XSS Prevention – automatic encoding in Razor
@Html.Raw("<script>alert('XSS')</script>") // Danger – avoid
// Use @ for automatic encoding
@Model.UserInput // Encoded by default
// In code
var encoded = System.Text.Encodings.Web.HtmlEncoder.Default.Encode(userInput);Advanced CORS configurations support credentials, preflight caching, and multiple origins with different policies.
// Advanced CORS – with credentials and preflight
builder.Services.AddCors(options => {
options.AddPolicy("AllowCredentials", policy => {
policy.WithOrigins("https://client.com")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod()
.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
});JWT authentication validates tokens issued by an authentication server, enabling stateless security in APIs.
// JWT Authentication – validation setup
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecretKey")),
ValidateIssuer = false,
ValidateAudience = false
};
});OAuth 2.0 and OpenID Connect are protocols for delegated authorization and authentication, enabling single sign‑on and secure access to resources.
// OAuth 2.0 / OpenID Connect
builder.Services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options => {
options.Authority = "https://identity.example.com";
options.ClientId = "aspnet_client";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.Scope.Add("openid");
options.Scope.Add("profile");
});Multi‑Factor Authentication (MFA) adds an extra layer of security by requiring a second factor, such as a TOTP code from an authenticator app.
// Multi-Factor Authentication (MFA) – two-factor with TOTP
// In Identity configuration
builder.Services.Configure<IdentityOptions>(options => {
options.Tokens.AuthenticatorTokenProvider = "Authenticator";
});
// Enable 2FA in login flow
var user = await _userManager.FindByNameAsync(username);
if (await _userManager.GetTwoFactorEnabledAsync(user)) {
// Redirect to MFA verification
}Serilog is a structured logging library that writes logs to various sinks (console, files, databases, etc.) and supports enriched log events with properties.
// Logging with Serilog
builder.Host.UseSerilog((context, config) => {
config.ReadFrom.Configuration(context.Configuration);
});
// In appsettings.json
{
"Serilog": {
"MinimumLevel": "Information",
"WriteTo": [
{ "Name": "File", "Args": { "path": "logs/log.txt" } }
]
}
}Application Insights is an Azure Monitor feature that collects telemetry data (requests, exceptions, traces) from your application for performance monitoring and diagnostics.
// Application Insights – telemetry
builder.Services.AddApplicationInsightsTelemetry();
// In controller
public IActionResult Get() {
var telemetry = new TelemetryClient(new TelemetryConfiguration());
telemetry.TrackEvent("Get called");
return Ok();
}xUnit is a popular testing framework for .NET that supports fact‑based and theory‑based tests with assertions.
// Unit Testing with xUnit
public class ProductServiceTests {
[Fact]
public void Add_ShouldReturnTrue_WhenProductIsValid() {
// Arrange
var service = new ProductService();
// Act
var result = service.Add(new Product());
// Assert
Assert.True(result);
}
}WebApplicationFactory enables integration tests that spin up an in‑memory test server to verify the full request/response pipeline.
// Integration Testing – WebApplicationFactory
public class IntegrationTests : IClassFixture<WebApplicationFactory<Program>> {
private readonly WebApplicationFactory<Program> _factory;
public IntegrationTests(WebApplicationFactory<Program> factory) => _factory = factory;
[Fact]
public async Task GetProducts_ReturnsOk() {
var client = _factory.CreateClient();
var response = await client.GetAsync("/api/products");
response.EnsureSuccessStatusCode();
}
}Moq is a mocking library that creates mock objects for interfaces or classes, allowing you to isolate units under test.
// Mocking with Moq
public class MockTest {
[Fact]
public void Service_ShouldUseRepository() {
var mockRepo = new Mock<IProductRepository>();
mockRepo.Setup(r => r.GetAll()).Returns(new List<Product>());
var service = new ProductService(mockRepo.Object);
var result = service.GetAll();
Assert.Empty(result);
}
}EF Core’s In‑Memory database provider allows you to write tests without a real database, using a temporary in‑memory store.
// In-Memory Database Testing (EF Core)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("TestDb"));
// In tests
[Fact]
public void AddProduct_SavesToDatabase() {
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;
using var context = new AppDbContext(options);
context.Products.Add(new Product { Name = "Test" });
context.SaveChanges();
Assert.Equal(1, context.Products.Count());
}Docker containers package your application with all dependencies, enabling consistent deployment across environments.
// Docker Deployment – Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]Azure App Service is a fully managed platform for hosting web applications, with CI/CD integration via Azure DevOps or GitHub Actions.
// Azure App Service Deployment – using az CLI
az webapp up --name myapp --resource-group myrg --plan myplan --runtime "DOTNET|8.0"
// Using Azure DevOps YAML
- task: DotNetCoreCLI@2
inputs:
command: publish
arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
- task: AzureWebApp@1
inputs:
azureSubscription: 'my-connection'
appName: 'myapp'
package: '$(Build.ArtifactStagingDirectory)/**/*.zip'Internet Information Services (IIS) is a web server that can host ASP.NET Core applications using the ASP.NET Core Module.
// IIS Deployment – web.config for IIS
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".MyApp.dll" stdoutLogEnabled="true" stdoutLogFile=".logsstdout" />
</system.webServer>
</configuration>BenchmarkDotNet is a powerful library for benchmarking .NET code, providing detailed performance metrics and memory diagnostics.
// Performance Profiling – using BenchmarkDotNet
[MemoryDiagnoser]
public class MyBenchmark {
[Benchmark]
public void MethodA() { }
[Benchmark]
public void MethodB() { }
}
// Run: dotnet run -c Release --filter *MyBenchmark*The IDisposable pattern ensures proper cleanup of unmanaged resources (file handles, database connections) to prevent memory leaks.
// Memory Management – dispose pattern
public class ResourceHolder : IDisposable {
private bool _disposed;
private Stream _stream;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) {
_stream?.Dispose();
}
_disposed = true;
}
}
}Connection pooling reuses database connections to reduce overhead. ADO.NET manages a pool per connection string with configurable size limits.
// Connection Pooling – default in ADO.NET
// Use standard patterns
using (var conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
// Operations
}
// Pool size can be adjusted in connection string:
// "Server=.;Database=MyDb;Pooling=True;Min Pool Size=5;Max Pool Size=100;"gRPC is a high‑performance, language‑agnostic RPC framework that uses Protocol Buffers for efficient binary serialization, ideal for microservice communication.
// gRPC Services – setup
builder.Services.AddGrpc();
app.MapGrpcService<GreeterService>();
public class GreeterService : Greeter.GreeterBase {
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) {
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
}Hot Chocolate is a GraphQL server implementation for .NET that allows clients to request exactly the data they need via a flexible query language.
// GraphQL with Hot Chocolate – schema
builder.Services.AddGraphQLServer()
.AddQueryType<Query>();
public class Query {
public IQueryable<Product> GetProducts([Service] AppDbContext db) => db.Products;
}
// Endpoint: /graphqlWebSockets provide full‑duplex communication channels over a single TCP connection, enabling real‑time bidirectional messaging.
// WebSockets – using WebSocketManager
app.UseWebSockets();
app.Map("/ws", async context => {
if (context.WebSockets.IsWebSocketRequest) {
var socket = await context.WebSockets.AcceptWebSocketAsync();
// Handle WebSocket
}
});SignalR simplifies adding real‑time web functionality, such as live dashboards, by pushing updates from the server to connected clients.
// Real-Time Dashboards – using SignalR
public class DashboardHub : Hub {
public async Task SubscribeToUpdates(string dashboardId) {
await Groups.AddToGroupAsync(Context.ConnectionId, dashboardId);
}
}
// Server-side push
await _hubContext.Clients.Group(dashboardId).SendAsync("UpdateData", data);Quartz.NET is a full‑featured job scheduler that allows you to run recurring tasks using cron expressions or simple intervals.
// Background Task Scheduling – using Quartz.NET
builder.Services.AddQuartz(q => {
q.ScheduleJob<MyJob>(trigger => trigger
.WithIdentity("MyJob")
.WithCronSchedule("0 0/5 * * * ?")
);
});
builder.Services.AddQuartzHostedService();
public class MyJob : IJob {
public Task Execute(IJobExecutionContext context) { /* work */ }
}ASP.NET Core supports environment‑specific configuration via appsettings.{Environment}.json and the IHostEnvironment interface.
// Environment Configuration
var env = builder.Environment;
if (env.IsDevelopment()) { /* dev settings */ }
if (env.IsProduction()) { /* prod settings */ }
// appsettings.Development.json will override default values
var config = builder.Configuration;
var dbConn = config.GetConnectionString("Default");Feature flags allow you to enable or disable features at runtime without redeploying, using the Microsoft.FeatureManagement library.
// Feature Flags – using Microsoft.FeatureManagement
builder.Services.AddFeatureManagement();
// In controller
[FeatureGate("NewFeature")]
public IActionResult NewFeature() { return View(); }
// In view
<feature name="NewFeature">
<p>New feature content</p>
</feature>Minimal APIs provide a lightweight way to build HTTP APIs with minimal boilerplate, focusing on endpoints and dependency injection.
// Minimal APIs – simplified endpoint
var app = builder.Build();
app.MapGet("/", () => "Hello Minimal API");
app.MapPost("/products", (Product product) => Results.Created($"/products/{product.Id}", product));
app.Run();