Skip to content

Medium .NET Full Stack Interview Questions

94 medium questions with answers. Applied questions that check whether you can use the concepts on real work.

Topics in the medium set

OOP in C#: 14 Async Programming and Multithreading: 13 ASP.NET Core Web API: 13 Angular and React Front End: 13 LINQ, Collections and Generics: 11 Entity Framework Core: 11 C# Fundamentals: 10 SQL Server and T-SQL: 9
Solved — mediumStart 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

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.
02

Explain the ref, out and in parameter modifiers.

Save to ▾
Show answer
ref passes a variable by reference; it must be initialised before the call and the method can read and change it. out also passes by reference but need not be initialised, and the method must assign it before returning, as in int.TryParse. in passes by reference as read-only, which avoids copying large structs while preventing changes. Overusing ref and out makes code harder to read, so tuples are often cleaner.
03

What are nullable value types and nullable reference types?

Save to ▾
Show answer
Nullable value types like int? wrap a struct so it can hold null, with HasValue and Value properties. Nullable reference types, enabled by <Nullable>enable</Nullable>, are a compile-time feature: string means non-null and string? means may be null, and the compiler warns when you dereference a possible null. They do not change runtime behaviour but catch many NullReferenceException bugs before deployment.
04

Show how to implement IDisposable and use the using statement.

Save to ▾
Show answer
public class FileLogger : IDisposable { private readonly StreamWriter _writer = new("log.txt"); public void Log(string msg) => _writer.WriteLine(msg); public void Dispose() => _writer.Dispose(); } // using declaration disposes at end of scope using var logger = new FileLogger(); logger.Log("Started"); Dispose releases unmanaged resources such as file handles and connections deterministically instead of waiting for the GC.
05

What is the difference between == and Equals() in C#?

Save to ▾
Show answer
For reference types, == by default checks reference equality, meaning both variables point to the same object, while Equals can be overridden to compare content. String overrides both, so == compares text. For value types, Equals compares field values. Records generate value-based equality automatically. When you override Equals, also override GetHashCode so objects behave correctly in dictionaries and hash sets.
06

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.
07

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.
08

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.
09

What is the difference between throw and throw ex inside a catch block?

Save to ▾
Show answer
throw rethrows the current exception and preserves the original stack trace, so logs show where the error really started. throw ex resets the stack trace to the catch block, hiding the real source. Always use throw when rethrowing. If you need more context, wrap it: throw new OrderException("Payment failed", ex), which keeps the original as InnerException. Also avoid catching Exception unless you log and rethrow.
10

Show a class using a primary constructor with dependency injection.

Save to ▾
Show answer
public class OrderService(IOrderRepository repo, ILogger<OrderService> logger) { public async Task<Order?> GetAsync(int id) { logger.LogInformation("Fetching order {Id}", id); return await repo.FindAsync(id); } } Primary constructors on classes arrived in C# 12. Parameters are captured and usable in all members, removing boilerplate fields. They are not readonly, so assign to a readonly field if immutability matters.
11

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.
12

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.
13

Explain the access modifiers in C#, including protected internal and private protected.

OOP in C#Medium
Save to ▾
Show answer
public is accessible everywhere, private only inside the type, protected inside the type and derived types, and internal anywhere in the same assembly. protected internal means accessible from the same assembly or from derived types in any assembly. private protected is stricter: only derived types inside the same assembly. Class members default to private and top-level types default to internal. Use the narrowest modifier that works.
14

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.
15

Explain the SOLID principles briefly.

OOP in C#Medium
Save to ▾
Show answer
Single Responsibility: a class has one reason to change. Open/Closed: extend behaviour without editing existing code. Liskov Substitution: derived types must work wherever the base type is expected. Interface Segregation: prefer small, focused interfaces over one fat interface. Dependency Inversion: depend on abstractions, not concrete classes. In ASP.NET Core, injecting IEmailSender into a controller instead of creating SmtpClient directly applies dependency inversion.
16

Show an example of the Dependency Inversion Principle in C#.

