Top 50 .NET & C# Interview Questions with Code Examples
A curated set of 50 high-frequency .NET and C# interview questions with clear, production-grade answers and concept-clarifying code examples. Covers C# language fundamentals, the CLR and garbage collection, OOP in C#, ASP.NET Core & MVC, REST Web API, Entity Framework, LINQ and design patterns — for .NET developer and full-stack interviews.
C# Language (13)
Q7.What's the difference between value types and reference types in C#?Beginner
Value types (int, double, bool, struct, enum) hold their data directly and are typically stored on the stack (or inline in the containing object); assigning one copies the value. Reference types (class, string, array, object, delegate) hold a reference to data on the managed heap; assigning copies the reference, so both variables point to the same object. This is the root of most 'why did my object change?' bugs.
struct PointV { public int X; }
class PointR { public int X; }
var v1 = new PointV { X = 1 };
var v2 = v1; v2.X = 99; // copy of the value
Console.WriteLine(v1.X); // 1 (independent)
var r1 = new PointR { X = 1 };
var r2 = r1; r2.X = 99; // copy of the reference
Console.WriteLine(r1.X); // 99 (same object)Q8.What is boxing and unboxing in C#?Intermediate
Boxing wraps a value type in an object on the heap so it can be treated as a reference type (e.g., assigning an int to object). Unboxing extracts the value type back out, with a runtime type check and a copy. Both have performance and allocation cost. Generics (List<int> instead of ArrayList) and avoiding object parameters eliminate most boxing.
int n = 42;
object boxed = n; // BOXING: int copied onto the heap
int unboxed = (int)boxed; // UNBOXING: type-checked + copied back
// Hidden boxing in a non-generic collection:
ArrayList list = new(); list.Add(42); // boxes every int
List<int> good = new(); good.Add(42); // no boxing (generic)Q9.What's the difference between const and readonly in C#?Beginner
const is a compile-time constant — its value is baked into callers at compile time and must be a literal known then; it's implicitly static. readonly is a runtime constant — assigned at declaration or in the constructor, so it can be computed or differ per instance, and it can hold reference types. Rule: use const for true compile-time literals (Pi, MaxRetries); use readonly for values fixed at construction.
class Config {
public const double Pi = 3.14159; // compile-time literal
public readonly DateTime CreatedAt; // runtime constant
public Config() {
CreatedAt = DateTime.Now; // ok: set in constructor
// Pi = 3.14; // ERROR: const is fixed
}
}Q10.What's the difference between ref and out parameters in C#?Intermediate
Both pass arguments by reference so the method can modify the caller's variable. ref requires the variable to be initialized before the call and the method may read it. out does NOT require prior initialization and the method MUST assign it before returning — used for returning extra values. Modern C# also has 'in' (pass by reference, read-only).
void Double(ref int x) { x *= 2; } // reads + writes
bool TryGet(out int y) { y = 5; return true; } // must assign y
int a = 10;
Double(ref a); // a must be initialized first
Console.WriteLine(a); // 20
if (int.TryParse("42", out int parsed)) // out: no init needed
Console.WriteLine(parsed); // 42Q11.What's the difference between string and StringBuilder in C#?Beginner
string is immutable — every modification creates a new string object. Concatenating in a loop with + creates many temporary strings (O(n²) and heavy GC). StringBuilder is a mutable buffer; appending modifies it in place (amortized O(n)). Use string for a few fixed concatenations; use StringBuilder when building strings in loops or with many appends.
// BAD: allocates a new string each iteration (O(n^2))
string s = "";
for (int i = 0; i < 10000; i++) s += i;
// GOOD: one growing buffer (O(n))
var sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.Append(i);
string result = sb.ToString();Q12.What's the difference between var, dynamic and object in C#?Intermediate
var is compile-time type inference — the type is fixed at compile time, fully type-checked, just less verbose. object is the base type of everything — strongly typed but you must cast to use members (boxing for value types). dynamic bypasses compile-time checking entirely; member resolution happens at runtime (DLR), so typos compile but throw at runtime. Prefer var; use dynamic only for interop (COM, JSON, reflection-heavy code).
var v = 5; // int, fixed at compile time, type-checked
object o = 5; // boxed; must cast to use as int
int x = (int)o;
dynamic d = 5;
d.Foo(); // compiles, but throws RuntimeBinderException
// at runtime (no compile-time check)Q13.What is the difference between IEnumerable and IQueryable in LINQ?Intermediate
IEnumerable<T> executes queries in-memory (LINQ to Objects) — data is fetched first, then filtering/sorting happens in your process. IQueryable<T> builds an expression tree that the provider (e.g., Entity Framework) translates to a backend query (SQL) so filtering happens at the database. For DB work use IQueryable so Where/Take run as SQL; switching to IEnumerable too early pulls the whole table into memory.
// IQueryable: filter runs in SQL -> SELECT TOP 10 ... WHERE Active=1
IQueryable<User> q = db.Users.Where(u => u.Active).Take(10);
// AsEnumerable() too early: downloads EVERY user, then filters in C#
var bad = db.Users.AsEnumerable()
.Where(u => u.Active).Take(10); // whole table loaded!Q14.What's the difference between IEnumerable and List in C#?Beginner
IEnumerable<T> is a read-only, forward-only abstraction for iterating a sequence — it may be lazily evaluated and doesn't expose Count or indexing. List<T> is a concrete, in-memory, indexable, mutable collection (Add/Remove/[]), and is itself an IEnumerable. Accept IEnumerable<T> in method parameters for flexibility; return/use List<T> when you need indexing, Count, or to materialize results once.
// Accept the abstraction (works with arrays, lists, LINQ queries)
void Process(IEnumerable<Order> orders) {
// orders[0] // ERROR: no indexer on IEnumerable
// orders.Count // ERROR: no Count property
var list = orders.ToList(); // materialize for indexing/Count
Console.WriteLine(list.Count);
}Q15.What are extension methods in C#?Intermediate
Extension methods let you add methods to an existing type (even sealed types or interfaces you don't own) without modifying it or subclassing. They are static methods in a static class whose first parameter is prefixed with 'this'. The compiler lets you call them as if they were instance methods. The entire LINQ API (Where, Select, etc.) is extension methods on IEnumerable<T>.
public static class StringExtensions {
public static bool IsNullOrEmpty(this string s)
=> string.IsNullOrEmpty(s);
}
string name = "";
bool empty = name.IsNullOrEmpty(); // called like an instance method
// (the entire LINQ Where/Select API is built this way)Q16.What's the difference between a delegate and an event in C#?Advanced
A delegate is a type-safe function pointer — a variable that holds a reference to one or more methods. An event is a restricted wrapper around a delegate that only allows += (subscribe) and -= (unsubscribe) from outside the declaring class; subscribers can't invoke it or overwrite other subscribers. Events implement the publish-subscribe pattern safely; Action/Func are common delegate types.
class Button {
public event EventHandler? Click; // event: safe outside
public void Press() => Click?.Invoke(this, EventArgs.Empty);
}
var b = new Button();
b.Click += (s, e) => Console.WriteLine("clicked"); // subscribe ok
// b.Click = null; // ERROR outside the class (event protects it)
// b.Click.Invoke(...) // ERROR outside the classQ17.What is async/await and how does it work in C#?Advanced
async/await enables non-blocking asynchronous code that reads like synchronous code. An async method returns Task/Task<T> (or ValueTask). await suspends the method at an awaited operation, returns the thread to the pool, and resumes when the operation completes — so threads aren't blocked waiting on I/O. It improves scalability (more requests per thread), not raw speed. Use it for I/O (DB, HTTP, files); avoid async void except for event handlers.
async Task<string> GetDataAsync() {
using var client = new HttpClient();
// While the HTTP call is in flight, the thread is FREED
// to serve other work — not blocked waiting.
string data = await client.GetStringAsync("https://api.x.com");
return data;
}Q18.What's the difference between Convert.ToString() and .ToString()?Intermediate
.ToString() throws a NullReferenceException if the object is null, because you're calling an instance method on null. Convert.ToString() is null-safe — it returns an empty string (or null for the nullable overload) instead of throwing. For known non-null values both are equivalent; Convert.ToString() is safer when the value might be null.
object? o = null;
// o.ToString(); // NullReferenceException
string a = Convert.ToString(o); // "" (null-safe)
string b = o?.ToString() ?? ""; // "" (null-conditional)
Console.WriteLine($"[{a}]"); // []Q19.What are mutable and immutable types in C#? Give examples.Intermediate
A mutable object's state can change after creation (e.g., List<T>, StringBuilder, a class with public setters). An immutable object's state can't change once constructed (e.g., string, record types, a class with only readonly/init-only fields). Immutability gives thread safety (shareable without locks), predictability, and safe use as dictionary keys. C# 'record' types and 'init'-only setters make immutability easy.
record Person(string Name, int Age); // immutable by default
var p1 = new Person("Sam", 30);
// p1.Name = "Bo"; // ERROR: init-only
var p2 = p1 with { Name = "Bo" }; // non-destructive copy
Console.WriteLine(p1.Name); // "Sam" (unchanged)
Console.WriteLine(p2.Name); // "Bo".NET & CLR Internals (6)
Q1.What is the CLR (Common Language Runtime) in .NET?Beginner
The CLR is the execution engine of .NET. It runs your compiled code and provides core services: JIT compilation of IL to native code, automatic memory management (garbage collection), type safety, exception handling, security, and thread management. Any .NET language (C#, F#, VB) compiles to a common Intermediate Language (IL/MSIL) that the CLR executes — which is why they interoperate.
// C#, F# and VB all compile to the SAME IL,
// which the CLR JIT-compiles to native code at runtime.
// Program.cs --(csc)--> IL (Program.dll) --(CLR JIT)--> native
int x = 2 + 3; // C# source
// ldc.i4.2 / ldc.i4.3 / add <-- the IL the CLR runs
Console.WriteLine(x); // 5Q2.What is the JIT compiler and how does it work in .NET?Intermediate
The Just-In-Time (JIT) compiler converts IL (Intermediate Language) into native machine code at runtime, method by method, the first time each method is called. Variants: standard JIT (compiles on demand), and Tiered Compilation (a quick tier first for fast startup, then a fully optimized tier for hot methods). AOT/ReadyToRun can precompile to cut startup cost.
void Hot() {
for (int i = 0; i < 1_000_000; i++) Work(i);
}
// 1st call to Work(): JIT compiles it (Tier 0 - quick).
// After many calls: re-JITted at Tier 1 with full optimizations
// (inlining, loop hoisting) because it's now a 'hot' method.Q3.Explain garbage collection and generations in .NET.Intermediate
The .NET GC automatically reclaims managed heap memory no longer referenced. It's generational: Gen 0 (new, short-lived objects — collected often and cheaply), Gen 1 (survivors — a buffer), and Gen 2 (long-lived objects — collected rarely). Large objects (≥85 KB) go on the Large Object Heap (LOH). Most objects die young, so collecting Gen 0 frequently is efficient. You don't free memory manually, but you must Dispose unmanaged resources.
var temp = new byte[100]; // allocated in Gen 0
// ...goes out of scope...
// Next Gen 0 collection reclaims it cheaply.
Console.WriteLine(GC.GetGeneration(temp)); // 0
GC.Collect(); // (almost always WRONG to call manually)
GC.WaitForPendingFinalizers();Q4.What's the difference between managed and unmanaged code?Intermediate
Managed code runs under the CLR, which provides garbage collection, type safety and security (e.g., C#). Unmanaged code runs directly on the OS with no CLR services — you manage memory yourself (e.g., C/C++, Win32 APIs, COM). When managed code calls unmanaged resources (file handles, DB connections, native libraries via P/Invoke), you must release them explicitly with Dispose/using because the GC doesn't track them.
// The GC frees the managed SqlConnection wrapper, but the
// underlying UNMANAGED native handle must be released explicitly.
using (var conn = new SqlConnection(connStr)) // using => Dispose()
{
conn.Open();
// work...
} // conn.Dispose() releases the unmanaged handle hereQ5.What's the difference between .NET Framework, .NET Core and modern .NET (.NET 5+)?Intermediate
.NET Framework is the original Windows-only platform (legacy, max version 4.8). .NET Core is the cross-platform, open-source, high-performance rewrite. Since .NET 5 they unified into just '.NET' (one cross-platform runtime, current is .NET 8/9). .NET Standard was a shared API spec so libraries could target both worlds — now largely obsolete since everything converged on .NET. New projects should target modern .NET.
<!-- .csproj target frameworks -->
<TargetFramework>net48</TargetFramework> <!-- legacy, Windows only -->
<TargetFramework>netcoreapp3.1</TargetFramework> <!-- old .NET Core -->
<TargetFramework>net8.0</TargetFramework> <!-- modern, cross-platform -->
<TargetFramework>netstandard2.0</TargetFramework> <!-- shared lib spec -->Q6.What is an assembly in .NET, and how does it differ from a class library?Advanced
An assembly is the unit of deployment, versioning and security in .NET — a compiled .dll or .exe containing IL plus a manifest (metadata about types, versions and dependencies). A class library is a project type that produces a .dll assembly of reusable types with no entry point. So 'assembly' is the runtime/deployment concept; 'class library' is the project that builds one. Assemblies can be private (app folder) or shared (the GAC, in .NET Framework).
// A class library project builds ONE assembly (MyApp.Data.dll)
// whose manifest records its version + dependencies.
using System.Reflection;
Assembly asm = typeof(OrderRepository).Assembly;
Console.WriteLine(asm.GetName().Name); // "MyApp.Data"
Console.WriteLine(asm.GetName().Version); // 1.0.0.0OOP in C# (7)
Q20.What are the four pillars of OOP, and how does C# implement them?Beginner
Encapsulation — hide state behind access modifiers and properties (private fields + public getters/setters). Inheritance — derive classes with ': BaseClass'. Polymorphism — virtual/override for runtime, overloading for compile-time. Abstraction — abstract classes and interfaces expose 'what' while hiding 'how'. Together they enable modular, reusable, maintainable code. For pure OOP depth see our OOP interview set.
interface IAccount { void Deposit(decimal amt); } // abstraction
class BankAccount : IAccount { // inheritance / abstraction
private decimal _balance; // encapsulation
public virtual void Deposit(decimal amt) => _balance += amt;
}
class SavingsAccount : BankAccount {
public override void Deposit(decimal amt) // polymorphism
=> base.Deposit(amt * 1.01m);
}Q21.What's the difference between method overloading and overriding in C#?Intermediate
Overloading: multiple methods with the same name but different parameter lists in the same class — resolved at COMPILE time (static binding). Overriding: a derived class redefines a base method marked virtual/abstract using the override keyword — resolved at RUNTIME (dynamic dispatch) based on the object's actual type. Overloading is compile-time polymorphism; overriding is runtime polymorphism.
class Shape {
public int Area(int s) => s * s; // overload 1
public int Area(int w, int h) => w * h; // overload 2 (compile-time)
public virtual void Draw() => Console.WriteLine("shape");
}
class Circle : Shape {
public override void Draw() => Console.WriteLine("circle"); // runtime
}
Shape s = new Circle();
s.Draw(); // "circle" — runtime dispatch picks Circle's overrideQ22.Explain virtual, override and new in C#. What does this print: A a = new C(); a.foo();?Advanced
virtual marks a base method as overridable; override replaces it with runtime dispatch; new HIDES the base method (compile-time, based on the reference type). If A.foo is virtual and B and C override it, then 'A a = new C(); a.foo();' prints 'C' — runtime dispatch follows the actual object (C). But if foo is NON-virtual (plain methods, hidden via new), 'a.foo()' prints 'A' — it binds to the reference type A at compile time. This exact trick is a classic .NET interview question.
class A { public virtual void Foo() => Console.WriteLine("A"); }
class B : A { public override void Foo() => Console.WriteLine("B"); }
class C : B { public override void Foo() => Console.WriteLine("C"); }
A a = new C(); a.Foo(); // "C" (virtual => runtime dispatch)
// If Foo were NON-virtual (plain method) in all three:
// A a = new C(); a.Foo(); // "A" (binds to reference type A)Q23.What's the difference between an abstract class and an interface in C#?Intermediate
An abstract class can have state (fields), constructors, and both abstract and concrete members; a class can inherit only ONE. An interface is a contract — historically members-only, but since C# 8 it can have default implementations; a class can implement MANY interfaces. Use an abstract class for a shared base with common state/behavior (is-a); use an interface to declare a capability multiple unrelated types can have (can-do).
abstract class Animal { // shared state + base behavior
public string Name = ""; // fields allowed
public abstract string Speak(); // must override
public void Sleep() => Console.WriteLine("zzz"); // concrete
}
interface IComparable<T> { int CompareTo(T other); } // capability
class Dog : Animal, IComparable<Dog> { // one class + many interfaces
public override string Speak() => "Woof";
public int CompareTo(Dog o) => 0;
}Q24.What's the difference between abstraction and encapsulation?Intermediate
Abstraction is about DESIGN — exposing only essential behavior and hiding complexity (achieved with interfaces/abstract classes). Encapsulation is about IMPLEMENTATION — bundling data with methods and restricting direct access to internal state (achieved with access modifiers and properties). Abstraction = hide complexity (what); encapsulation = hide data (how). They reinforce each other.
interface ICar { void Drive(); } // ABSTRACTION: only 'what'
class Car : ICar {
private int _fuel; // ENCAPSULATION: hidden state
public void Drive() { // exposes behavior, hides 'how'
if (_fuel <= 0) Refuel();
_fuel--;
}
private void Refuel() => _fuel = 100; // internal detail
}Q25.What's the difference between association, aggregation and composition?Advanced
All describe 'has-a' relationships with increasing ownership. Association: objects know each other but have independent lifecycles (a Teacher and a Student). Aggregation: a 'whole' references 'parts' that can exist independently (a Department has Employees, but employees survive the department). Composition: the 'whole' owns the 'parts' and they die with it (a House has Rooms — destroy the house, the rooms are gone).
// Aggregation: parts exist independently (passed in)
class Department {
private List<Employee> _staff;
public Department(List<Employee> staff) => _staff = staff;
}
// Composition: whole OWNS the parts (created inside, die with it)
class House {
private readonly List<Room> _rooms = new() { new Room(), new Room() };
} // no House => no RoomsQ26.What are the SOLID principles?Advanced
Five OOP design principles for maintainable code: S — Single Responsibility (a class has one reason to change). O — Open/Closed (open for extension, closed for modification). L — Liskov Substitution (subtypes must be usable wherever the base type is, without breaking behavior). I — Interface Segregation (many small interfaces over one fat one). D — Dependency Inversion (depend on abstractions, not concretions — the basis of DI).
// D - Dependency Inversion: depend on the abstraction, not SqlRepo
class OrderService {
private readonly IRepository _repo;
public OrderService(IRepository repo) => _repo = repo; // injected
}
// O - Open/Closed: add new shapes without editing existing code
abstract class Shape { public abstract double Area(); }
class Circle : Shape { public override double Area() => 3.14; }ASP.NET Core & MVC (11)
Q27.What is ASP.NET Core and how does it differ from classic ASP.NET?Beginner
ASP.NET Core is the cross-platform, open-source, high-performance framework for building web apps and APIs on modern .NET. Versus classic ASP.NET (Windows/IIS-only, System.Web): Core runs anywhere (Linux, containers), has a unified MVC + Web API + Razor Pages model, built-in dependency injection, a flexible middleware pipeline, configuration providers, and is far faster. It can self-host via Kestrel rather than requiring IIS.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // built-in DI
builder.Services.AddScoped<IOrderService, OrderService>();
var app = builder.Build();
app.UseAuthentication(); // middleware pipeline
app.UseAuthorization();
app.MapControllers();
app.Run(); // self-hosted (Kestrel)Q28.Explain the MVC pattern and the ASP.NET MVC request life cycle.Intermediate
MVC separates concerns: Model (data + business logic), View (UI), Controller (handles input, coordinates model and view). The ASP.NET MVC life cycle: routing maps the URL to a controller/action → controller instantiated → action filters run → action executes → it returns an ActionResult (View, JSON, Redirect) → the view engine renders → response sent. Understanding this pipeline is key to placing filters, model binding and validation correctly.
// GET /products/details/5
public class ProductsController : Controller {
[Authorize] // filter runs first
public IActionResult Details(int id) { // model binding: id=5
var product = _repo.Get(id); // build the model
return View(product); // view engine renders
}
}Q29.What's the difference between convention-based and attribute routing?Intermediate
Convention-based routing defines URL patterns centrally (e.g., {controller}/{action}/{id?}) — concise for uniform apps but less explicit. Attribute routing places routes directly on controllers/actions with [Route("...")], [HttpGet("...")] — explicit, supports complex/RESTful URLs, versioning and parameter constraints per endpoint. Web APIs typically use attribute routing; you can mix both.
// Convention-based (central): one rule handles many actions
app.MapControllerRoute("default",
"{controller=Home}/{action=Index}/{id?}");
// Attribute routing (per endpoint): explicit + constrained + versioned
[ApiController]
public class ProductsController : ControllerBase {
[HttpGet("api/v2/products/{id:int}")] // :int constraint
public IActionResult Get(int id) => Ok();
}Q30.What's the difference between ViewData, ViewBag and TempData in ASP.NET MVC?Intermediate
All pass data from controller to view, but: ViewData is a dictionary (ViewData["x"]) — requires casting, available for the current request. ViewBag is a dynamic wrapper over ViewData (ViewBag.x) — no casting, also current request only. TempData persists across ONE subsequent request (backed by session) — ideal for passing data through a redirect (e.g., a success message after POST-redirect-GET).
// current request only
ViewData["Title"] = "Home"; // dictionary (needs casting)
ViewBag.Title = "Home"; // dynamic (no casting)
// survives ONE redirect (POST-Redirect-GET)
[HttpPost]
public IActionResult Save() {
TempData["Msg"] = "Saved!"; // read on the next page
return RedirectToAction("List");
}Q31.What are filters in ASP.NET MVC / Core?Intermediate
Filters inject logic into the request pipeline around action execution. Types (in order): Authorization filters (auth checks), Resource filters (caching, short-circuiting), Action filters (run before/after an action — logging, model tweaks), Exception filters (handle errors), Result filters (around result execution). They centralize cross-cutting concerns instead of repeating code in every action.
public class LogActionAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext c)
=> Console.WriteLine($"-> {c.ActionDescriptor.DisplayName}");
}
[Authorize] // authorization filter
[LogAction] // custom action filter
public IActionResult Delete(int id) => Ok();Q32.What is middleware in ASP.NET Core?Advanced
Middleware are components assembled into a pipeline that each handle an HTTP request and decide whether to pass it to the next component (via next()) or short-circuit. Order matters — they run top-down on the request and bottom-up on the response. Built-in middleware: routing, authentication, authorization, CORS, static files, exception handling. It's the Core replacement for HttpModules/HttpHandlers.
// Order matters: exception handling wraps everything below it
app.UseExceptionHandler("/error");
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// Custom inline middleware
app.Use(async (ctx, next) => {
Console.WriteLine(ctx.Request.Path);
await next(); // pass to the next component
});Q33.What's the difference between HttpModule and HttpHandler (classic ASP.NET)?Advanced
Both intercept requests in classic ASP.NET. An HttpHandler is the endpoint that PROCESSES a request and produces the response for a specific resource/extension (e.g., .ashx, an image generator) — one handler ultimately serves the request. HttpModules run for EVERY request and hook pipeline events (BeginRequest, AuthenticateRequest, EndRequest) for cross-cutting concerns like logging, auth, compression. In ASP.NET Core both are replaced by middleware.
// HttpHandler: IS the response for a specific resource (/chart.ashx)
public class ChartHandler : IHttpHandler {
public void ProcessRequest(HttpContext ctx)
=> ctx.Response.Write("<chart/>");
public bool IsReusable => true;
}
// HttpModule: runs for EVERY request, hooks pipeline events
public class LogModule : IHttpModule {
public void Init(HttpApplication app)
=> app.BeginRequest += (s, e) => { /* log */ };
public void Dispose() {}
}Q34.What's the difference between a web farm and a web garden?Intermediate
A web farm is MULTIPLE servers (machines) hosting the same app behind a load balancer — for scale-out and high availability. A web garden is MULTIPLE worker processes (w3wp.exe) on a SINGLE server (IIS application pool set to >1 process) — for using multiple cores/process isolation on one box. Both break in-process session/state, so you need an external session store (SQL/Redis) in either.
// Web FARM: Load Balancer -> [Server1][Server2][Server3] (many machines)
// Web GARDEN: Server1 app pool -> [w3wp #1][w3wp #2][w3wp #3] (one machine)
// Both break in-process session. Fix: distributed store.
builder.Services.AddStackExchangeRedisCache(o =>
o.Configuration = "redis:6379"); // shared across all processes/serversQ35.How does session state management work, and what are its modes?Intermediate
Session stores per-user data across requests, keyed by a session ID (usually a cookie). Classic ASP.NET modes: InProc (in worker-process memory — fast but lost on recycle and not shared across a farm), StateServer (a separate Windows service), SQLServer (a database — durable, farm-safe), and Custom. ASP.NET Core uses IDistributedCache (in-memory, SQL, or Redis). For web farms/gardens use a distributed store, never InProc.
// ASP.NET Core: distributed session survives recycles + works in a farm
builder.Services.AddStackExchangeRedisCache(o =>
o.Configuration = "redis:6379");
builder.Services.AddSession();
app.UseSession();
// usage
context.Session.SetString("Cart", json);
string? cart = context.Session.GetString("Cart");Q36.What's the difference between authentication and authorization?Beginner
Authentication verifies WHO you are (identity) — login with credentials, tokens, OAuth. Authorization determines WHAT you're allowed to do (permissions/roles) — can this authenticated user access this resource? Authentication always comes first; authorization builds on it. In ASP.NET Core: UseAuthentication() then UseAuthorization(), with [Authorize(Roles="Admin")] enforcing access.
app.UseAuthentication(); // 1. WHO are you? (verify identity)
app.UseAuthorization(); // 2. WHAT can you do? (check permissions)
[Authorize] // must be logged in
[Authorize(Roles = "Admin")] // must also be an Admin
public IActionResult DeleteUser(int id) => Ok(); // else 403 ForbiddenQ37.What is a worker process (w3wp.exe) and an application pool in IIS?Advanced
In IIS, an application pool is an isolation boundary; each pool is served by one or more worker processes (w3wp.exe) that actually run your app code. Isolation means a crash or memory leak in one app pool doesn't affect apps in other pools. Pools can recycle (restart) on schedules, memory limits or idle timeouts — which is why InProc session is fragile. A web garden = one pool with multiple worker processes.
// IIS structure:
// AppPool "SiteA" -> w3wp.exe (process) -> runs SiteA's code
// AppPool "SiteB" -> w3wp.exe (process) -> runs SiteB's code
//
// SiteA's w3wp crashes => SiteB keeps running (isolation).
// A scheduled recycle restarts w3wp => any InProc session is WIPED.
// Web garden = AppPool "SiteA" -> { w3wp #1, w3wp #2 }Web API & REST (5)
Q38.What is a RESTful API and what are its core principles?Beginner
REST (Representational State Transfer) is an architectural style for web APIs over HTTP. Principles: resources identified by URIs (/users/5), standard HTTP verbs (GET read, POST create, PUT replace, PATCH partial update, DELETE remove), statelessness (each request carries everything needed — no server session), proper status codes (200, 201, 404, 400, 401), and representations (usually JSON). It's resource-oriented, cacheable and uniform.
GET /api/orders/42 -> 200 OK { "id":42, "total":99 }
POST /api/orders -> 201 Created + Location header
PUT /api/orders/42 -> 200 OK (replace order 42)
PATCH /api/orders/42 -> 200 OK (partial update)
DELETE /api/orders/42 -> 204 No Content
GET /api/orders/999 -> 404 Not FoundQ39.What's the difference between REST and SOAP?Intermediate
SOAP is a strict PROTOCOL: XML-only messages, a rigid envelope, WSDL contracts, built-in standards (WS-Security, transactions), works over multiple transports. REST is an architectural STYLE: typically JSON over HTTP, lightweight, flexible, uses HTTP verbs and status codes, easier to consume from browsers/mobile. SOAP suits enterprise/finance needing formal contracts and security; REST suits most modern web and mobile APIs.
<!-- SOAP: verbose XML envelope, strict contract (WSDL) -->
<soap:Envelope><soap:Body>
<GetUser><Id>5</Id></GetUser>
</soap:Body></soap:Envelope>
// REST: lightweight JSON over HTTP verbs
GET /api/users/5
=> { "id": 5, "name": "Sam" }Q40.What does it mean for a REST API to be idempotent? Which verbs are idempotent?Advanced
An operation is idempotent if making the same request multiple times has the same effect as making it once. GET, PUT and DELETE are idempotent (PUT replaces with the same payload → same final state; DELETE twice → still deleted). POST is NOT idempotent (two POSTs create two resources). PATCH may or may not be. Idempotency matters for safe retries on network failures — clients can retry PUT/DELETE without side effects.
PUT /users/5 {name:"Bo"} // run 1x or 10x -> user 5 name is "Bo"
DELETE /users/5 // run 2x -> still deleted (same result)
GET /users/5 // read -> no side effects
POST /orders // run 2x -> TWO orders created (NOT idempotent)
// Fix: send an Idempotency-Key header so the server dedupes retriesQ41.When should you use PUT vs POST vs PATCH?Intermediate
POST — create a new resource (server assigns the ID); not idempotent. PUT — create-or-replace a resource at a known URI with the FULL representation; idempotent. PATCH — apply a PARTIAL update (only changed fields) to an existing resource. Rule: POST to a collection (/orders) to create; PUT/PATCH to an item (/orders/42) to update — PUT for full replacement, PATCH for partial.
POST /users { name, email, role } // create (new id)
PUT /users/5 { name, email, role } // replace ALL fields
PATCH /users/5 { email: "x@y.com" } // update ONE field
// Wrong: POST /users/5 to update -> breaks REST + caching semanticsQ42.What's the difference between Web API and an MVC controller?Intermediate
Historically ASP.NET MVC controllers returned Views (HTML) while Web API (ApiController) returned data (JSON/XML) for clients. In ASP.NET Core they unified — both derive from Controller/ControllerBase. An API controller uses [ApiController] + ControllerBase (no view support, automatic model-validation responses, attribute routing) and returns data via IActionResult/ActionResult<T>; an MVC controller adds view rendering. Same framework, different intent.
// MVC controller -> returns HTML views
public class HomeController : Controller {
public IActionResult Index() => View(); // renders Razor HTML
}
// API controller -> returns JSON data
[ApiController]
public class OrdersController : ControllerBase {
[HttpGet("{id}")]
public ActionResult<Order> Get(int id) => Ok(_repo.Get(id)); // JSON
}Entity Framework & Data (4)
Q43.What is Entity Framework and what are its approaches (Code First, Database First)?Intermediate
Entity Framework (EF Core) is Microsoft's ORM — it maps C# classes to database tables so you work with objects and LINQ instead of raw SQL. Approaches: Code First (write C# entities + DbContext, EF generates the schema via migrations — most popular for new apps), Database First (scaffold entities from an existing database), and the legacy Model First (design an EDMX visually). Code First with migrations is the modern default.
// Code First: C# classes drive the schema
public class Product { public int Id { get; set; } public string Name = ""; }
public class AppDb : DbContext {
public DbSet<Product> Products => Set<Product>();
}
// $ dotnet ef migrations add Init
// $ dotnet ef database update -> EF creates the table
var cheap = db.Products.Where(p => p.Id > 10).ToList(); // query via LINQQ44.Explain eager, lazy and explicit loading in Entity Framework.Advanced
These control when related data is loaded. Eager loading fetches related entities up front in one query via .Include() — avoids extra round trips. Lazy loading defers loading related entities until you access the navigation property (each access fires a query) — convenient but causes the N+1 problem. Explicit loading loads related data on demand with .Entry().Collection().Load(). Prefer eager (.Include) for known needs; beware lazy loading in loops.
// Lazy loading: 1 + 100 queries (the N+1 problem)
foreach (var o in db.Orders.ToList())
Console.WriteLine(o.Customer.Name); // fires a query each access
// Eager loading: ONE query with a JOIN
foreach (var o in db.Orders.Include(o => o.Customer).ToList())
Console.WriteLine(o.Customer.Name); // already loadedQ45.What is deferred (lazy) execution in LINQ?Intermediate
A LINQ query isn't executed when you define it — only when you ENUMERATE the results (foreach, ToList(), Count(), First()). The query is built as an expression and runs against the live data at iteration time, so changes to the source before enumeration are reflected. Operators like Where/Select/OrderBy are deferred; ToList/ToArray/Count/First force immediate execution. This enables query composition but can re-run a query if you enumerate twice.
var nums = new List<int> { 1, 2, 3 };
var q = nums.Where(x => x > 1); // NOT executed yet
nums.Add(4); // source changes...
foreach (var n in q) Console.Write(n); // 2 3 4 (runs NOW, sees 4)
var list = q.ToList(); // forces immediate executionQ46.What's the difference between ADO.NET connected and disconnected architecture?Advanced
Connected architecture keeps an open connection while reading data via a DataReader (SqlDataReader) — fast, forward-only, low memory, but holds the connection open the whole time. Disconnected architecture uses a DataAdapter to fill a DataSet/DataTable, then closes the connection; you work with the in-memory cache and reconcile changes later — better for scalability and offline edits, but uses more memory. EF Core builds on these concepts.
// CONNECTED: DataReader streams rows, connection stays open
using var reader = cmd.ExecuteReader(); // forward-only, fast
while (reader.Read()) Console.WriteLine(reader["Name"]);
// DISCONNECTED: fill a DataTable, connection closes immediately
var table = new DataTable();
new SqlDataAdapter(cmd).Fill(table); // work offline with `table`Design Patterns (4)
Q47.What is the Singleton design pattern and how do you implement it in C#?Beginner
Singleton ensures a class has exactly one instance with a global access point. In C# the cleanest thread-safe implementation uses a static readonly field (or Lazy<T>) with a private constructor. In modern ASP.NET Core you rarely hand-roll it — you register a service with a singleton lifetime in the DI container (services.AddSingleton<IFoo, Foo>()), which manages the single instance for you.
// Thread-safe Singleton via Lazy<T>
public sealed class Logger {
private static readonly Lazy<Logger> _i = new(() => new Logger());
public static Logger Instance => _i.Value;
private Logger() {} // private ctor blocks `new`
}
// Modern DI way (preferred): container manages the single instance
builder.Services.AddSingleton<ICache, MemoryCache>();Q48.What's the difference between a static class and a Singleton?Intermediate
Both give a single shared point of access, but: a static class can't be instantiated, can't implement interfaces or be injected, can't have instance state, and can't be mocked — it's a stateless utility holder. A Singleton is a real object (one instance) that CAN implement interfaces, be passed around, hold state, and be injected/mocked. Prefer a DI singleton over a static class when you need testability or polymorphism.
// Static class: pure utility, can't implement interfaces or be mocked
static class MathHelper { public static int Square(int n) => n * n; }
// Singleton: a real object — implements an interface, injectable, mockable
public interface IConfigService { string Get(string key); }
public sealed class ConfigService : IConfigService {
public string Get(string key) => _settings[key]; // holds state
}Q49.What is the Factory design pattern?Intermediate
The Factory pattern delegates object creation to a separate method/class so callers depend on an abstraction, not concrete types. A Simple Factory has a method that returns instances based on input; the Factory Method pattern lets subclasses decide which class to instantiate; Abstract Factory creates families of related objects. It centralizes construction logic, decouples creation from use, and eases adding new types.
public interface IShape { void Draw(); }
public static class ShapeFactory {
public static IShape Create(string type) => type switch {
"circle" => new Circle(),
"square" => new Square(),
_ => throw new ArgumentException(type)
};
}
IShape shape = ShapeFactory.Create("circle"); // caller never does `new Circle()`Q50.What are the Repository and Unit of Work patterns, and how do they relate to Dependency Injection?Advanced
The Repository pattern abstracts data access behind an interface (IRepository<T> with Add/Get/Remove), so business logic doesn't depend on EF or SQL directly — improving testability and swap-ability. The Unit of Work pattern groups multiple repository operations into a single transaction and commits them together (one SaveChanges). EF Core's DbContext already IS a Unit of Work and DbSet IS a repository; you layer your own when you want stricter abstraction. Both are wired via Dependency Injection — register interfaces, inject them into services, and substitute mocks in tests (Dependency Inversion in action).
public interface IOrderRepository { void Add(Order o); }
public interface IUnitOfWork { Task<int> SaveChangesAsync(); }
public class CheckoutService {
private readonly IOrderRepository _orders; // injected abstractions
private readonly IUnitOfWork _uow;
public CheckoutService(IOrderRepository o, IUnitOfWork u)
{ _orders = o; _uow = u; }
public async Task Place(Order o) {
_orders.Add(o);
await _uow.SaveChangesAsync(); // commit as one transaction
}
}📚 Want to go deeper?
Pair these questions with real implementation practice from our free courses: