Skip to content

.NET Full Stack Interview Questions for 2 Years Experience

40 questions with answers for candidates with 2 years of experience — 4 easy, 34 medium, 2 hard.

What interviewers expect: 2 years experience

At two years, you should handle features independently. Expect deeper questions on JWT authentication, global error handling, logging, caching, N+1 queries, AsNoTracking, transactions, SQL indexes and stored procedures. Scenario questions on slow APIs and production bugs are common, and you may get a live coding task building a small API with validation.

This set is weighted towards Async Programming and Multithreading (9), Entity Framework Core (6), C# Fundamentals (5), LINQ, Collections and Generics (5).

Authentication and authorization EF Core performance SQL indexing Debugging production issues
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

Demonstrate a race condition on a counter and fix it.

Save to ▾
Show answer
int count = 0; Parallel.For(0, 100_000, _ => count++); Console.WriteLine(count); // often less than 100000 int safe = 0; Parallel.For(0, 100_000, _ => Interlocked.Increment(ref safe)); Console.WriteLine(safe); // always 100000 count++ is a read, add and write, so two threads can read the same value and lose an update. Interlocked performs the increment atomically without a full lock.
02

Fetch products from an ASP.NET Core API in React with loading and error states.

Save to ▾
Show answer
function Products() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const ctrl = new AbortController(); fetch('/api/products', { signal: ctrl.signal }) .then(r => { if (!r.ok) throw new Error(r.status); return r.json(); }) .then(setItems) .catch(e => { if (e.name !== 'AbortError') setError(e.message); }) .finally(() => setLoading(false)); return () => ctrl.abort(); }, []); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return <ul>{items.map(p => <li key={p.id}>{p.name}</li>)}</ul>; }
03

What are boxing and unboxing?

Save to ▾
Show answer
Boxing converts a value type to object or an interface type by wrapping it in a heap object, for example object o = 42. Unboxing extracts the value back with an explicit cast, such as int n = (int)o. Both cost allocations and CPU time, and repeated boxing in loops increases garbage collection pressure. Generic collections like List<int> avoid boxing, which is one reason ArrayList is no longer used.
04

Show how yield return works with an example.

