ASP.NET MVC Questions and Answers

Why OOP is used in C#?
OOP is a fundamental pattern used in C# and it allows you to write scalable, modular, maintainable code.
Example: - Imagine you're building a school management system.
Instead of writing everything in one big program, you can break it down into objects like:
Student
→ has properties likeName
,Age
,Grade
, and methods likeEnroll()
,Promote()
.Teacher
→ has properties likeName
,Subject
, and methods likeAssignGrade()
.Classroom
→ has a list of students and a teacher.
What method do you call to add MVC services to the ASP.NET Core pipeline?
Use builder.Services.AddMVC() or builder.Services.AddControllersWithViews() to register MVC services.
So you can use Controllers, Views, ViewData, ViewBag, etc.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); // or builder.Services.AddMvc(); var app = builder.Build(); app.MapDefaultControllerRoute(); app.Run();
What is Middleware and Why it’s Sequence order important?
ASP.NET Core apps use middleware to handle HTTP requests.
Middleware processes requests/responses in a pipeline.
Because middleware is executed in the order it's added in Startup.cs.
If authentication is first, it checks the user. If valid, it passes to the next step like routing, then to your controller.
Middleware Sequence in ASP.Net Core should be as follows.
public void Configure(IApplicationBuilder app) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); // Shows detailed error pages with stack traces during development. } else { app.UseExceptionHandler("/Home/Error"); // Global error page app.UseHsts(); // Enforces HTTPS via response header //HSTS tells browsers to only use HTTPS. //We avoid it in dev to prevent local redirect issues, //and enable it in production for security compliance. } app.UseHttpsRedirection(); // Redirects HTTP requests to HTTPS app.UseStaticFiles(); // When your app needs to serve frontend assets from wwwroot folder. // (e.g,.html, .js, .css, images). app.UseRouting(); // Matches incoming request URLs to route endpoints // (controller actions, Razor pages, minimal APIs). app.UseAuthentication(); // Validates authentication tokens/cookies and sets the HttpContext.User and also // used if your app uses JWT tokens, cookies, OAuth, etc. app.UseAuthorization(); // Checks if the authenticated user is allowed to access a route ([Authorize] attributes). app.UseCors("AllowAll"); // Enables Cross-Origin Resource Sharing — allows APIs to be accessed from // different domains If needed, must be placed after routing // and before endpoints app.UseEndpoints(endpoints => { endpoints.MapControllers(); // Maps attribute-routed Web API controllers endpoints.MapDefaultControllerRoute(); // Enables default MVC route: {controller=Home}/{action=Index}/{id?} }); }
How do you create custom middleware using Middleware Class?
You create a separate Custom Middleware class with InvokeAsync method that takes the HttpContext as parameter, does some work (like logging or modifying headers), and then calls the next middleware in the pipeline.
Call the Custom Middleware inside Configure method in Startup.cs file as follows
app.UseMiddleware<CustomHeaderMiddleware>();
Clean, reusable, and testable.
Great for complex or reusable logic.
//Define CustomHeaderMiddleware.cs class. public class CustomHeaderMiddleware { private readonly RequestDelegate _next; public CustomHeaderMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { Console.WriteLine("Before next middleware"); context.Response.OnStarting(() => { context.Response.Headers.Add("X-Custom", "Hello from middleware"); return Task.CompletedTask; }); await _next(context); // Pass to the next middleware Console.WriteLine("After next middleware"); } } //How to use in Startup.cs public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); // Register your middleware’s dependencies if needed } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Optional: Developer exception page if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // Your custom middleware goes here app.UseMiddleware<CustomHeaderMiddleware>(); // Built-in middleware app.UseRouting(); app.UseAuthentication(); // if any app.UseAuthorization(); // if any app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
What does MVC stand for and what are its responsibilities?
- By splitting responsibilities in 3 main parts
Model: Handles data and business logic.
View: Renders UI and displays data.
Controller: Processes requests, updates models, returns views.
//Model public class Product { public int Id { get; set; } public string Name { get; set; } } //Controller public class ProductController : Controller { private readonly IProductRepository _repository; public ProductController(IProductRepository repository) { _repository = repository; } public IActionResult List() { var products = _repository.GetAll(); return View(products); } }
<!-- File: Views/Product/List.cshtml --> @model IEnumerable<Product> <h2>Product List</h2> <table class="table"> <thead> <tr> <th>Id</th> <th>Name</th> </tr> </thead> <tbody> @foreach (var product in Model) { <tr> <td>@product.Id</td> <td>@product.Name</td> </tr> } </tbody> </table>
How does routing work in ASP.NET Core?
Routing maps URL paths to controller actions using endpoint routing, and there are 2 types of routing as follows:
Attribute routing:
Routes are defined directly above controller actions using attributes like [Route], [HttpGet], [HttpPost] etc.
Use only when you want fine control over each route.
It causes less confusion and more flexibility when different actions don’t follow a strict naming pattern.
Route Urls like
/get-active-users
or/reset-password/token/{id}
, which can't be easily mapped by convention-based routing.
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
[HttpGet("list")]
public IActionResult GetAll() { ... }
[HttpGet("{id}")]
public IActionResult GetById(int id) { ... }
}
//In Startup.cs → Configure() method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); //This enables attribute routing.
});
}
//Also, in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); // Or AddControllersWithViews() if you're using views.
}
Conventional routing:
Defines URL patterns in Startup.cs (or Program.cs in minimal hosting model).
It relies on naming conventions of controllers and actions.
Use only when your routes follow a simple and consistent pattern.
It causes less repetition in your code because route URLs like
/product/list
(
mapped toProductController.List()
),/order/details/5
(mapped toOrderController.Details(int id)
), and/account/login
(mapped toAccountController.Login()
) are automatically handled without needing explicit route attributes.
//In Startup.cs file
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
//Example URL
/*
- /Home/Index
- /Home/About/5
*/
What's the difference between Authentication and Authorization?
🔐Authentication
Purpose: Identifies who the user is
Sets: HttpContext.User
Middleware: app.UseAuthentication()
Happens before Authorization
🧾 Example: Validating username & password, token, etc.
🔒 Authorization
Purpose: Determines what the user is allowed to do
Uses: Roles, policies, or claims to grant/restrict access
Middleware: app.UseAuthorization()
Happens after Authentication
🧾 Example: Checking if a user has admin role before accessing a route.
Does an ASP.NET MVC application have to use convention-based routing?
No, it's not necessary.
MVC apps can use either convention-based routing or attribute routing — or both together.
Can API-only controllers use convention-based routing in ASP.NET Core?
Technically yes, but it’s not recommended.
ASP.NET Core Web API controllers can use convention-based routing, but attribute routing is the default and preferred approach for APIs.
Partial View vs Layout?
Partial View: reusable chunk of UI (_Header.cshtml)
Layout: template structure (_Layout.cshtml), like a master page.
ViewBag vs ViewData vs TempData
Feature | ViewBag | ViewData | TempData |
Type | Dynamic object | Dictionary (string key, object value) | Dictionary (string key, object value) |
Scope | Single request | Single request | Across multiple requests (survives redirect) |
Purpose | Pass data from controller to view | Pass data from controller to view | Pass data between requests (e.g., after redirect) |
Use Case | Simple data passing | Structured key-value pairs | Success/failure messages after POST-Redirect-GET |
// ViewBag (Controller)
ViewBag.Message = "Hello from ViewBag!";
return View();
// View (Razor)
<p>@ViewBag.Message</p>
// ViewData (Controller)
ViewData["Message"] = "Hello using ViewData!";
return View();
// View (Razor)
<p>@ViewData["Message"]</p>
// TempData (Controller)
TempData["Success"] = "Data saved successfully!";
return RedirectToAction("Index");
// View (Razor)
@if (TempData["Success"] != null)
{
<p>@TempData["Success"]</p>
}
Middleware vs Filters?
⚙️ Middleware:
Runs for every request.
Works before routing.
Used for logging, auth, CORS, etc.
More general/global.
🔎 Filters:
Runs only for controller/action methods.
Works after routing.
Used for validation, error handling in MVC.
More controller-specific.
What are Filters in ASP.NET Core MVC?
In ASP.NET Core MVC, filters allow you to run custom code before or after specific stages in the request pipeline.
The Filters run after routing has selected the controller and action, and allow logic to validate input, log access, handle errors, or modify results.
Types of Filters?
There are five main types of filters, each designed to run at different stages of the request pipeline.
- Authorization Filter
Runs first, before anything else (even model binding).
Determines if the user is authorized to access the resource.
Example use:
[Authorize]
, or custom role checks.
public class MyAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = new UnauthorizedResult();
}
}
}
- Resource Filter
Runs after authorization, but before model binding.
Useful for caching, resource-heavy setups.
Can short-circuit the pipeline before action runs.
public class MyResourceFilter : IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
Console.WriteLine("Resource Filter - Before model binding");
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
Console.WriteLine("Resource Filter - After everything");
}
}
- Action Filter
Runs before and after the action method executes.
Best for validation, logging, preprocessing.
public class MyActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("Action Filter - Before action");
}
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("Action Filter - After action");
}
}
- Exception Filter
Catches unhandled exceptions thrown during controller or action execution.
Good for logging errors or returning custom error responses.
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
Console.WriteLine($"Exception caught: {context.Exception.Message}");
context.Result = new JsonResult(new { error = "Something went wrong" });
context.ExceptionHandled = true;
}
}
- Result Filter
Runs before and after the result is executed (e.g.,
ViewResult
,JsonResult
).Useful for modifying the response, like formatting or wrapping.
public class MyResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
Console.WriteLine("Result Filter - Before result");
}
public void OnResultExecuted(ResultExecutedContext context)
{
Console.WriteLine("Result Filter - After result");
}
}
Mention Filter Execution Order?
- HTTP Request →
[Authorization Filters]
→[Resource Filters]
→ Model Binding →[Action Filters]
→ Controller Action →[Result Filters]
→ Result Executed (e.g., View rendered) → ([Exception Filter]
- if exception occurred at any step) → HTTP Response.
- HTTP Request →
How do you create custom filter class?
Create a custom ActionFilterAttribute to run logic before and after a controller action method executes.
Use OnActionExecuting to perform validation on input parameters (e.g., checking if id exists).
Use OnActionExecuted to log or handle post-action logic after the method completes.
Apply the filter via attribute to either the entire controller or specific actions for targeted behavior.
Demonstrate real use cases like input validation, logging, and flow control within MVC action execution.
public class MyActionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { // Before the action executes var actionName = context.ActionDescriptor.DisplayName; Console.WriteLine($"[Before] Executing action: {actionName}"); // Example: Reject request if no "id" provided if (!context.ActionArguments.ContainsKey("id") || context.ActionArguments["id"] == null) { context.Result = new BadRequestObjectResult("ID is required"); } } public override void OnActionExecuted(ActionExecutedContext context) { // After the action executes Console.WriteLine($"[After] Finished executing action: {context.ActionDescriptor.DisplayName}"); } } //Apply the Filter to a Controller or Action [MyActionFilter] // Applies to all actions in this controller public class UsersController : Controller { public IActionResult GetUser(int id) { // If ID is valid, logic proceeds here return Ok($"User ID: {id}"); } } //Or, apply to just a single action: public class UsersController : Controller { [MyActionFilter] // Applies only to this method public IActionResult GetUser(int id) { return Ok($"User ID: {id}"); } }
What is Dependency Injection in ASP.NET Core?
It’s a design pattern where objects are provided with their dependencies rather than creating them manually. ASP.NET Core has built-in DI.
We used constructor injection to register services like logging, repositories, and config settings.
Difference between AddTransient, AddScoped, and AddSingleton.
AddTransient<TInterface, TImplementation>()
A new instance is created every time the service is used.
Best for stateless and lightweight services.
Use when you don’t need to share state and want a fresh copy every time.
Ideal for simple tasks like data transformation, validation, token generation, or sending emails.
// Sends emails (stateless, no need to retain data) services.AddTransient<IEmailSender, EmailSender>(); // Generates secure tokens or OTPs per request services.AddTransient<ITokenGenerator, JwtTokenGenerator>(); // Cleans and sanitizes input fields services.AddTransient<IInputSanitizer, HtmlInputSanitizer>(); // Formats data for reports — called only when needed services.AddTransient<IReportFormatter, PdfReportFormatter>();
AddScoped<TInterface, TImplementation>()
Creates one instance per HTTP request.
All classes in the same request share the same instance.
Use when you need to share data across layers in a request (e.g., Controller → Service → Repository).
Good for things like request tracking, user info, or per-request caching (e.g.,
DbContext
,UnitOfWork
).// Tracks and manages the current user's data during a request services.AddScoped<IUserService, UserService>(); // Processes an order in checkout flow (data shared across layers) services.AddScoped<IOrderProcessor, OrderProcessor>(); // Holds temporary cart data across steps in a request services.AddScoped<IShoppingCartService, ShoppingCartService>(); // EF Core DbContext – reused in service & repository during a request services.AddScoped<IAppDbContext, AppDbContext>();
AddSingleton<TInterface, TImplementation>()
Creates a single instance for the entire application.
That instance is shared across all users and requests.
Created at startup or the first time it's needed.
Use for shared global data or heavy services like Logger, Config, Cache, or reading a file.
// Logs errors and events — shared by entire app services.AddSingleton<ILoggerService, FileLoggerService>(); // Reads config file once and exposes settings globally services.AddSingleton<IAppConfigurationService, AppConfigurationService>(); // Stores shared in-memory data like tokens or frequent API responses services.AddSingleton<ICacheService, MemoryCacheService>(); // Provides a static list of countries — no need to recreate every time services.AddSingleton<ICountryListProvider, CountryListProvider>();
What is the difference between .NET Framework and .NET Core?
OS Support
.NET Framework: Works only on Windows
.NET Core: Cross-platform — runs on Windows, Linux, and macOS
Open Source
.NET Framework: Not open source.
.NET Core: Fully open source on GitHub
Application Types
.NET Framework: Best for Desktop apps (Windows Forms, WPF) and ASP.NET Web Forms/MVC
.NET Core: Suited for Web APIs, Cloud apps, CLI tools, Microservices, and Cross-platform apps
Performance
.NET Framework: Global Assembly Cache (GAC)-based.
.NET Core: App-local DLLs.
What is EF Core?
- EF Core is an ORM (Object-Relational Mapper) that allows .NET developers to work with databases using .NET objects.
What are DbContext and DbSet in Entity Framework Core?
DbContext is the primary class in Entity Framework Core that acts as a bridge between your .NET application and the database.
It represents a session with the database that manages database connections, tracks entity states (Added, Modified, Deleted) and is used to query and save data.
DbContext
in EF Core is used to interact with the database. It tracks the state of entities, so when I callSaveChanges()
, only the modified records are updated. I useDbSet<T>
to represent tables and query them using LINQ.public class AppDbContext : DbContext { public DbSet<Product> Products { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { //code here.. } }
DbSet<T> represents a table in the database, and each
T
is a row/entity.DbSet<T> is like a typed table in memory connected to the actual table in SQL.
It is used to query, insert, update, and delete entities in the database and acts as a collection of all entities of a given type.
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } // Querying var products = _context.Products.ToList(); // Create/Add _context.Products.Add(new Product { Name = "Pen", Price = 10 }); _context.SaveChanges(); // Read var product = _context.Products.FirstOrDefault(p => p.Id == 1); // Update product.Price = 20; _context.SaveChanges(); // Delete _context.Products.Remove(product); _context.SaveChanges();
Entity Framework Core Methods
//EF Core methods are built-in functions provided by the Entity Framework Core library //It is used to perform database operations like insert, update, delete, and track entity states. _context.Products.Add(product); //Add marks a new entity as added to DbSet. _context.Products.AddRange(product1, product2); // AddRange marks multiple entities at once to the DbSet. _context.Products.Update(product); //Update marks the entire entity as Modified to the Dbset. _context.Products.Remove(product); //Remove marks an entity as Deleted to the DbSet. _context.Products.RemoveRange(products); //RemoveRange marks multiple entities as Deleted to the DbSet. _context.Products.Attach(product); //Attach tells EF to start tracking an entity without marking it for changes //Used for manual tracking. _context.Entry(product).State = EntityState.Modified; //Manually marks an entity as modified. //It Useful when the context isn’t tracking the entity var product = _context.Products.Find(id); //Find() retrieves an entity by primary key. //It uses the local cache first before hitting the database. await _context.Products.AddAsync(product); //AddAsync is an asynchronous method in Entity Framework Core that marks a single entity as added. await _context.Products.AddRangeAsync(listOfProducts); //AddRangeAsync is the async version of AddRange() — it marks a collection of entities as Added so they can be inserted together. _context.Products.UpdateRange(listOfProducts); //Batch versions of Update() for updating multiple entities at once. _context.Products.RemoveRange(listOfProducts); //Batch versions of Remove() for deleting multiple entities at once. var products = _context.Products.AsNoTracking().ToList(); //Disables tracking for read-only scenarios — improves performance and memory usage. //ChangeTracker gives you access to all currently tracked entities and lets you inspect or clear their states. _context.ChangeTracker.Clear(); _context.ChangeTracker.Entries(); _context.Entry(product).State = EntityState.Detached; //Stops tracking the entity _context.SaveChanges(); //SaveChanges() applies all pending changes (Added, Modified, Deleted) to the database in a single transaction. await _context.SaveChangesAsync(); //SaveChangesAsync is the async version of SaveChanges()
LINQ (Method Syntax vs Query Syntax)
- LINQ (Language Integrated Query) provides query capabilities directly in C# for collections or databases.
✅ Select – Projects data from a collection
// Method
var names = employees.Select(e => e.Name);
// Query
var names = from e in employees
select e.Name;
✅ Where – Filters data based on a condition
// Method
var highSalaryEmployees = employees.Where(e => e.Salary > 50000);
// Query
var highSalaryEmployees = from e in employees
where e.Salary > 50000
select e;
✅ OrderBy – Sorts data ascending
// Method
var sortedEmployees = employees.OrderBy(e => e.Salary);
// Query
var sortedEmployees = from e in employees
orderby e.Salary
select e;
✅ OrderByDescending – Sorts data descending
// Method
var sortedEmployees = employees.OrderByDescending(e => e.Salary);
// Query
var sortedEmployees = from e in employees
orderby e.Salary descending
select e;
✅ GroupBy – Groups data based on a key
// Method
var groups = employees.GroupBy(e => e.Department);
// Query
var groups = from e in employees
group e by e.Department;
✅ Join – Joins two collections
// Method
var employeeDepartments = employees.Join(departments,
e => e.DeptId,
d => d.Id,
(e, d) => new { e.Name, Department = d.Name });
// Query
var employeeDepartments = from e in employees
join d in departments on e.DeptId equals d.Id
select new { e.Name, Department = d.Name };
✅ Take – Takes first n elements
// Method
var top3Employees = employees.Take(3);
// Query
var top3Employees = (from e in employees
select e).Take(3);
✅ Skip – Skips first n elements
// Method
var remainingEmployees = employees.Skip(3);
// Query
var remainingEmployees = (from e in employees
select e).Skip(3);
✅ First – Gets first matching element
// Method
var firstHighEarner = employees.First(e => e.Salary > 50000);
// Query
var firstHighEarner = (from e in employees
where e.Salary > 50000
select e).First();
✅ Single – Gets one element (throws if multiple)
// Method
var specificEmployee = employees.Single(e => e.Id == 1);
// Query
var specificEmployee = (from e in employees
where e.Id == 1
select e).Single();
🔹 First
Returns the first element in a sequence.
Throws an exception (
InvalidOperationException
) if the sequence is empty.Can use a predicate:
First(x => x.Age > 18)
returns the first match.
🔹 Single
Returns exactly one element from the sequence.
Throws an exception if the sequence is empty or has more than one element.
Use it when only one item is expected to match (like a unique ID).
✅ FirstOrDefault – Gets first match or default
// Method
var firstEmployee = employees.FirstOrDefault(e => e.Salary > 100000);
// Query
var firstEmployee = (from e in employees
where e.Salary > 100000
select e).FirstOrDefault();
✅ SingleOrDefault – Gets one or default
// Method
var employee = employees.SingleOrDefault(e => e.Id == 100);
// Query
var employee = (from e in employees
where e.Id == 100
select e).SingleOrDefault();
🔹 FirstOrDefault
Similar to
First
, but returnsdefault
value (e.g.,null
for reference types) if no element is found.Does not throw if the sequence is empty.
Use when the sequence may or may not have elements, and you want to avoid exceptions.
🔹 SingleOrDefault
Like Single, but returns default value if the sequence is empty.
Throws an exception if the sequence has more than one element.
Use when zero or one element is expected, but not more than one.
✅ Any – Checks if any element matches
// Method
var hasHighEarners = employees.Any(e => e.Salary > 80000);
// Query
var hasHighEarners = (from e in employees
where e.Salary > 80000
select e).Any();
✅ All – Checks if all elements match
// Method only
var allAboveMinimumWage = employees.All(e => e.Salary > 20000);
✅ Count – Counts matching elements
// Method
var highEarnerCount = employees.Count(e => e.Salary > 50000);
// Query
var highEarnerCount = (from e in employees
where e.Salary > 50000
select e).Count();
✅ Sum – Sum of numeric property
// Method
var totalSalary = employees.Sum(e => e.Salary);
// Query
var totalSalary = (from e in employees
select e.Salary).Sum();
✅ Average – Average of numeric property
// Method
var averageSalary = employees.Average(e => e.Salary);
// Query
var averageSalary = (from e in employees
select e.Salary).Average();
✅ Max – Finds max value
// Method
var highestSalary = employees.Max(e => e.Salary);
// Query
var highestSalary = (from e in employees
select e.Salary).Max();
✅ Min – Finds min value
// Method
var lowestSalary = employees.Min(e => e.Salary);
// Query
var lowestSalary = (from e in employees
select e.Salary).Min();
✅ Distinct – Removes duplicates
// Method
var uniqueDepartments = employees.Select(e => e.Department).Distinct();
// Query
var uniqueDepartments = (from e in employees
select e.Department).Distinct();
✅ Concat – Combines collections
// Method only
var allEmployees = employees.Concat(contractors);
✅ Union – Combines, removes duplicates
// Method only
var uniqueEmployees = employees.Union(contractors);
✅ Intersect – Gets common elements
// Method only
var commonEmployees = employees.Intersect(contractors);
✅ Except – Gets difference
// Method only
var exclusiveEmployees = employees.Except(contractors);
✅ ToList – Converts to List
// Method
var employeeList = employees.ToList();
// Query
var employeeList = (from e in employees
select e).ToList();
✅ ToDictionary – Converts to Dictionary
// Method only
var employeeDict = employees.ToDictionary(e => e.Id, e => e.Name);
✅ SelectMany – Flattens nested collections
// Method only
var allTasks = projects.SelectMany(p => p.Tasks);
✅ DefaultIfEmpty – Returns default if empty
// Method only
var employeesOrDefault = employees.DefaultIfEmpty(
new Employee { Name = "No Employee", Salary = 0 }
);
✅ Zip – Combines two lists
// Method only
var pairedList = employees.Zip(managers, (e, m) =>
new { Employee = e.Name, Manager = m.Name });
✅ Aggregate – Custom accumulation of any logic
// Method only
var totalSales = sales.Aggregate((total, next) => total + next);
✅ INNER JOIN - Joins two sequences where keys match.
// Query Syntax
var result = from e in employees
join d in departments on e.DeptId equals d.Id
select new { e.Name, Department = d.Name };
// Method Syntax
var result = employees.Join(departments,
e => e.DeptId,
d => d.Id,
(e, d) => new { e.Name, Department = d.Name });
✅ LEFT JOIN (GroupJoin + SelectMany + DefaultIfEmpty) - Includes all from left, even if no match in right.
// Query Syntax
var result = from e in employees
join d in departments on e.DeptId equals d.Id into deptGroup
from d in deptGroup.DefaultIfEmpty()
select new { e.Name, Department = d?.Name ?? "No Dept" };
// Method Syntax
var result = employees.GroupJoin(departments, e => e.DeptId, d => d.Id,
(e, ds) => new { e, ds }).SelectMany(ed => ed.ds.DefaultIfEmpty(),
(ed, d) => new { ed.e.Name, Department = d?.Name ?? "No Dept" });
✅ RIGHT JOIN - LINQ doesn’t directly support Right Join, but you can swap the collections in Left Join.
// Query Syntax (simulate Right Join)
var result = from d in departments
join e in employees on d.Id equals e.DeptId into empGroup
from e in empGroup.DefaultIfEmpty()
select new { Employee = e?.Name ?? "No Employee", Department = d.Name };
// Method Syntax
var result = departments.GroupJoin(employees,
d => d.Id,
e => e.DeptId,
(d, es) => new { d, es })
.SelectMany(de => de.es.DefaultIfEmpty(),
(de, e) => new { Employee = e?.Name ?? "No Employee", Department = de.d.Name });
✅ CROSS JOIN (Cartesian Product)
// Query Syntax
var result = from e in employees
from d in departments
select new { e.Name, Department = d.Name };
// Method Syntax
var result = employees.SelectMany(e => departments,
(e, d) => new { e.Name, Department = d.Name });
✅ CROSS APPLY (like SelectMany with logic) - Used when right sequence depends on the left.
// Query Syntax
var result = from e in employees
from p in GetProjectsFor(e.Id)
select new { e.Name, Project = p.Name };
// Method Syntax
var result = employees.SelectMany(e => GetProjectsFor(e.Id),
(e, p) => new { e.Name, Project = p.Name });
✅ OUTER APPLY (like Left Join with a dependent subquery)
// Query Syntax
var result = from e in employees
let projects = GetProjectsFor(e.Id)
from p in projects.DefaultIfEmpty()
select new { e.Name, Project = p?.Name ?? "No Project" };
// Method Syntax
var result = employees.SelectMany(e => GetProjectsFor(e.Id).DefaultIfEmpty(),
(e, p) => new { e.Name, Project = p?.Name ?? "No Project" });
✅ Group Join (used to simulate One-to-Many + Left Joins) - Returns grouped results per key.
// Query Syntax
var result = from d in departments
join e in employees on d.Id equals e.DeptId into empGroup
select new { Department = d.Name, Employees = empGroup };
// Method Syntax
var result = departments.GroupJoin(employees,
d => d.Id,
e => e.DeptId,
(d, empGroup) => new { Department = d.Name, Employees = empGroup });
✅ Multiple Joins (Chain Joins)
// Query Syntax
var result = from e in employees
join d in departments on e.DeptId equals d.Id
join l in locations on d.LocationId equals l.Id
select new { e.Name, d.Name, Location = l.City };
// Method Syntax
var result = employees.Join(departments,
e => e.DeptId,
d => d.Id,
(e, d) => new { e, d })
.Join(locations,
ed => ed.d.LocationId,
l => l.Id,
(ed, l) => new { ed.e.Name, Department = ed.d.Name, Location = l.City });
✅ Self Join (join table with itself)
// Query Syntax
var result = from e1 in employees
join e2 in employees on e1.ManagerId equals e2.Id
select new { Employee = e1.Name, Manager = e2.Name };
// Method Syntax
var result = employees.Join(employees,
e1 => e1.ManagerId,
e2 => e2.Id,
(e1, e2) => new { Employee = e1.Name, Manager = e2.Name });
✅ Anti Join (e.g., Get items in one collection not in another) - Using Except
, Any
, or All
.
// Using Any
var employeesWithoutDept = employees
.Where(e => !departments.Any(d => d.Id == e.DeptId));
// Using Left Join + DefaultIfEmpty (Query Syntax)
var result = from e in employees
join d in departments on e.DeptId equals d.Id into deptGroup
from d in deptGroup.DefaultIfEmpty()
where d == null
select e;
✅ Full Outer Join (Simulated) - LINQ doesn’t support full outer join directly, but we can combine left join and right join using Union
.
csharpCopyEdit// Query Syntax Simulated
var left = from e in employees
join d in departments on e.DeptId equals d.Id into deptGroup
from d in deptGroup.DefaultIfEmpty()
select new { e.Name, Department = d?.Name };
var right = from d in departments
join e in employees on d.Id equals e.DeptId into empGroup
from e in empGroup.DefaultIfEmpty()
select new { Name = e?.Name ?? "No Employee", Department = d.Name };
var fullOuter = left.Union(right);
What is the difference between
[ApiController]
and[Controller]
?- ApiController
[ApiController]
is a real attribute used in Web API controllers.It enables automatic model validation.
It infers binding sources like
[FromBody]
,[FromQuery]
.It returns
400 Bad Request
automatically for invalid models.It adds consistent behavior for HTTP 204 responses.
[ApiController]
is optional but strongly recommended for Web API projects.
- Controller
[Controller]
is not an actual attribute in ASP.NET Core.A class is treated as a controller if it inherits from
Controller
orControllerBase
and ends withController
.
When would you use Dapper over EF Core?
Use Dapper when performance is critical and you want raw SQL control.
Use EF Core when productivity, relationships, and migrations are your priority.
How do you protect an endpoint using Auth0 JWT in .NET Core?
- Use
[Authorize]
and configure JWT authentication inProgram.cs
.
- Use
app.UseAuthentication();
app.UseAuthorization();
[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData() => Ok("This is protected");
How would you implement user authentication and authorization using Auth0 in a .NET Core application?
Configure JWT in
Startup.cs
.Secure endpoints with
[Authorize]
.Use Auth0 to handle token generation.
csharpCopyEditservices.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://YOUR_AUTH0_DOMAIN";
options.Audience = "YOUR_API_IDENTIFIER";
});
What is SignalR?
SignalR is a library in ASP.NET Core that simplifies adding real-time web functionality using WebSockets, Server-Sent Events, or Long Polling.
We used SignalR in our Dental Clinics System to broadcast appointment updates in real-time to the receptionist’s dashboard when a patient checks in. It used DynamoDB + SignalR hub to notify Angular frontend using
ReceiveNotification()
.
- Real-life example use cases?
Live chat systems
Stock ticker updates
Real-time dashboards
Collaborative apps (e.g., Google Docs style)
- How does SignalR work under the hood?
Client → Hub → Server
SignalR auto-negotiates the best transport (WebSocket > SSE > Long Polling)
Maintains persistent connection using Hubs
Basic SignalR Implementation (Code)
Create a Hub
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Register Hub in Program.cs
builder.Services.AddSignalR();
app.MapHub<ChatHub>("/chathub");
Client-side JavaScript (basic)
<script src="https://cdn.jsdelivr.net/npm/@microsoft/signalr@latest/dist/browser/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.build();
connection.on("ReceiveMessage", (user, message) => {
console.log(`${user}: ${message}`);
});
connection.start().then(() => {
connection.invoke("SendMessage", "Afroz", "Hello from client!");
});
</script>
- How to send messages to specific clients or groups?
// Send to specific connection
await Clients.Client(connectionId).SendAsync("ReceiveMessage", user, msg);
// To group
await Groups.AddToGroupAsync(Context.ConnectionId, "Admins");
await Clients.Group("Admins").SendAsync("ReceiveMessage", "System", "Admin update only.");
WebSocket – Basic Idea
✅ What is WebSocket?
A persistent, full-duplex communication channel between client and server over a single TCP connection.
Unlike HTTP (request–response), WebSockets keep the connection open and allow real-time bidirectional data flow.
✅ Use Cases:
Chat apps
Multiplayer games
Live dashboards
Notification systems
Difference: WebSocket vs SignalR.
- SignalR uses WebSocket internally when available but falls back gracefully.
📡 WebSocket (Raw)
Abstraction: Low (manual work required)
Ease of Use: Harder
Protocol Fallback: No fallback support
Built-in Features: None
🚀 SignalR
Abstraction: High (automates fallback and state management)
Ease of Use: Easier
Protocol Fallback: Yes (supports SSE, Long Polling)
Built-in Features: Groups, users, scaling, reconnection
✅ 1. Enable WebSockets in Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(context, webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
async Task Echo(HttpContext context, WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, result.Count),
result.MessageType,
result.EndOfMessage,
CancellationToken.None);
result = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
2. Client-Side HTML + JS
<input type="text" id="msg" />
<button onclick="send()">Send</button>
<script>
const socket = new WebSocket("ws://localhost:5000/ws");
socket.onopen = () => console.log("Connected");
socket.onmessage = e => console.log("Server says:", e.data);
function send() {
const msg = document.getElementById("msg").value;
socket.send(msg);
}
</script>
🛠️ In Traditional ASP.NET MVC (.NET Framework)?
ASP.NET Framework does not natively support WebSockets easily — you'd need:
WebSocket support in IIS 8+ and Windows Server 2012+
Or use SignalR (recommended) because it works better with MVC out of the box.
If the interviewer asks about WebSockets:
- Say “We use SignalR in .NET Core which builds on WebSockets when available.”
What is the difference between .NET Framework 4.8 and .NET 8?
🖥️ Platform
.NET Framework 4.8: Windows-only
.NET 8: Cross-platform
🔓 Open Source
.NET Framework 4.8: No
.NET 8: Yes
⚙️ Performance
.NET Framework 4.8: Slower
.NET 8: Much faster
🚀 Deployment
.NET Framework 4.8: Requires IIS/Windows
.NET 8: Can self-host via Kestrel
🖼️ UI Support
.NET Framework 4.8: WinForms, WPF
.NET 8: WinForms (Windows only), MAUI
🔄 Future Updates
.NET Framework 4.8: No major future updates
.NET 8: Actively developed
Why migrate from .NET Framework to .NET 8?
For performance, security, and cross-platform compatibility
To take advantage of modern C# features, minimal APIs, containers
Because .NET Framework is feature-frozen since 4.8
What features were introduced in C# 7.3 and 8.0?
✅ Supported in Both
ref
,readonly
,in
parameters
🚫 C# 7.3 Only
- None beyond the above
✅ C# 8.0 Only
Nullable Reference Types
Switch Expressions
Async Streams (
await foreach
)Default Interface Methods
What does this return and why?
string? name = null; Console.WriteLine(name.Length);
- In C# 8.0 with nullable reference types enabled, this gives a compile-time warning: possible dereference of null.
In earlier versions, this compiles but throws a runtimeNullReferenceException
.
- In C# 8.0 with nullable reference types enabled, this gives a compile-time warning: possible dereference of null.
How do you return proper status codes in Web API?
Answer: Use built-in methods:
return Ok(data); // 200 return NotFound(); // 404 return BadRequest("Error msg"); // 400 return StatusCode(500, "Server error"); // custom
How do you version APIs in ASP.NET Core?
Answer: Use NuGet:
Microsoft.AspNetCore.Mvc.Versioning
[ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ProductsController : ControllerBase
In
Program.cs
:services.AddApiVersioning(o => { o.DefaultApiVersion = new ApiVersion(1, 0); o.AssumeDefaultVersionWhenUnspecified = true; o.ReportApiVersions = true; });
What is dependency injection and how is it used in Web API?
Answer: Services are registered in
Program.cs
and injected via constructor.// Register builder.Services.AddScoped<IProductService, ProductService>(); // Use in controller public class ProductController : ControllerBase { private readonly IProductService _service; public ProductController(IProductService service) { _service = service; } }
How do you secure a Web API?
Use JWT Bearer tokens (e.g., Auth0)
Add
[Authorize]
on controllersRegister middleware in
Program.cs
:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(...);
app.UseAuthentication();
app.UseAuthorization();
How do you enable CORS in Web API?
builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); }); app.UseCors("AllowAll");
How do you implement file upload in Web API?
[HttpPost("upload")] public async Task<IActionResult> Upload(IFormFile file) { var path = Path.Combine("Uploads", file.FileName); using var stream = new FileStream(path, FileMode.Create); await file.CopyToAsync(stream); return Ok("Uploaded"); }
What is Model Binding and Validation in Web API?
Model binding auto-maps request data to C# objects.
Validation via
[Required]
,[StringLength]
, etc.Use
ModelState.IsValid
to check before saving.
public class Product {
[Required] public string Name { get; set; }
}
🔗 What is HATEOAS?
HATEOAS stands for: Hypermedia As The Engine Of Application State
It's a constraint of REST where the server provides links (URLs) in the response so the client knows what to do next, without needing hardcoded paths.
You're using an app and instead of guessing the next screen or button, the app tells you:
“Hey, if you want to
update
, click this link. If you want todelete
, go here.”
🧪 Simple Example (Without HATEOAS):
{ "id": 1, "name": "Book", "price": 10 }
✅ With HATEOAS:
{ "id": 1, "name": "Book", "price": 10, "links": [ { "rel": "self", "href": "/api/books/1" }, { "rel": "update", "href": "/api/books/1", "method": "PUT" }, { "rel": "delete", "href": "/api/books/1", "method": "DELETE" } ] }
rel
= relationship (what the link is for)href
= where to gomethod
= what HTTP method to use
💻 .NET Example – Creating a HATEOAS response:
public class BookWithLinks : Book { public List<Link> Links { get; set; } = new(); } public class Link { public string Rel { get; set; } public string Href { get; set; } public string Method { get; set; } }
In your controller:
var book = new BookWithLinks { Id = 1, Name = "Book", Price = 10, Links = new List<Link> { new Link { Rel = "self", Href = $"/api/books/1", Method = "GET" }, new Link { Rel = "update", Href = $"/api/books/1", Method = "PUT" }, new Link { Rel = "delete", Href = $"/api/books/1", Method = "DELETE" } } };
📦 Real-World Use
Makes API self-explaining
Useful in hypermedia-driven REST clients
Common in HAL, JSON-LD, GraphQL alternatives
Dapper:
Dapper is a lightweight, high-performance micro-ORM (Object Relational Mapper) for .NET that simplifies data access by mapping SQL query results to C# objects using extension methods on IDbConnection.
Types of Dapper method are as follows:
✅ 1. Query<T>()
- Synchronously executes a query and maps the result to a list of objects.
var users = connection.Query<User>("SELECT * FROM Users");
✅ 2. QueryAsync<T>()
- Asynchronously executes a query and maps results to objects.
var users = await connection.QueryAsync<User>("SELECT * FROM Users");
✅ 3. QueryFirst<T>()
- Returns the first row of the result set, throws if none.
var user = connection.QueryFirst<User>("SELECT * FROM Users WHERE Id = @id", new { id = 1 });
✅ 4. QueryFirstOrDefault<T>()
- Returns the first row or default (null) if no result.
var user = connection.QueryFirstOrDefault<User>("SELECT * FROM Users WHERE Id = @id", new { id = 999 });
✅ 5. QuerySingle<T>()
- Expects exactly one row; throws if zero or more than one.
var user = connection.QuerySingle<User>("SELECT * FROM Users WHERE Email = @email", new { email = "test@example.com" });
✅ 6. QuerySingleOrDefault<T>()
- Returns single row or null; throws if more than one row.
var user = connection.QuerySingleOrDefault<User>("SELECT * FROM Users WHERE Email = @email", new { email = "nonexistent@example.com" });
✅ 7. Execute()
- Executes an INSERT, UPDATE, or DELETE. Returns affected row count.
int rows = connection.Execute("DELETE FROM Users WHERE Id = @id", new { id = 5 });
✅ 8. ExecuteAsync()
- Async version of
Execute()
.
int rows = await connection.ExecuteAsync("UPDATE Users SET IsActive = 1 WHERE Id = @id", new { id = 3 });
✅ 9. ExecuteScalar<T>()
- Executes a query and returns a single value (e.g., count).
int count = connection.ExecuteScalar<int>("SELECT COUNT(*) FROM Users");
✅ 10. ExecuteReader()
- Returns a raw
DbDataReader
for manual data reading.
using var reader = connection.ExecuteReader("SELECT * FROM Users");
✅ 11. QueryMultiple()
- Executes multiple SQL queries in a single call and reads results separately.
using var multi = connection.QueryMultiple("SELECT * FROM Users; SELECT * FROM Roles");
var users = multi.Read<User>().ToList();
var roles = multi.Read<Role>().ToList();
✅ 12. QueryAsync<T>(string, CommandType.StoredProcedure)
- You can also call stored procedures easily.
var result = await connection.QueryAsync<User>("GetActiveUsers", commandType: CommandType.StoredProcedure);
✅ 13. Dynamic Parameters
(via DynamicParameters
class)
- Allows passing IN, OUT, and RETURN parameters to stored procs.
var parameters = new DynamicParameters();
parameters.Add("@Id", 1);
var user = connection.QueryFirst<User>("GetUserById", parameters, commandType: CommandType.StoredProcedure);
✅ 14. Buffered = false
(for streaming large results)
- Prevents loading entire result set into memory.
var users = connection.Query<User>("SELECT * FROM BigTable", buffered: false);
✅ 15. Type Handlers
- Lets you map custom types (like enums) to DB types.
SqlMapper.AddTypeHandler(new MyEnumHandler());
✅ 16. Transaction Support
- You can use
IDbTransaction
to wrap Dapper calls in transactions.
using var transaction = connection.BeginTransaction();
connection.Execute("DELETE FROM Orders WHERE Id = @id", new { id = 10 }, transaction);
transaction.Commit();
✅ 17. Dapper.Contrib
(optional extension)
- Adds methods like
Insert
,Update
,Get
,Delete
.
var user = connection.Get<User>(1); // Needs Dapper.Contrib.Extensions
What are generics?
Generics allow reusable, type-safe code. Prevents runtime casting errors and enables compile-time checks.
Generics use angle brackets to define type placeholders.
Here, T can be any type: int, decimal, string, MyClass, etc.
When to use generics?
You want to write reusable code that works with different data types
You want type safety at compile time (avoid runtime casting errors)
You're working with collections, like List, Dictionary<TKey, TValue>, etc.
public class Box<T> { public T Content; public void PrintContent() { Console.WriteLine(Content); } } Box<string> nameBox = new Box<string>(); nameBox.Content = "Umar"; nameBox.PrintContent(); // Output: Umar Box<int> numberBox = new Box<int>(); numberBox.Content = 42; numberBox.PrintContent(); // Output: 42
How to handle exceptions in C#?
You use try-catch-finally blocks.
It Prevents app crashes, helps recover from errors, improves user experience.
try
: Place the code that might throw an errorcatch
: Handle specific exceptionsfinally
: (Optional) Runs no matter what—used for cleanup (closing files, releasing resources, etc.)
Best Practices:
Catch only what you can handle.
Use specific exceptions first, then general Exception last.
Always log or give meaningful messages to help debugging.
Avoid silent catches.
Use finally for resource release.
Access modifiers in C#?
public
: When you want a member to be accessible from anywhere, including other classes, assemblies, and projects.internal
: When you want to allow access only within the same assembly/project, even by unrelated classes.protected
: When you want to share a member with derived classes, but still keep it hidden from unrelated classes, even within the same assembly.private
: When you want to restrict access to a member strictly within the same class, ensuring that no external class or derived class can access or modify it.protected internal
: When access is needed from derived classes across assemblies, but also from any class within the same assembly, whether related or not.private protected
: When you want access to be restricted to derived classes, but only within the same assembly—not from other assemblies.
✅
async
/await
/Task
in CWhat is
async
?Definition: Marks a method as asynchronous so it can use the
await
keyword and return control to the caller while waiting.public async Task DoWorkAsync() { ... }
What is await?
- Definition:
await
pauses the method execution until the awaited asynchronous Task completes.
- Definition:
await SomeAsyncMethod();
What is
Task
?Definition:
Task
represents an asynchronous operation.Task
is used for methods returning no result (void
-like).Task<T>
is for methods returning a result (T
).public async Task<int> GetDataAsync() => 42;
Why use Task over void?
✅ Task:
Can be awaited.
Exceptions can be caught.
Can track completion.
❌ Avoid async
void
:Can't be awaited.
Hard to catch exceptions.
Only use for event handlers.
Common Async Methods in C#
Task.Run
()
Executes CPU-bound code on a background thread.Task.Delay(milliseconds)
Creates a non-blocking delay for the given time.ConfigureAwait(false)
Prevents capturing the current synchronization context (useful in library code to avoid deadlocks).Task.WhenAll(tasks)
Waits for all provided tasks to complete; returns when all are done.Task.WhenAny(tasks)
Returns the first task that completes out of multiple tasks.
Examples of Common Async Methods in C#
- Simple Async Method:
public async Task<string> GetNameAsync()
{
await Task.Delay(1000); // Simulate delay
return "John";
}
- Using WhenAll:
await Task.WhenAll(Task1(), Task2());
- Using WhenAny:
1var finishedTask = await Task.WhenAny(Task1(), Task2());
- Async with Dapper:
var user = await connection.QueryFirstOrDefaultAsync<User>(
"SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });
How do you debug a production issue in .NET Core?
Check logs (e.g., Serilog)
Reproduce with test data
Use try/catch + logging
Use tools like Postman for endpoint testing.
Use Mordern AI Tools.
Contact Seniors before adding any changes.
Subscribe to my newsletter
Read articles from Afroz Kazi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