OOP in C#Medium
Save to ▾
Show answer
public interface INotifier { Task SendAsync(string to, string msg); } public class SmsNotifier : INotifier { public Task SendAsync(string to, string msg) => Task.CompletedTask; // call SMS API } public class OrderService(INotifier notifier) { public Task ConfirmAsync(Order o) => notifier.SendAsync(o.Phone, "Order placed"); } // Program.cs builder.Services.AddScoped<INotifier, SmsNotifier>(); OrderService depends on the abstraction, so you can swap to email or a mock without changing it.
17

A teammate created a deep inheritance chain Employee > Manager > SeniorManager > RegionalHead to add features. What would you suggest?

OOP in C#Medium
Save to ▾
Show answer
Deep hierarchies are fragile: a change in a base class ripples into every child, and behaviours that do not fit the tree get duplicated. I would suggest composition over inheritance. Keep a single Employee entity and give it composed parts such as a Role, an ApprovalPolicy and a list of Permissions, injected as interfaces. New behaviour then means a new policy class, not a new subclass, which is easier to test.
18

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.
19

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.
20

Show how Func, Action and Predicate are used.

OOP in C#Medium
Save to ▾
Show answer
Func<decimal, decimal> addGst = price => price * 1.18m; Action<string> log = msg => Console.WriteLine($"[LOG] {msg}"); Predicate<int> isAdult = age => age >= 18; decimal total = addGst(1000m); // 1180 log($"Total {total}"); bool ok = isAdult(21); // true Func returns a value (last type argument is the return type), Action returns void, and Predicate<T> returns bool. They save you from declaring custom delegate types.
21

Which design patterns do you commonly see in ASP.NET Core projects?

OOP in C#Medium
Save to ▾
Show answer
Dependency Injection is built in. The Repository and Unit of Work patterns wrap data access, although DbContext already implements them. Factory creates objects by type, for example IHttpClientFactory. Strategy swaps algorithms, such as different tax or discount rules, behind one interface. Options pattern binds configuration to classes. Middleware is a Chain of Responsibility, and Mediator libraries implement CQRS by sending commands to handlers.
22

What is the difference between a shallow copy and a deep copy?

OOP in C#Medium
Save to ▾
Show answer
A shallow copy duplicates the object but its reference fields still point to the same child objects; MemberwiseClone and record with-expressions do this. A deep copy duplicates the whole object graph, so changes to the copy never affect the original. Deep copies are usually written by hand or by serialising and deserialising. ICloneable is avoided because it does not say which kind of copy it returns.
23

Tell me about a time you refactored messy code that others depended on.

OOP in C#Medium
Save to ▾
Show answer
In my last project (Situation) an invoice module had one 900-line class with duplicated GST logic. My task was to add a new tax rule without breaking billing. I first wrote unit tests around current behaviour, then extracted tax rules into strategy classes and moved data access into a repository, merging in small pull requests. Result: the new rule took one day, and bugs in that module dropped noticeably the next quarter.
24

How do you explain good object-oriented design to a junior developer on your team?

OOP in C#Medium
Save to ▾
Show answer
I avoid theory first. I take a real piece of our code, like a controller that talks to the database directly, and pair with them to move logic into a service behind an interface, then write a unit test with a mock. Seeing how much easier testing becomes explains SOLID better than slides. I then review their next pull request with specific comments and point them to one or two good examples in our codebase.
25

What is deferred execution in LINQ?

Save to ▾
Show answer
Most LINQ operators such as Where and Select do not run when you write them; they build a query that executes only when you enumerate it with foreach, ToList, Count or First. This means the query sees the latest data at enumeration time and can be composed step by step. The catch is that enumerating the same query twice runs it twice, which can mean two database calls. Call ToList to materialise once.
26

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.
27

What is the difference between Select and SelectMany?

Save to ▾
Show answer
Select projects each element to one result, so a list of customers mapped to their orders gives a list of lists. SelectMany projects each element to a sequence and flattens all sequences into one, so customers.SelectMany(c => c.Orders) gives a single list of all orders. In EF Core, SelectMany over a navigation collection translates to a SQL join, which is handy for reports.
28

Using LINQ, group employees by department and get the count and average salary.

Save to ▾
Show answer
var report = employees .GroupBy(e => e.Department) .Select(g => new { Department = g.Key, Count = g.Count(), AvgSalary = g.Average(e => e.Salary) }) .OrderByDescending(x => x.AvgSalary) .ToList(); GroupBy returns groups with a Key, and aggregate methods run per group. With EF Core this translates to SQL GROUP BY.
29

Write a LINQ query that joins orders with customers and returns customer name and order total.

Save to ▾
Show answer
var result = orders .Join(customers, o => o.CustomerId, c => c.Id, (o, c) => new { c.Name, o.Total }) .ToList(); // query syntax var result2 = (from o in orders join c in customers on o.CustomerId equals c.Id select new { c.Name, o.Total }).ToList(); Join is an inner join. For a left join use GroupJoin with DefaultIfEmpty, or the LeftJoin operator added in .NET 10.
30

What are generic constraints? Give examples.

Save to ▾
Show answer
Constraints restrict which types can be used for a type parameter so the generic code can rely on certain members. Examples: where T : class (reference type), where T : struct, where T : new() (has a parameterless constructor), where T : BaseEntity (inherits a class), where T : IComparable<T>, and where T : notnull. A repository might use where T : class, IEntity so it can read the Id property safely.
31

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.
32

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.
33

A job matches 2 lakh uploaded records against 2 lakh existing IDs using list.Where(x => existingIds.Contains(x.Id)) and takes minutes. What is wrong?

Save to ▾
Show answer
existingIds is a List, so Contains is O(n) and the whole operation becomes O(n x m), around 40 billion comparisons. Convert the IDs to a HashSet<int> first, making each lookup O(1) and the job run in milliseconds. If you also need associated data, build a Dictionary keyed by ID. Always check data structure complexity before parallelising or adding hardware.
34

Show the newer LINQ methods Chunk, DistinctBy and MaxBy.

Save to ▾
Show answer
// split 1,000 ids into batches of 100 for bulk API calls foreach (int[] batch in ids.Chunk(100)) await SendBatchAsync(batch); // unique customers by email var unique = customers.DistinctBy(c => c.Email).ToList(); // highest-paid employee object, not just the salary Employee? top = employees.MaxBy(e => e.Salary); These were added in .NET 6 and remove the need for custom helpers or third-party libraries like MoreLINQ.
35

Tell me about a time you improved the performance of slow code.

Save to ▾
Show answer
In my previous role (Situation) a daily reconciliation job took 40 minutes and sometimes missed its window. My task was to bring it under 10 minutes. I profiled it and found nested loops over two large lists and one database call per row. I replaced the inner loop with a Dictionary lookup and batched the database reads. Result: the job ran in about 4 minutes, and I documented the approach for the team.
36

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.
37

Why does using async in ASP.NET Core controllers improve scalability?

Save to ▾
Show answer
Each request needs a thread from the thread pool. If a controller blocks while waiting for SQL Server or an external API, that thread sits idle and cannot serve other users, so under load the pool runs out and requests queue. With async I/O, the thread is released during the wait and reused, so the same server handles many more concurrent requests. It does not make a single request faster.
38

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.
39

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.
40

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.
41

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.
42

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.
43

You must resize 10,000 images and also call an external API for 10,000 records. Which approach do you use for each task?

Save to ▾
Show answer
Parallel.ForEach is for CPU-bound work, such as resizing thousands of images, where splitting work across cores speeds things up. async/await is for I/O-bound work, where you mostly wait on networks or disks. Using Parallel.ForEach for HTTP calls wastes threads while they wait. For many concurrent I/O operations with a limit, use Parallel.ForEachAsync with MaxDegreeOfParallelism, available since .NET 6.
44

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.
45

Write a BackgroundService that cleans expired OTPs every 5 minutes.

Save to ▾
Show answer
public class OtpCleanupService(IServiceScopeFactory scopes) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken ct) { using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5)); while (await timer.WaitForNextTickAsync(ct)) { using var scope = scopes.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<AppDbContext>(); await db.Otps.Where(o => o.ExpiresAt < DateTime.UtcNow).ExecuteDeleteAsync(ct); } } } // builder.Services.AddHostedService<OtpCleanupService>(); A scope is created because DbContext is scoped while hosted services are singletons.
46

A developer wraps every database call in Task.Run inside ASP.NET Core controllers to make them async. Is this a good idea?

Save to ▾
Show answer
No. Task.Run moves work to another thread pool thread, but the request still waits for it, so you use two threads instead of one and gain nothing. The database call itself is still blocking. The correct approach is to use true async APIs like ToListAsync and SaveChangesAsync, which release the thread during I/O. Task.Run makes sense mainly in desktop apps to keep the UI thread free.
47

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.
48

Tell me about a production bug you had to debug under time pressure.

Save to ▾
Show answer
Last year (Situation) our order API started timing out during a sale. My task was to restore service fast. I checked dashboards, saw thread count climbing with low CPU, and took a dump that showed threads blocked on a .Result call added in a recent release. Action: I hotfixed it to proper async and rolled out within an hour. Result: latency returned to normal, and I added an analyser rule to block sync-over-async.
49

Write custom middleware that logs how long each request takes.

Save to ▾
Show answer
public class TimingMiddleware(RequestDelegate next, ILogger<TimingMiddleware> logger) { public async Task InvokeAsync(HttpContext context) { var sw = Stopwatch.StartNew(); await next(context); sw.Stop(); logger.LogInformation("{Method} {Path} -> {Status} in {Ms} ms", context.Request.Method, context.Request.Path, context.Response.StatusCode, sw.ElapsedMilliseconds); } } // Program.cs app.UseMiddleware<TimingMiddleware>(); Code before next runs on the request, code after it runs on the response.
50

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.
51

How do model binding and validation work in ASP.NET Core Web API?

Save to ▾
Show answer
Model binding maps request data into action parameters from the route, query string, headers or body, using [FromRoute], [FromQuery] and [FromBody]. Validation uses data annotations like [Required], [StringLength] and [Range] on DTOs. With [ApiController], invalid models automatically return 400 Bad Request with ProblemDetails listing the errors, so you do not need to check ModelState manually. FluentValidation is popular for complex rules.
52

What types of filters exist in ASP.NET Core MVC?

Save to ▾
Show answer
Authorization filters run first and can block a request. Resource filters run before model binding, useful for caching. Action filters run before and after an action, for logging or validation. Exception filters handle errors thrown by actions. Result filters run around the result execution, for example to add headers. Filters apply globally, per controller or per action. Unlike middleware, they can access MVC details like action arguments.
53

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();
54

Configure JWT bearer authentication in an ASP.NET Core API.

Save to ▾
Show answer
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorization(); app.UseAuthentication(); app.UseAuthorization(); Then protect endpoints with [Authorize] or [Authorize(Roles = "Admin")].
55

Show how to bind configuration from appsettings.json using the Options pattern.

Save to ▾
Show answer
// appsettings.json // "Smtp": { "Host": "smtp.example.com", "Port": 587 } public class SmtpOptions { public string Host { get; set; } = ""; public int Port { get; set; } } builder.Services.Configure<SmtpOptions>(builder.Configuration.GetSection("Smtp")); public class EmailService(IOptions<SmtpOptions> options) { private readonly SmtpOptions _smtp = options.Value; } appsettings.{Environment}.json and environment variables override base values. Use IOptionsMonitor to pick up changes without restart.
56

How does logging work in ASP.NET Core?

Save to ▾
Show answer
ASP.NET Core has a built-in ILogger<T> abstraction injected into any class. You log with levels Trace, Debug, Information, Warning, Error and Critical, and configure minimum levels per category in appsettings.json. Use structured logging with message templates, like logger.LogInformation("Order {OrderId} placed", id), so values become searchable fields. Providers such as Serilog or OpenTelemetry send logs to files, Seq, Elasticsearch or Application Insights.
57

An API creates a new HttpClient inside a using block for every outgoing call, and under load you see socket exhaustion errors. How do you fix it?

Save to ▾
Show answer
Disposing HttpClient does not immediately free the underlying socket; connections stay in TIME_WAIT, so thousands of short-lived clients exhaust ports. A single static client fixes that but misses DNS changes. The recommended fix is IHttpClientFactory: register builder.Services.AddHttpClient<PaymentClient>() and inject the typed client. The factory pools and rotates handlers, and you can add resilience with Microsoft.Extensions.Http.Resilience for retries and timeouts.
58

You must change an API response format, but an older mobile app version still calls it. How do you avoid breaking those users?

Save to ▾
Show answer
Versioning lets you change contracts without breaking existing clients such as mobile apps that users have not updated. Common approaches are URL segments (/api/v1/orders), query strings (?api-version=2.0) and headers. The Asp.Versioning library supports all of them and integrates with OpenAPI docs. URL versioning is the most visible and cache-friendly. Keep old versions running for a published period, then deprecate them with headers.
59

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.
60

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.
61

Tell me about a disagreement with the front-end team over an API contract and how you resolved it.

Save to ▾
Show answer
On a dashboard feature (Situation) the React team wanted one endpoint returning everything, while I worried about payload size. My task was to agree a design quickly. I set up a short call, we listed exact screen needs, and I proposed one aggregated endpoint with paging and optional fields. We wrote the OpenAPI spec together. Result: payload dropped by half, the UI got one call, and the spec became our template for later features.
62

Explain eager, lazy and explicit loading in EF Core.

Save to ▾
Show answer
Eager loading fetches related data in the same query using Include and ThenInclude, for example orders with their items. Lazy loading loads a navigation property automatically the first time you access it, requiring proxies and virtual properties; it is convenient but easily causes N+1 queries. Explicit loading loads a navigation on demand with Entry(order).Collection(o => o.Items).LoadAsync(). Projection with Select is often better than all three.
63

A page listing 100 orders with customer names runs 101 SQL queries. What is the problem and how do you fix it?

Save to ▾
Show answer
This is the N+1 problem: one query loads the orders, then lazy loading fires a separate query for each order's Customer. Fix it by loading everything in one query, either with Include(o => o.Customer) or better with a projection: Select(o => new OrderListDto(o.Id, o.Total, o.Customer.Name)), which fetches only needed columns via a join. Turn on EF Core logging to spot such patterns early.
64

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.
65

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.
66

How does EF Core handle many-to-many relationships?

Save to ▾
Show answer
Since EF Core 5, you can define collection navigations on both sides, such as Student.Courses and Course.Students, and EF Core creates a hidden join table automatically. If the join needs extra data, like EnrolledOn or Grade, create an explicit join entity Enrollment with two foreign keys and configure it with UsingEntity or two one-to-many relationships. Querying works naturally with Include or projections through the navigations.
67

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.
68

Show bulk update and delete with ExecuteUpdate and ExecuteDelete.

Save to ▾
Show answer
// mark all unpaid invoices older than 30 days as overdue int updated = await db.Invoices .Where(i => !i.IsPaid && i.DueDate < DateTime.UtcNow.AddDays(-30)) .ExecuteUpdateAsync(s => s .SetProperty(i => i.Status, "Overdue") .SetProperty(i => i.UpdatedAt, DateTime.UtcNow)); // delete old logs int deleted = await db.AuditLogs .Where(l => l.CreatedAt < cutoff) .ExecuteDeleteAsync(); These run a single SQL UPDATE or DELETE without loading entities, available since EF Core 7. They bypass the change tracker.
69

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.
70

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.
71

Explain a technical trade-off you had to justify to a manager or client.

Save to ▾
Show answer
On a reporting module (Situation) the manager wanted everything in EF Core for consistency, but one report was too slow. My task was to recommend a fix without derailing the plan. I measured the EF query against a Dapper version with a hand-tuned SQL query, showed the 8x difference and explained that we would use Dapper only for that read path. Result: the manager approved, the report loaded in under a second, and the rest stayed in EF Core.
72

Implement paging for a product list in EF Core.

Save to ▾
Show answer
public async Task<PagedResult<ProductDto>> GetPageAsync(int page, int size) { var query = db.Products.AsNoTracking().OrderBy(p => p.Id); int total = await query.CountAsync(); var items = await query .Skip((page - 1) * size) .Take(size) .Select(p => new ProductDto(p.Id, p.Name, p.Price)) .ToListAsync(); return new PagedResult<ProductDto>(items, total, page, size); } Always order before Skip and Take. For very deep pages, keyset paging with Where(p => p.Id > lastId) is faster than large offsets.
73

What is the difference between a clustered and a non-clustered index?

Save to ▾
Show answer
A clustered index defines the physical order of the table's data, so the leaf level is the data itself, and a table can have only one. A non-clustered index is a separate structure with key values and a pointer back to the row, and a table can have many. Range queries on the clustered key are fast. Non-clustered indexes speed up searches on other columns but add cost to inserts and updates.
74

What is the difference between a stored procedure and a user-defined function?

Save to ▾
Show answer
A stored procedure can perform inserts, updates and deletes, use transactions and TRY-CATCH, return multiple result sets and output parameters, and is called with EXEC. A function must return a value or table, cannot modify database state, and can be used inside SELECT, WHERE and JOIN. Scalar functions used row by row can hurt performance, although SQL Server 2019 and later can inline many of them.
75

Show the difference between ROW_NUMBER, RANK and DENSE_RANK with a query.

Save to ▾
Show answer
SELECT Name, Department, Salary, ROW_NUMBER() OVER (PARTITION BY Department ORDER BY Salary DESC) AS RowNum, RANK() OVER (PARTITION BY Department ORDER BY Salary DESC) AS Rnk, DENSE_RANK() OVER (PARTITION BY Department ORDER BY Salary DESC) AS DenseRnk FROM Employees; -- salaries 90, 90, 80 give -- ROW_NUMBER: 1, 2, 3 -- RANK: 1, 1, 3 -- DENSE_RANK: 1, 1, 2 PARTITION BY restarts numbering for each department.
76

A query that used to run in 1 second now takes 30 seconds. How do you troubleshoot it?

Save to ▾
Show answer
Capture the actual execution plan and look for table or index scans on large tables, key lookups, big differences between estimated and actual rows, and warnings like implicit conversions or spills to tempdb. Check missing or fragmented indexes and outdated statistics, and update them. Use Query Store to compare the old and new plans; if a plan regressed, you can force the good plan while fixing the root cause.
77

What is the difference between temporary tables and table variables?

Save to ▾
Show answer
Temporary tables (#Temp) live in tempdb, support indexes and statistics, and are visible to nested procedures, so they suit larger intermediate results. Table variables (@T) also use tempdb but have limited statistics, which made old optimisers estimate one row; SQL Server 2019 improved this with deferred compilation. Table variables are not affected by transaction rollback. Use table variables for small sets and temp tables for large ones.
78

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
79

What is a view and what is an indexed view?

Save to ▾
Show answer
A view is a saved SELECT query that acts like a virtual table; it simplifies complex joins and can restrict which columns users see, but by default it stores no data. An indexed view has a unique clustered index, so SQL Server physically stores and maintains its results, which speeds up heavy aggregations. The cost is slower writes on base tables, and it requires SCHEMABINDING and deterministic expressions.
80

Tell me about a time you made a mistake that affected data, and what you did.

Save to ▾
Show answer
Early in my job (Situation) I ran an UPDATE on a test copy that I believed was staging, but it was a shared QA database, and 2,000 test orders lost their status. My task was to fix it fast. I told my lead immediately, restored the rows from the nightly backup with the DBA, and verified counts. Result: QA lost only an hour. Since then I always wrap manual updates in a transaction and run a SELECT with the same WHERE first.
81

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.
82

What are services and dependency injection in Angular?

Save to ▾
Show answer
A service is a class, marked @Injectable, that holds logic or data shared across components, such as API calls, authentication or a cart. Angular's DI creates the service and provides it to components that request it in the constructor or with the inject() function. With providedIn: 'root', one shared instance exists for the whole app. This keeps components focused on UI and makes logic easy to test.
83

What are signals in Angular and why were they introduced?

Save to ▾
Show answer
Signals are reactive values: count = signal(0) holds state, computed() derives values and effect() runs side effects when dependencies change. Angular knows exactly which parts of the view read which signal, so it updates only those parts instead of checking the whole component tree. This enables zoneless change detection, which became the default for new apps in Angular 21, and gives simpler state handling than RxJS for local UI state.
84

What is the difference between an RxJS Observable and a Promise?

Save to ▾
Show answer
A Promise represents a single future value, starts immediately and cannot be cancelled. An Observable can emit many values over time, is lazy until subscribed, and can be cancelled by unsubscribing. RxJS operators like map, debounceTime, switchMap and retry make it powerful for streams such as search boxes or WebSocket data. Angular's HttpClient returns Observables; use the async pipe or toSignal to avoid manual unsubscribe.
85

Write an Angular HTTP interceptor that adds a JWT to every API request.

Save to ▾
Show answer
import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const token = inject(AuthService).token(); if (token) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next(req); }; // app.config.ts provideHttpClient(withInterceptors([authInterceptor])); Requests are immutable, so clone() creates a modified copy. The ASP.NET Core JWT middleware then reads the header.
86

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.
87

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.
88

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>; }
89