Save to ▾
Show answer
static IEnumerable<int> EvenNumbers(int max) { for (int i = 0; i <= max; i += 2) { yield return i; // pause here, resume on next MoveNext } } foreach (var n in EvenNumbers(10).Take(3)) Console.WriteLine(n); // 0 2 4 The compiler builds a state machine, so values are produced lazily one at a time. Only three values are generated here, which saves memory for large or infinite sequences.
05

You need to add a new method to a widely implemented interface without breaking existing classes. How can you do it?

OOP in C#Medium
Save to ▾
Show answer
Since C# 8, an interface can provide a method body. Classes implementing the interface get that behaviour without writing it, though they can override it. The main use is evolving a published interface: you can add a new member without breaking every existing implementation. The default method is only callable through the interface type, not the class, and overusing it blurs the line with abstract classes.
06

What is the difference between a static class and a singleton?

OOP in C#Medium
Save to ▾
Show answer
A static class cannot be instantiated, cannot implement interfaces and is initialised by the runtime; it suits stateless helpers like Math. A singleton is a normal class with exactly one instance, so it can implement interfaces, be passed as a parameter and be mocked in tests. In ASP.NET Core, you rarely write a singleton by hand; you register the class with AddSingleton and let the DI container manage its lifetime.
07

Write a stored procedure that transfers money between accounts with TRY-CATCH and a transaction.

Save to ▾
Show answer
CREATE OR ALTER PROCEDURE usp_Transfer @From INT, @To INT, @Amt DECIMAL(18,2) AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRY BEGIN TRAN; UPDATE Accounts SET Balance = Balance - @Amt WHERE Id = @From AND Balance >= @Amt; IF @@ROWCOUNT = 0 THROW 50001, 'Insufficient balance', 1; UPDATE Accounts SET Balance = Balance + @Amt WHERE Id = @To; COMMIT; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK; THROW; END CATCH END
08

Protect an Angular route so only logged-in users can open it.

Save to ▾
Show answer
import { CanActivateFn, Router } from '@angular/router'; import { inject } from '@angular/core'; export const authGuard: CanActivateFn = () => { const auth = inject(AuthService); return auth.isLoggedIn() ? true : inject(Router).createUrlTree(['/login']); }; // app.routes.ts export const routes = [ { path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] }, { path: 'login', component: LoginComponent } ]; Guards only improve user experience; the API must still enforce [Authorize] on the server.
09

Add rate limiting to a login endpoint in ASP.NET Core.

Save to ▾
Show answer
builder.Services.AddRateLimiter(o => { o.RejectionStatusCode = StatusCodes.Status429TooManyRequests; o.AddPolicy("login", ctx => RateLimitPartition.GetFixedWindowLimiter( ctx.Connection.RemoteIpAddress?.ToString() ?? "unknown", _ => new FixedWindowRateLimiterOptions { PermitLimit = 5, Window = TimeSpan.FromMinutes(1) })); }); app.UseRateLimiter(); app.MapPost("/api/login", LoginHandler).RequireRateLimiting("login"); Each IP gets 5 attempts per minute, which slows brute-force attacks. Behind a proxy, use the forwarded client IP.
10

You need to remove inactive users from a List<User> while looping through it with foreach, and you get an InvalidOperationException. How do you fix it?

Save to ▾
Show answer
foreach does not allow modifying the collection it is enumerating, so the enumerator throws. The simplest fix is users.RemoveAll(u => !u.IsActive), which is efficient and clear. Alternatives are looping backwards with a for loop and RemoveAt, or building a new filtered list with Where and ToList. For a dictionary, collect keys to remove first, then remove them after the loop.
11

Why should you avoid async void methods?

Save to ▾
Show answer
An async void method cannot be awaited, so the caller does not know when it finishes. Exceptions thrown inside it cannot be caught by the caller; they go to the synchronization context and can crash the process. It is also hard to unit test. Use async Task instead. The only accepted use of async void is for event handlers, such as button click handlers in desktop apps, where the signature is fixed.
12

What is a delegate in C#?

OOP in C#Medium
Save to ▾
Show answer
A delegate is a type-safe reference to a method with a specific signature, similar to a function pointer but checked by the compiler. You can pass methods as parameters, store them and call them later. Delegates can be multicast, holding several methods that run in order. Lambdas, LINQ, events and callbacks are all built on delegates, and the built-in Func and Action types cover most needs.
13

Users trigger a large Excel report that takes 3 minutes, and the HTTP request times out. How would you redesign it?

Save to ▾
Show answer
Make it asynchronous at the workflow level. The API validates the request, saves a job record and returns 202 Accepted with a job ID. A background worker, using a queue such as a Channel, Hangfire or a message broker, generates the file and stores it in blob storage. The UI polls a status endpoint or receives a SignalR notification, then downloads the file. This avoids timeouts and survives retries.
14

Tell me about a time you worked under a tight deadline.

Save to ▾
Show answer
During my internship (Situation) a client demo was moved two days earlier, and the reports page was not ready. My task was to deliver the SQL queries and API for it. I listed must-have reports with my mentor, wrote and tested the three critical queries first, and postponed exports. I stayed in touch with the UI developer every few hours. Result: the demo went well, and we added the remaining features the next week.
15

What is the difference between const, readonly and static readonly?

Save to ▾
Show answer
const is a compile-time constant, implicitly static, and its value is copied into calling assemblies, so changing it requires recompiling them. readonly fields are set at declaration or in the constructor and can differ per instance. static readonly is set once per type at runtime, so it can hold values like DateTime.Now or a new object. Use const only for values that will never change, such as Math.PI.
16

You are designing a type to hold a 2D point used millions of times in a calculation. Struct or class?

Save to ▾
Show answer
A struct is a good fit because the point is small, logically a single value and created in huge numbers. Structs avoid heap allocation and GC pressure when stored in arrays or locals. Make it a readonly struct so it is immutable and avoids defensive copies. Choose a class instead if the type is large, needs inheritance, or is frequently boxed through interfaces, since copying and boxing remove the benefit.
17

Implement global exception handling that returns ProblemDetails.

Save to ▾
Show answer
public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler { public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct) { logger.LogError(ex, "Unhandled error"); var status = ex is KeyNotFoundException ? 404 : 500; ctx.Response.StatusCode = status; await ctx.Response.WriteAsJsonAsync(new ProblemDetails { Status = status, Title = status == 404 ? "Not found" : "Server error" }, ct); return true; } } // Program.cs builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddProblemDetails(); app.UseExceptionHandler();
18

Where should you store secrets like connection strings and API keys in an ASP.NET Core app?

Save to ▾
Show answer
Never commit them in appsettings.json. During development use the Secret Manager (dotnet user-secrets), which stores them outside the project folder. In production use environment variables or, better, a vault such as Azure Key Vault or AWS Secrets Manager, loaded through a configuration provider and accessed with managed identity so no credential sits in code. Rotate secrets regularly and restrict who can read them.
19

Explain the virtual, override and new keywords.

OOP in C#Medium
Save to ▾
Show answer
virtual marks a base method as replaceable. override in a derived class replaces it, so calls through a base reference run the derived version. new hides the base method instead: a call through a base reference still runs the base version, and only a derived-type reference sees the new one. Method hiding is usually a sign of a design problem, and the compiler warns when you hide without writing new.
20

Show how Task.WhenAll and Task.WhenAny are used.

Save to ▾
Show answer
// run three independent calls in parallel var userTask = userApi.GetAsync(id); var ordersTask = orderApi.GetAsync(id); var walletTask = walletApi.GetAsync(id); await Task.WhenAll(userTask, ordersTask, walletTask); var dashboard = new Dashboard(userTask.Result, ordersTask.Result, walletTask.Result); // timeout using WhenAny var work = SlowCallAsync(); if (await Task.WhenAny(work, Task.Delay(2000)) != work) throw new TimeoutException(); WhenAll completes when all finish; WhenAny returns the first task to finish.
21

How do you use CancellationToken in an ASP.NET Core API?

Save to ▾
Show answer
[HttpGet("report")] public async Task<IActionResult> Report(CancellationToken ct) { var rows = await _db.Sales .Where(s => s.Year == 2026) .ToListAsync(ct); await _http.PostAsJsonAsync("/audit", rows.Count, ct); return Ok(rows); } ASP.NET Core binds the token to HttpContext.RequestAborted, so if the user closes the browser the token is cancelled and EF Core and HttpClient stop work, saving database and CPU time.
22

A dashboard API calls three independent services one after another, each taking about 1 second. How can you make it faster?

Save to ▾
Show answer
Because the calls do not depend on each other, start all three tasks first without awaiting, then await Task.WhenAll. Total time becomes roughly the slowest call, about 1 second instead of 3. Add a timeout and handle failure of one service gracefully, for example showing partial data. If the same data is requested often, also add caching so most requests skip the calls entirely.
23

A developer runs two EF Core queries in parallel with Task.WhenAll on the same DbContext and gets an InvalidOperationException. Why?

Save to ▾
Show answer
DbContext is not thread-safe and supports only one active operation at a time, so starting a second query before the first finishes throws 'A second operation was started on this context instance'. Either await the queries one after another, or create a separate context for each parallel query using IDbContextFactory. Usually sequential queries are fine; parallelism only helps when the queries are slow and independent.
24

A read-only product listing endpoint in EF Core uses a lot of memory and CPU. What simple change helps, and why?

Save to ▾
Show answer
By default, EF Core tracks every entity it loads so it can detect changes on SaveChanges, which costs memory and CPU. AsNoTracking tells EF Core not to track the results, making read-only queries faster and lighter. Use it for lists, reports and GET endpoints where you will not update the data. AsNoTrackingWithIdentityResolution avoids duplicate instances of the same entity in results while still skipping tracking.
25

How do you run raw SQL safely in EF Core?

Save to ▾
Show answer
// entity query with parameters (interpolation becomes SqlParameter) var orders = await db.Orders .FromSql($"SELECT * FROM Orders WHERE City = {city}") .Where(o => o.Total > 1000) .ToListAsync(); // non-entity results var totals = await db.Database .SqlQuery<decimal>($"SELECT SUM(Total) AS Value FROM Orders") .ToListAsync(); // stored procedure await db.Database.ExecuteSqlAsync($"EXEC usp_CloseMonth {month}"); Never build SQL by string concatenation; FromSqlRaw with concatenated input invites SQL injection.
26

What is the difference between a Task and a Thread?

Save to ▾
Show answer
A Thread is an OS-level execution unit with its own stack, costly to create. A Task is a higher-level promise of work that may or may not use a thread; I/O tasks often use no thread while waiting, and CPU tasks run on pooled threads. Tasks support return values, continuations, cancellation, exceptions and composition with WhenAll. In modern .NET you almost always use Tasks rather than creating threads manually.
27

How are events different from plain delegates?

OOP in C#Medium
Save to ▾
Show answer
An event is a delegate wrapped with restricted access. Outside the class, subscribers can only add or remove handlers with += and -=; they cannot invoke the event or overwrite the whole list with =. This protects the publisher. The standard pattern uses EventHandler<TEventArgs> with sender and args. Remember to unsubscribe long-lived subscriptions, otherwise the publisher keeps subscribers alive and causes memory leaks.
28

What is the difference between reactive forms and template-driven forms in Angular?

Save to ▾
Show answer
Template-driven forms are defined mainly in HTML with ngModel and directives; they are quick for simple forms but harder to test. Reactive forms are defined in TypeScript with FormGroup, FormControl and Validators, giving explicit, strongly typed models, easy dynamic fields, custom and async validators, and straightforward unit testing. For enterprise apps such as loan application or KYC forms, reactive forms are the usual choice.
29

What does the lock keyword do?

Save to ▾
Show answer
lock ensures that only one thread at a time executes a block of code for a given lock object, preventing race conditions on shared data. It compiles to Monitor.Enter and Monitor.Exit inside try-finally. Lock on a private readonly object, never on this or a string. Since .NET 9 there is a dedicated System.Threading.Lock type that is faster. You cannot await inside a lock; use SemaphoreSlim for async code.
30

Your code builds a LINQ query over a database, then calls Count() and later loops over it, and the logs show two SQL queries. Why, and how do you fix it?

Save to ▾
Show answer
This is multiple enumeration caused by deferred execution. The variable holds a query, not data, so Count runs one SQL statement and the foreach runs another. Besides extra load, the two results may differ if data changes in between. Materialise once with var list = await query.ToListAsync(), then use list.Count and loop over the list. Code analysers flag possible multiple enumeration of IEnumerable as a warning.
31

Sort a list of students by marks descending, then by name ascending.

Save to ▾
Show answer
var ranked = students .OrderByDescending(s => s.Marks) .ThenBy(s => s.Name) .ToList(); // in-place alternative with a comparison students.Sort((a, b) => b.Marks != a.Marks ? b.Marks.CompareTo(a.Marks) : string.Compare(a.Name, b.Name, StringComparison.Ordinal)); OrderBy is stable and returns a new sequence; List.Sort sorts in place and is not stable.
32

Write a switch expression using pattern matching to calculate shipping cost.

Save to ▾
Show answer
decimal Shipping(Order o) => o switch { { Total: >= 1000 } => 0m, { Country: "IN", Weight: < 2 } => 49m, { Country: "IN" } => 99m, { Weight: > 20 } => throw new ArgumentException("Too heavy"), _ => 499m }; Property and relational patterns replace long if-else chains, and the compiler warns if cases are not exhaustive.
33

What is the difference between controllers and Minimal APIs?

Save to ▾
Show answer
Controllers group actions into classes with attributes for routing, filters and model binding, which suits large APIs with many conventions. Minimal APIs map endpoints directly with app.MapGet and lambdas, with less ceremony and slightly better performance, ideal for microservices and small APIs. Since .NET 7 they support filters, route groups and OpenAPI, and .NET 10 adds built-in validation, so both are production-ready choices.
34

Configure a one-to-many relationship between Customer and Order using the Fluent API.

Save to ▾
Show answer
public class Customer { public int Id { get; set; } public List<Order> Orders { get; set; } = []; } public class Order { public int Id { get; set; } public int CustomerId { get; set; } public Customer Customer { get; set; } = null!; } protected override void OnModelCreating(ModelBuilder mb) { mb.Entity<Order>() .HasOne(o => o.Customer) .WithMany(c => c.Orders) .HasForeignKey(o => o.CustomerId) .OnDelete(DeleteBehavior.Restrict); mb.Entity<Order>().Property(o => o.Total).HasPrecision(18, 2); } Restrict prevents deleting a customer who still has orders.
35

How does change tracking work and what are entity states?

Save to ▾
Show answer
The change tracker keeps a snapshot of each tracked entity and compares it on SaveChanges. Each entity has a state: Added (insert), Modified (update), Deleted (delete), Unchanged (no action) and Detached (not tracked). Calling Add, Update or Remove sets states, and changing a property of a tracked entity marks it Modified. SaveChanges then generates SQL only for changed columns, all inside one transaction.
36

Show how to stream data with IAsyncEnumerable.

Save to ▾
Show answer
public async IAsyncEnumerable<Order> GetOrdersAsync( [EnumeratorCancellation] CancellationToken ct = default) { await foreach (var o in _db.Orders.AsNoTracking().AsAsyncEnumerable().WithCancellation(ct)) yield return o; } // consumer await foreach (var order in service.GetOrdersAsync()) Console.WriteLine(order.Id); Items are produced and consumed one at a time asynchronously. ASP.NET Core can stream an IAsyncEnumerable result as JSON without buffering the whole list.
37

How do you use an explicit transaction in EF Core across multiple SaveChanges calls?

Save to ▾
Show answer
await using var tx = await db.Database.BeginTransactionAsync(); try { db.Orders.Add(order); await db.SaveChangesAsync(); var stock = await db.Stocks.SingleAsync(s => s.ProductId == order.ProductId); stock.Quantity -= order.Quantity; await db.SaveChangesAsync(); await tx.CommitAsync(); } catch { await tx.RollbackAsync(); throw; } A single SaveChanges is already transactional; explicit transactions are needed when you combine several saves or raw SQL into one unit.
38

What are record types and when would you use them?

Save to ▾
Show answer
Records are reference types (or record struct for value types) designed for immutable data. The compiler generates value-based equality, a readable ToString, a deconstructor and support for with-expressions that create a modified copy. A positional record is one line: public record Order(int Id, decimal Total). They suit DTOs, API responses, events and value objects, but not EF Core entities that need change tracking by identity.
39

Describe an API you designed end to end and the decisions you made.

Save to ▾
Show answer
In my last project (Situation) we needed a leave-management API for 3,000 employees. My task was the full design. I modelled resources like /employees/{id}/leaves, used DTOs separate from EF entities, JWT with role policies for managers, FluentValidation, ProblemDetails errors and paging on every list. I documented it in OpenAPI and agreed the contract with the Angular team first. Result: front and back ends were built in parallel and launched on time.
40

What is the difference between IEnumerable and IQueryable?

Save to ▾
Show answer
IEnumerable<T> works on in-memory data and executes LINQ with C# delegates, so filters run in the application. IQueryable<T> holds an expression tree that a provider such as EF Core translates into SQL, so filtering, sorting and paging happen in the database. If you return IEnumerable from a repository and then call Where, all rows may be loaded first. Keep queries as IQueryable until the final shape is ready.

Frequently asked questions

What is asked in .NET Full Stack interviews for 2 years of experience?

At two years, you should handle features independently. Expect deeper questions on JWT authentication, global error handling, logging, caching, N+1 queries, AsNoTracking, transactions, SQL indexes and stored procedures. Scenario questions on slow APIs and production bugs are common, and you may get a live coding task building a small API with validation.

How many questions should I practise before a .NET Full Stack interview?

Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.

Which topics matter most for 2 years of experience?

Focus first on Authentication and authorization, EF Core performance, SQL indexing, Debugging production issues.