A synchronous ASP.NET Core MVC action (running under the default request context in a legacy self-hosted scenario with a SynchronizationContext) calls `var data = GetDataAsync().Result;`. Inside `GetDataAsync`, an `await httpClient.GetStringAsync(url)` is used without `ConfigureAwait(false)`. The request hangs forever. What is the precise root cause?
- A. The HttpClient instance is not thread-safe and blocks its own connection pool
- B. The awaited continuation is posted back to the captured SynchronizationContext, whose only thread is already blocked on .Result waiting for that continuation ✓
- C. GetStringAsync internally uses a ThreadPool thread that is exhausted by the blocking call
- D. .Result throws an AggregateException that is silently swallowed, leaving the task incomplete
Correct answer: B. The await captures the single-threaded context; the continuation is queued to a thread that is blocked on .Result, so neither can proceed — ConfigureAwait(false) avoids capturing the context and breaks the deadlock.
Consider: `var actions = new List<Func<int>>(); for (int i = 0; i < 3; i++) actions.Add(() => i); Console.WriteLine(string.Join(",", actions.Select(a => a())));` in C# 4.0 semantics (pre-C# 5 foreach fix, classic for loop). What is printed?
- A. 0,1,2
- B. 3,3,3 ✓
- C. 0,0,0
- D. 2,2,2
Correct answer: B. A `for` loop's counter `i` is a single variable captured by reference by all closures, so after the loop each lambda reads the final value 3 — unlike `foreach`, which since C# 5 declares a fresh variable per iteration.
You register `IClock` (a Scoped service that reads a per-request tenant id) and inject it into a Singleton `CacheWarmer`. In production the warmer intermittently serves data for the wrong tenant. What is the correct explanation and fix?
- A. Scoped services are thread-unsafe by design; wrap IClock access in a lock
- B. The Singleton captured the first request's scoped IClock instance (captive dependency); inject IServiceScopeFactory and resolve IClock inside a created scope per operation ✓
- C. Scoped lifetime is invalid for interfaces; re-register IClock as Transient to get a fresh instance each call
- D. The DI container leaks the scope; call GC.Collect() after each request to release it
Correct answer: B. A Singleton resolving a Scoped dependency captures it once for the app's lifetime (the captive dependency problem); the fix is to inject IServiceScopeFactory and create a scope per unit of work.
This EF Core query loops `foreach (var o in ctx.Orders.ToList()) { total += o.Customer.Name.Length; }` with lazy loading enabled. Against 1,000 orders it issues 1,001 database round trips. Which single change eliminates the N+1 without changing tracked results?
- A. Add `.AsNoTracking()` to the Orders query
- B. Replace `.ToList()` with `.AsEnumerable()` to stream results
- C. Use `ctx.Orders.Include(o => o.Customer).ToList()` so Customer is loaded in the same query ✓
- D. Wrap the loop in a single explicit transaction with Serializable isolation
Correct answer: C. Each `o.Customer` access triggers a separate lazy-load query (the N+1); `Include` eager-loads Customer in the original query, collapsing 1,001 round trips into one.
A payment service receives the same 'charge $50' message twice due to at-least-once broker delivery. To make the handler idempotent so the customer is charged once, which design is correct and race-safe under concurrent duplicate delivery?
- A. Check `SELECT` for an existing charge with the same idempotency key, then `INSERT` if absent — in two separate statements
- B. Persist the charge with the client-supplied idempotency key under a UNIQUE constraint in the same transaction as the charge, treating a duplicate-key violation as 'already processed' ✓
- C. Add a 5-second in-memory dedup cache of recently seen message ids on each service instance
- D. Configure the broker for exactly-once delivery so the handler needs no idempotency logic
Correct answer: B. A non-atomic check-then-insert has a TOCTOU race under concurrency; a UNIQUE constraint on the idempotency key committed atomically with the charge lets the database enforce single-charge semantics, and exactly-once broker delivery is generally unattainable end-to-end.
In an ASP.NET Core pipeline, a developer writes `app.UseRouting(); app.UseEndpoints(...); app.UseAuthorization();` (authorization added AFTER endpoints). Requests to `[Authorize]` endpoints succeed even when unauthenticated. Why?
- A. UseAuthorization only works with cookie auth, not JWT bearer
- B. The authorization middleware is registered after the endpoint has already executed, so its policy check never runs for the matched endpoint ✓
- C. Endpoint routing disables all middleware ordering, so position is irrelevant
- D. UseAuthorization must be called before UseRouting to build the endpoint graph
Correct answer: B. Middleware runs in registration order; placing UseAuthorization after the endpoint execution means the terminal endpoint handles the request before the authorization check can enforce the policy.
Under sudden load, a service that does `httpClient.GetAsync(...).GetAwaiter().GetResult()` on every request sees requests time out and thread count climb, even though CPU is near-idle. What is happening?
- A. Thread pool starvation: each request blocks a pool thread on I/O, the pool grows only ~1 thread/500ms, so incoming work queues faster than threads are injected ✓
- B. The garbage collector is pausing all threads in a Gen 2 full collection under load
- C. HttpClient's connection pool is exhausted, forcing synchronous DNS lookups on each call
- D. The CLR is JIT-recompiling the hot path repeatedly, consuming threads
Correct answer: A. Sync-over-async blocks a thread pool thread for the whole I/O duration; the pool injects new threads slowly, so under load the queue outpaces thread injection — classic thread pool starvation despite idle CPU.
You have `struct Point { public int X; public void Inc() => X++; }` and store instances in a `List<Point>`. Calling `myList[0].Inc()` does not change the stored value, but calling `Inc()` on a `Point[]` element via `myArray[0].Inc()` does. Why the difference?
- A. List<T> boxes value types on insertion, so Inc mutates a boxed copy
- B. List<T>'s indexer returns a copy of the struct (a method call), while an array element access returns a direct reference to the element ✓
- C. Arrays store structs by reference while List<T> stores them by value
- D. The array version also fails; both leave the original unchanged
Correct answer: B. `List<T>`'s indexer is a property returning a by-value copy that Inc mutates and discards, whereas array indexing yields a direct addressable location so the in-place mutation sticks — a well-known mutable-struct trap.
A method returns `ValueTask<int>`. A caller writes `var vt = obj.ComputeAsync(); int a = await vt; int b = await vt;` where the implementation pools its IValueTaskSource. What is the correct assessment?
- A. Fully safe — ValueTask is just a lightweight Task and can be awaited any number of times
- B. Awaiting the same ValueTask twice is undefined behavior; the backing IValueTaskSource may be reset/reused, so the second await can throw or return a wrong/torn result ✓
- C. The second await always returns the cached result with zero cost, which is the point of ValueTask
- D. It only fails if the ValueTask wraps a Task; pooled sources are always safe to re-await
Correct answer: B. A ValueTask backed by a pooled IValueTaskSource must be consumed exactly once; the token/version can be recycled after the first await, making a second await undefined behavior.
A high-throughput producer/consumer needs bounded buffering with backpressure so fast producers block when consumers fall behind, plus native async consumption. Which primitive best fits and why over the alternatives?
- A. `ConcurrentQueue<T>` with a busy-wait polling consumer loop for lowest latency
- B. `Channel<T>` created bounded with `BoundedChannelFullMode.Wait`, exposing an async writer that yields on a full buffer and an IAsyncEnumerable reader ✓
- C. `BlockingCollection<T>`, since its async support gives non-blocking backpressure out of the box
- D. `List<T>` guarded by a single lock, resizing to absorb bursts
Correct answer: B. A bounded `Channel<T>` in Wait mode provides asynchronous, non-thread-blocking backpressure and native async reads; BlockingCollection blocks threads, ConcurrentQueue has no built-in bounding/backpressure, and a locked List is neither async nor bounded.
Consider `async Task M() { await Task.Delay(100); }` called as `M().Wait()` on a UI thread with a synchronization context. What is the classic risk?
- A. A compile error
- B. A deadlock because the continuation needs the UI thread that is blocked on Wait() ✓
- C. An immediate NullReferenceException
- D. The delay is skipped entirely
Correct answer: B. Wait() blocks the UI thread while the awaited continuation tries to resume on that same captured context, causing a deadlock.
In the .NET garbage collector, what best describes the purpose of the Large Object Heap (LOH)?
- A. It stores objects >= 85,000 bytes and is collected with Gen 2, historically not compacted by default ✓
- B. It stores all Gen 0 objects
- C. It stores only string literals
- D. It is compacted on every Gen 0 collection
Correct answer: A. Objects of ~85,000 bytes or larger go on the LOH, collected as part of Gen 2 and, by default, not compacted (compaction is opt-in).
Given `struct Point { public int X; }` used as a dictionary key implementing default equality, what performance concern arises with a struct that does not override GetHashCode/Equals?
- A. It cannot be a key at all
- B. Default ValueType.Equals/GetHashCode use reflection over fields, which can be slow ✓
- C. It always boxes on every method call regardless
- D. It causes a compile error
Correct answer: B. The default ValueType implementations can fall back to reflection-based field comparison/hashing, so overriding them is recommended for struct keys.
In EF Core, a query with `.Include()` on multiple collection navigations can cause a 'cartesian explosion'. Which feature mitigates this in modern EF Core?
- A. AsNoTracking
- B. Split queries via AsSplitQuery() ✓
- C. Lazy loading proxies
- D. Compiled models
Correct answer: B. AsSplitQuery() issues separate SQL queries per collection include, avoiding the row multiplication of a single joined query.
What does the C# `Span<T>` type provide that a normal array reference does not, and what is its key constraint?
- A. Heap-only storage; can be a class field
- B. A contiguous memory view (stack or heap, incl. stackalloc) that is a ref struct and cannot be boxed or used across await ✓
- C. Automatic thread safety
- D. Garbage-collection immunity for all referenced memory
Correct answer: B. Span<T> is a ref struct giving a zero-copy view over contiguous memory, but being stack-only it cannot be boxed, stored on the heap, or captured across await/yield.
In ASP.NET Core, injecting a Scoped service into a Singleton via constructor causes what?
- A. Nothing; it is recommended
- B. A captive dependency: the scoped service is effectively promoted to singleton lifetime, risking stale/incorrect state ✓
- C. A guaranteed compile error
- D. The singleton becomes transient
Correct answer: B. This is the captive dependency problem—the scoped instance lives as long as the singleton, defeating its per-request semantics.
When implementing IDisposable with the full Dispose pattern, why include a finalizer (~Class) and call GC.SuppressFinalize(this) in Dispose?
- A. To force immediate garbage collection
- B. The finalizer is a safety net for unmanaged resources; SuppressFinalize avoids the costly extra finalization pass once Dispose already cleaned up ✓
- C. To make the class thread-safe
- D. To prevent the object from ever being collected
Correct answer: B. The finalizer guards unmanaged resources if Dispose is missed; SuppressFinalize removes the object from the finalization queue when Dispose already ran.
In C#, what is the difference between `Task` continuation behavior with `ValueTask<T>` and `Task<T>` for a frequently-synchronous async method?
- A. ValueTask always allocates more
- B. ValueTask<T> can avoid heap allocation when the result is available synchronously, but must not be awaited more than once ✓
- C. They are interchangeable in all cases with no rules
- D. ValueTask cannot be awaited
Correct answer: B. ValueTask<T> reduces allocations for synchronously-completing paths but carries the constraint that it should be consumed (awaited) only once.
A high-throughput API sees thread-pool starvation under load. Which pattern is the most likely root cause?
- A. Using async/await for all I/O
- B. Synchronously blocking on async calls (e.g., .Result/.Wait) which ties up pool threads ✓
- C. Registering services as Transient
- D. Enabling response compression
Correct answer: B. Blocking on async (sync-over-async) consumes pool threads while they wait, starving the pool under concurrency—true async avoids this.
Regarding string interning in .NET, what is true of two string literals with identical content within the same assembly?
- A. They are always distinct objects
- B. The compiler interns them so both references point to the same intern-pool instance ✓
- C. They can never be compared with ==
- D. Interning only happens at garbage collection
Correct answer: B. Compile-time literals are automatically interned, so identical literals share one reference in the intern pool.
In .NET's garbage collector, what is the purpose of the Large Object Heap (LOH) and why can it cause issues?
- A. It stores all reference types
- B. Objects >= ~85,000 bytes are allocated on the LOH, which historically was not compacted, leading to fragmentation ✓
- C. It is a faster generation-0 heap
- D. It only holds strings
Correct answer: B. Objects >= ~85KB go on the LOH, which by default is not compacted, so fragmentation can waste memory and cause OOM.
What subtle bug can arise from using 'async void' methods (other than event handlers)?
- A. They run synchronously
- B. Exceptions thrown cannot be caught by the caller via await and can crash the process ✓
- C. They cannot contain await
- D. They always deadlock
Correct answer: B. async void methods can't be awaited, so their exceptions propagate to the SynchronizationContext and may crash the app.
In EF Core, when tracking is enabled and you load an entity, modify it, then load the same key again in the same context, what happens?
- A. A new instance is returned every time
- B. The context returns the already-tracked instance (identity resolution), so your in-memory change is preserved and the DB values may be ignored ✓
- C. It throws a concurrency exception
- D. The entity is duplicated in the change tracker
Correct answer: B. EF Core's identity map returns the already-tracked instance for the same key, so a second load does not overwrite tracked changes.
Why might 'ValueTask<T>' be preferred over 'Task<T>' in a hot async path, and what is the caveat?
- A. ValueTask is always faster and has no caveats
- B. ValueTask can avoid heap allocation when results are often synchronous, but must not be awaited multiple times or accessed after consumption ✓
- C. ValueTask cannot be awaited
- D. ValueTask disables exceptions
Correct answer: B. ValueTask avoids allocations for frequently-synchronous results but must be consumed once; reusing it leads to undefined behavior.
What does the C# 'volatile' keyword guarantee in a multithreaded scenario?
- A. Atomicity of compound operations like i++
- B. It prevents certain compiler/CPU reorderings and ensures reads/writes go to main memory, but does not make compound operations atomic ✓
- C. A full memory barrier around every method
- D. That the field is thread-local
Correct answer: B. volatile enforces acquire/release ordering and fresh reads/writes but does not make read-modify-write operations atomic.
In ASP.NET Core middleware, what is the consequence of not calling 'await next(context)' in a middleware component?
- A. The pipeline continues normally
- B. The rest of the pipeline (downstream middleware and endpoint) is short-circuited and never executes ✓
- C. It throws a compile-time error
- D. It runs the next middleware twice
Correct answer: B. Omitting the call to next short-circuits the pipeline, so downstream middleware and the endpoint are skipped.
Boxing a value type in C# has what performance and correctness implication in a dictionary keyed by an interface?
- A. It has no cost
- B. Boxing allocates on the heap and creates a copy; mutating a boxed value type via interface affects only the box, causing subtle bugs ✓
- C. It makes the value type immutable permanently
- D. It converts the struct to a class definition
Correct answer: B. Boxing heap-allocates a copy; mutating through an interface changes the box, not the original, and adds GC pressure.
In a high-throughput API, why might you use IAsyncEnumerable<T> with 'await foreach' instead of returning Task<List<T>>?
- A. It is required for all APIs
- B. It streams items as they become available with backpressure, avoiding buffering the entire result set in memory ✓
- C. It disables async entirely
- D. It guarantees ordering that List cannot
Correct answer: B. IAsyncEnumerable streams results incrementally, reducing memory pressure versus materializing the whole list before returning.
What is a potential pitfall of registering a Scoped service as a dependency of a Singleton in ASP.NET Core DI?
- A. It works fine with no consequences
- B. The Scoped instance gets captured (captive dependency) and effectively becomes a singleton, potentially outliving its intended scope and causing stale/threading issues ✓
- C. It throws at compile time always
- D. Singletons cannot have dependencies
Correct answer: B. A captive dependency: the scoped service is captured by the singleton and lives for the app lifetime, breaking scope semantics.
In C# pattern matching, what does the compiler do with an exhaustive switch expression over an enum missing a case at runtime with an unmapped value?
- A. Returns default(T) silently
- B. Throws a SwitchExpressionException at runtime because no arm matched ✓
- C. Skips the switch
- D. Compiles to an infinite loop
Correct answer: B. A switch expression with no matching arm (and no discard) throws SwitchExpressionException at runtime.