What are useMemo and useCallback used for in React?

Save to ▾
Show answer
useMemo caches the result of an expensive calculation and recalculates only when its dependencies change, such as filtering 10,000 rows. useCallback caches a function reference so that a child wrapped in React.memo does not re-render just because a new function was created. Both are optimisations and add complexity, so use them where profiling shows a problem. The React Compiler, stable since late 2025, can apply much of this memoisation automatically.
90

How do you manage application state in Angular and React apps?

Save to ▾
Show answer
Start simple: local component state, then share through a parent or a service. In Angular, a service holding signals covers most needs, and NgRx or NgRx SignalStore suits large apps with complex flows. In React, Context works for low-frequency data like theme or user, Redux Toolkit or Zustand for complex client state, and TanStack Query for server data with caching. Keep server data and UI state separate.
91

Your Angular app's first load is slow on mobile because the main bundle is very large. How does lazy loading of routes help?

Save to ▾
Show answer
Lazy loading splits the app into chunks that download only when the user navigates to that route, so the initial bundle is smaller and the first page loads faster, especially on mobile data. In Angular use loadComponent or loadChildren with a dynamic import in the route config; in React use React.lazy with Suspense or the router's lazy option. Angular's @defer blocks can also delay heavy parts of a page.
92

An admin page renders 20,000 rows in a table and the browser freezes. How do you fix it?

Save to ▾
Show answer
Do not send or render everything at once. Add server-side paging, sorting and filtering in the ASP.NET Core API so the page loads perhaps 50 rows at a time. If users truly need to scroll a long list, use virtual scrolling, such as Angular CDK virtual scroll or TanStack Virtual in React, so only visible rows exist in the DOM. Also use trackBy or keys, and debounce search inputs.
93

How would you deploy an Angular or React front end together with an ASP.NET Core API?

Save to ▾
Show answer
Two common options exist. Build the SPA with ng build or npm run build and serve the static files from the API's wwwroot with UseStaticFiles and MapFallbackToFile, giving one deployment and no CORS. Or host the SPA separately on a CDN or static hosting such as Azure Static Web Apps and the API on App Service or containers, which scales independently. Use environment config for API URLs and a CI/CD pipeline for both.
94

How do you handle requirements that keep changing during a sprint?

Save to ▾
Show answer
In one sprint (Situation) the product owner changed a checkout screen twice. My task was to deliver without losing quality. I asked for a quick meeting to understand the reason, split the work into a stable API part and a flexible UI part, and agreed with the team which change would move to the next sprint. Result: we shipped the core flow on time, and the team started confirming acceptance criteria before sprint planning.

Frequently asked questions

Who should practise medium .NET Full Stack questions?

Anyone with some hands-on practice: most real interview time is spent on medium questions like these.

How should I practise these?

Answer each question aloud before opening the answer, mark it as solved only when your answer matches the key idea, and save misses to Last-Minute Revision.

Other difficulty levels