ASP.NET Core • Dependency Injection • EF Core • Security • Performance • 2026

.NET Interview Questions

30 questions What each one tests, an answer frame, a spoken answer 35 min read

This page is for developers facing a .NET backend round, from a first job to a senior role. Most interviews start with the ASP.NET Core request pipeline and dependency injection lifetimes, move to configuration, routing, controllers and minimal APIs, then test JWT authentication and authorization and Entity Framework Core: tracking, loading, migrations and concurrency. Senior rounds add performance, background services, integration testing and a production story. Each question shows what the interviewer is really checking, the shape of a strong answer and a short answer you can say out loud. C# language questions have their own page.

Search all questions by round, difficulty and level, or save the ones you want to practise.

Hosting & Middleware 4 questions

Easy Technical round Fresher, Mid-level Practice question

1. What is middleware in ASP.NET Core, and why does the order you add it in Program.cs matter so much?

What the interviewer is really testing:
Whether you picture a request flowing through a chain of components, and can explain real bugs caused by putting them in the wrong order.
Answer frame:

Pipeline: each middleware gets the HttpContext, can act, then call the next one or stop the request there.

Two directions: the request goes in in registration order; the response comes back out in reverse.

Order bugs: exception handling first, authentication before authorization, both before the endpoints.

Sample spoken answer:

"Middleware is a chain of small components that every request passes through. Each one gets the HttpContext, can do some work, and then either calls the next one or short-circuits and writes the response itself, like the static files middleware does when it finds a file. The request travels down the chain in the order I registered things, and the response travels back up in reverse. That's why order matters. The exception handler goes near the top so it can catch errors from everything below it. Authentication has to run before authorization, otherwise authorization sees an anonymous user and every protected call fails. And anything that needs to know which endpoint was picked, like authorization or CORS, has to sit after routing. Most odd pipeline bugs I've seen came down to one line in the wrong place."

Red flag to avoid:

Treating the registration order as cosmetic, or not knowing that the response passes back through the middleware in reverse.

They may ask next:
  • What is the difference between app.Use, app.Run and app.Map?
  • Where would you put CORS and response compression, and why?
Say it in 60 seconds
Medium Coding round Fresher, Mid-level Practice question

2. Write a custom middleware that logs the method, path, status code and duration of every request.

What the interviewer is really testing:
Whether you can write a convention-based middleware correctly, including where to measure, how to survive exceptions and what the middleware lifetime means for its dependencies.
Answer frame:

Shape: a class taking RequestDelegate in the constructor and exposing InvokeAsync(HttpContext).

Measure around next: start the timer, await next, log in a finally block so failures are timed too.

Lifetime: the class is created once, so scoped services go in InvokeAsync parameters, not the constructor.

Sample spoken answer:

"I'd write a class that takes the next RequestDelegate and a logger in its constructor, and has an InvokeAsync method. In InvokeAsync I take a timestamp, await the next delegate, and in a finally block I work out the elapsed time and log the method, path, status code and milliseconds. The finally matters, because I want slow requests that throw to show up too. I'd log with a message template and named placeholders, not string interpolation, so the log system can filter by path or status. Then I register it with UseMiddleware near the top of the pipeline so it measures almost everything. One detail people miss: a middleware class like this is built once for the life of the app, so if I needed a scoped service, like a DbContext, I'd add it as a parameter on InvokeAsync, not in the constructor."

Code:
public sealed class RequestTimingMiddleware(
    RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
{
    public async Task InvokeAsync(HttpContext context)
    {
        long start = Stopwatch.GetTimestamp();
        try
        {
            await next(context);
        }
        finally
        {
            logger.LogInformation("{Method} {Path} -> {StatusCode} in {ElapsedMs} ms",
                context.Request.Method, context.Request.Path, context.Response.StatusCode,
                Stopwatch.GetElapsedTime(start).TotalMilliseconds);
        }
    }
}
// Program.cs: app.UseMiddleware<RequestTimingMiddleware>();
Red flag to avoid:

Injecting a scoped service such as a DbContext into the middleware constructor, or logging with string interpolation.

They may ask next:
  • How would you add the duration as a response header, given headers cannot change once the body has started?
  • If an exception escapes past this middleware with no handler below it, what status code gets logged, and why is that misleading?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

3. What is Kestrel, and why do teams often put IIS, Nginx or a load balancer in front of it?

What the interviewer is really testing:
Whether you know how an ASP.NET Core app is actually served, and the one setting that breaks when a proxy sits in front.
Answer frame:

Kestrel: the cross-platform web server built into ASP.NET Core; the app runs on it by default.

Proxy jobs: TLS termination, load balancing, many sites on one port, extra hardening.

Forwarded headers: behind a proxy, restore the real client IP and scheme with the forwarded headers middleware.

Sample spoken answer:

"Kestrel is the web server that ships inside ASP.NET Core. When I call app.Run, the host starts Kestrel and it listens for HTTP requests on whatever ports I've configured. It's fast and can face the internet directly, but in practice teams often put something in front of it: IIS on Windows, Nginx on Linux, or a cloud load balancer. That front layer handles TLS certificates, spreads traffic across instances, lets several apps share port 443, and adds limits and filtering. On IIS there's also in-process hosting, where the app runs inside the IIS worker process instead of behind it. The catch with any proxy is that the app now sees the proxy's IP and maybe plain HTTP. So I add the forwarded headers middleware, and tell it which proxies to trust, so redirects, logs and rate limits use the real client details."

Red flag to avoid:

Thinking an ASP.NET Core app needs IIS to run, or never having heard of forwarded headers.

They may ask next:
  • Why is trusting X-Forwarded-For from any source a security problem?
  • What is the difference between in-process and out-of-process hosting on IIS?
Say it in 60 seconds
Hard Behavioral round Mid-level, Senior Practice question

4. Have you moved an application from the old .NET Framework to modern .NET? What broke, and how did you manage the risk?

What the interviewer is really testing:
Whether you have done or can plan a real platform migration, know the typical blockers and prefer an incremental path over a big-bang rewrite.
Answer frame:

Inventory: which dependencies and APIs had no modern equivalent, such as System.Web or Windows-only pieces.

Path: libraries first, then the web layer, ideally incrementally behind a proxy.

Safety: tests around behaviour, side-by-side running, a rollback route, clear metrics.

Sample spoken answer:

"Yes, at my last company we moved an ASP.NET MVC app on .NET Framework to ASP.NET Core. The biggest blockers were code that used HttpContext.Current and other System.Web types deep in the business layer, settings in web.config, and a couple of Windows-only libraries. We started with an inventory, then moved the class libraries first, because they had the fewest web dependencies. HttpContext.Current had to be replaced by passing the data we actually needed, which was a good cleanup anyway. For the web layer we didn't do a big bang. We put a reverse proxy in front, moved a few routes to the new app at a time, and left the rest on the old one until each slice was proven. Before each move we wrote API-level tests against the old behaviour. Sharing authentication cookies across both apps was the fiddliest part."

Red flag to avoid:

Describing a big-bang rewrite with no tests or rollback plan, or not knowing what System.Web dependencies mean for a move.

They may ask next:
  • How did you share login state between the old and the new application during the move?
  • What would make you choose a rewrite over an incremental migration?
Say it in 60 seconds

Dependency Injection 2 questions

Easy Technical round Fresher, Mid-level Practice question

5. Explain the singleton, scoped and transient lifetimes, and give a real example of when you would use each one.

What the interviewer is really testing:
Whether you can choose a lifetime on purpose, based on state and thread safety, instead of copying whatever the last registration used.
Answer frame:

Transient: a new instance every time it is resolved; for light, stateless services.

Scoped: one instance per scope, which in a web app means per request; DbContext is the classic case.

Singleton: one instance for the app; must be thread-safe; caches, clients, configuration holders.

Sample spoken answer:

"The lifetime decides how long the container keeps an instance. Transient means I get a new object every time something asks for it, so it suits small stateless helpers, like a price formatter. Scoped means one object per scope, and in ASP.NET Core every request gets its own scope, so everything in one request shares it. The DbContext is the classic example, because I want one unit of work and one change tracker per request, not shared across users. Singleton means one object for the whole life of the app, shared by every request on every thread, so it has to be thread-safe. I'd use it for things that are expensive to build and safe to share, like an in-memory cache or a client that holds connections. The container also disposes what it created when the scope ends, so scoped and transient disposables get cleaned up per request."

Red flag to avoid:

Saying scoped means per thread, or making everything singleton for speed without mentioning thread safety.

They may ask next:
  • If you register two implementations of the same interface, which one gets injected, and how do you get both?
  • What happens to a transient service that is injected into a singleton?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

6. A singleton service takes a scoped service in its constructor. What goes wrong, and how do you fix it properly?

What the interviewer is really testing:
Whether you understand captive dependencies well enough to predict the production symptoms, and know the safe pattern for using scoped work from a long-lived object.
Answer frame:

Captured: the scoped object is created once and lives as long as the singleton, shared by every request.

Symptoms: stale data, a DbContext hit by two threads at once, memory growth from one change tracker.

Guard and fix: scope validation throws in Development; fix the lifetime or create a scope per unit of work.

Sample spoken answer:

"That's a captive dependency. The singleton is built once, so the scoped service it receives is also built once and then held for the life of the app. It stops being per request. If that scoped service is a DbContext, every request now shares one context: its change tracker keeps growing, data goes stale, and sooner or later two requests use it at the same moment and EF throws because a second operation started before the first finished. The good news is that the default host validates scopes in the Development environment, so it usually fails at startup with a message saying it can't consume a scoped service from a singleton. The fix depends on the design. Often the outer service shouldn't be a singleton at all, so I make it scoped. If it really must live long, I inject IServiceScopeFactory and create a scope for each piece of work."

Red flag to avoid:

Fixing it by making the DbContext a singleton too, or by turning off scope validation.

They may ask next:
  • Scope validation is only on in Development by default. How would you make sure this mistake is also caught in CI or other environments?
  • Is a transient injected into a singleton also a problem, and when?
Say it in 60 seconds

Configuration & Logging 3 questions

Easy Technical round Fresher, Mid-level Practice question

7. Where does an ASP.NET Core app read its configuration from, and when the same key is set in two places, which value wins?

What the interviewer is really testing:
Whether you can explain why a setting has a surprising value in some environment, and keep secrets out of source control.
Answer frame:

Layered sources: appsettings.json, then the environment file, user secrets in Development, environment variables, command line.

Last wins: a later source overrides an earlier one for the same key.

Secrets: never in committed files; user secrets locally, environment variables or a secret store in production.

Sample spoken answer:

"Configuration is built from several providers stacked in layers, and the last one to set a key wins. With the default builder, it reads appsettings.json first, then the file for the current environment, like appsettings.Production.json. In Development it adds user secrets. Then environment variables, then command-line arguments on top. So if a connection string is in appsettings.json but also set as an environment variable on the server, the environment variable wins. Nested keys use a colon in code, like ConnectionStrings:Default, and a double underscore in environment variable names, because some platforms don't allow colons there. The environment itself comes from the ASPNETCORE_ENVIRONMENT variable. For secrets, I keep them out of any committed file. Locally I use user secrets, and in production I use environment variables or a proper secret store that the app loads as another configuration source."

Red flag to avoid:

Committing production passwords in appsettings.json, or not knowing that later sources override earlier ones.

They may ask next:
  • Why are user secrets fine on a laptop but not a way to protect production secrets?
  • A value looks right in appsettings.Production.json but the app ignores it. What would you check?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

8. What is the options pattern, and how do IOptions, IOptionsSnapshot and IOptionsMonitor behave differently?

What the interviewer is really testing:
Whether you bind configuration to typed classes, pick the right interface for reload behaviour and lifetime, and fail fast on bad settings.
Answer frame:

Typed settings: bind a config section to a class instead of reading string keys everywhere.

Three flavours: IOptions is read once; IOptionsSnapshot is scoped and recomputed per request; IOptionsMonitor is a singleton that tracks changes.

Validate at startup: data annotations or custom rules plus ValidateOnStart, so bad config stops the deploy.

Sample spoken answer:

"The options pattern binds a configuration section to a plain class, so my code asks for a typed SmtpOptions instead of reading string keys. There are three ways to consume it. IOptions gives me the value computed once, so later changes to the file are never seen. It's fine for settings that only change with a deploy. IOptionsSnapshot is scoped: it's recomputed for each request, so it picks up reloaded files, but because it's scoped I can't inject it into a singleton. IOptionsMonitor is a singleton, its CurrentValue always gives the latest value, and it has an OnChange callback, so it's what I use in singletons and background services. I also validate options. I add data annotation rules and call ValidateOnStart, so a missing host name stops the app at startup instead of failing on the first email at two in the morning."

Code:
public sealed class SmtpOptions
{
    [Required] public string Host { get; set; } = "";
    [Range(1, 65535)] public int Port { get; set; } = 587;
}

builder.Services.AddOptions<SmtpOptions>()
    .Bind(builder.Configuration.GetSection("Smtp"))
    .ValidateDataAnnotations()
    .ValidateOnStart();
Red flag to avoid:

Injecting IOptionsSnapshot into a singleton, or expecting IOptions to pick up changes without a restart.

They may ask next:
  • What are named options, and when have you needed them?
  • How would you validate a rule that involves two settings together?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

9. How do you log properly in ASP.NET Core with ILogger, and why should you not build log messages with string interpolation?

What the interviewer is really testing:
Whether your logs will be searchable and cheap in production, and whether you know categories, levels and what must never be logged.
Answer frame:

Templates: named placeholders keep values as searchable properties; interpolation flattens them to text.

Levels and categories: ILogger<T> sets the category; config filters levels per category.

Hygiene: scopes for correlation, no secrets or personal data, source-generated logging for hot paths.

Sample spoken answer:

"I inject ILogger of my class, which makes the class name the log category, and I pick a level that matches the event: Information for normal business events, Warning for something odd but handled, Error with the exception object when something failed. I always use a message template with named placeholders, like 'Order {OrderId} shipped', and pass the values as arguments. With a structured sink, OrderId becomes a real field I can filter on. If I interpolate the string myself, it's just text, and I also pay the formatting cost even when that level is switched off. Levels are set per category in configuration, so I can turn up EF Core's SQL logging without touching code. I use scopes to attach a correlation ID to everything in a request, and I never log passwords, tokens or personal data. For very hot paths there's the LoggerMessage source generator."

Red flag to avoid:

Logging with string interpolation or Console.WriteLine, or logging full request bodies with tokens in them.

They may ask next:
  • How would you see the SQL that EF Core sends, only in one environment?
  • How do you connect all the logs for one request across two services?
Say it in 60 seconds

Web APIs 4 questions

Medium Technical round Fresher, Mid-level Practice question

10. How does routing work in ASP.NET Core? Walk me through attribute routes, route parameters and constraints.

What the interviewer is really testing:
Whether you understand that routing picks an endpoint early and runs it late, and can design routes that are clear and unambiguous.
Answer frame:

Match then run: routing picks the endpoint; middleware in between can read its metadata; the endpoint runs last.

Templates: attribute routes on controllers, Map methods for minimal APIs, parameters in braces.

Constraints: like int or guid, to separate routes that would otherwise clash, not to validate input.

Sample spoken answer:

"ASP.NET Core uses endpoint routing. Early in the pipeline, routing looks at the path and method and picks the matching endpoint. Middleware that runs after that can see which endpoint was chosen and its metadata, which is how authorization knows an action has an Authorize attribute. The endpoint itself runs at the end. For APIs I use attribute routing on controllers, like a Route attribute of api/orders on the class and HttpGet with an id template on the method, or MapGet and MapGroup in minimal APIs. Route parameters sit in braces and bind to method parameters. Constraints like colon int or colon guid help pick between routes that look alike. I don't use them as validation, though, because a failed constraint gives a 404, and for bad input the client deserves a 400 with a reason."

Red flag to avoid:

Believing attribute routes are matched in the order they were declared, or using constraints as the main input validation.

They may ask next:
  • Two routes match the same URL. How does ASP.NET Core decide, and what happens if it cannot?
  • How would you version this API in the URL or in a header?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level, Senior Practice question

11. Controllers or minimal APIs: how do they differ, and how would you choose for a new service?

What the interviewer is really testing:
Whether you know both styles and can make a reasoned choice for a team, rather than repeating that one is modern and the other is old.
Answer frame:

Controllers: classes grouped by resource, ApiController conventions, automatic 400s, filters, familiar structure.

Minimal APIs: lambdas or methods mapped directly, less ceremony, route groups and endpoint filters.

Choice: team habits, size of the API and the features you rely on; both run on the same pipeline.

Sample spoken answer:

"Both sit on the same routing, dependency injection and middleware, so the difference is mostly how I organise and describe endpoints. Controllers are classes with action methods. With the ApiController attribute I get conventions like automatic 400 responses when model validation fails, and I can use the full set of MVC filters. It's a structure most .NET developers already know, which helps on a big API with many people. Minimal APIs map a route straight to a lambda or a method with MapGet or MapPost. There's less ceremony, it starts a bit lighter, and I can group routes with MapGroup and share things like authorization or endpoint filters on the group. For a small service or a new team I'd lean minimal. For a large existing codebase built on controllers, or one that relies on MVC-specific filters and conventions, I'd stay with controllers. I wouldn't mix them randomly in one service."

Red flag to avoid:

Saying minimal APIs are only for demos, or that controllers are deprecated.

They may ask next:
  • How would you get request validation in a minimal API endpoint?
  • How do you keep Program.cs from growing into a thousand-line file with minimal APIs?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

12. What are filters in ASP.NET Core, how are they different from middleware, and in what order do the filter types run?

What the interviewer is really testing:
Whether you know where cross-cutting logic belongs: in the pipeline for every request, or around an action where the arguments and result are visible.
Answer frame:

Middleware: sees every request but only the raw HttpContext.

Filters: run inside the action pipeline and see action arguments, model state and the result.

Order: authorization, resource, then model binding, action, exception, result; endpoint filters for minimal APIs.

Sample spoken answer:

"Middleware runs for every request and only knows the HttpContext. Filters run inside the MVC action pipeline, after an action has been chosen, so they can see things middleware can't: the bound action arguments, the model state, and the result object before it's written. The order goes authorization filters first, then resource filters, which run before model binding and are good for short-circuiting, like a cache. Then model binding, then action filters around the action itself, exception filters for errors thrown by actions and filters, and result filters around writing the result. I use middleware for things that apply to everything, like correlation IDs or request logging. I use a filter when the logic is about the action, like auditing with the actual arguments, or shaping validation errors. Minimal APIs have their own endpoint filters that play a similar role."

Red flag to avoid:

Using an exception filter as the only global error handler, or saying filters and middleware are the same thing.

They may ask next:
  • Why will an exception filter not catch an exception thrown in middleware?
  • How do you give a filter constructor dependencies from the container?
Say it in 60 seconds
Medium Coding round Mid-level, Senior Practice question

13. How do you handle unhandled exceptions globally in an ASP.NET Core API so clients always get a consistent error response?

What the interviewer is really testing:
Whether you centralise error handling in the pipeline, return a standard shape, log with context and avoid leaking internals.
Answer frame:

One place: the exception handler middleware at the top, not try/catch in every action.

Standard shape: Problem Details with a status, a title and a trace ID the client can quote.

Safe: log the full exception on the server; never send stack traces to clients outside Development.

Sample spoken answer:

"I handle it once, in the pipeline, instead of wrapping every action in try and catch. I register the exception handler middleware near the top so it catches anything thrown below it. In recent versions I implement IExceptionHandler: it logs the exception with the request path, sets the status code, and writes a Problem Details body, which is the standard JSON error shape with a status, a title and extra fields. I add the trace identifier so a customer can quote it and I can find the exact logs. I can also map known exceptions, like a not-found exception from my domain layer, to 404 instead of 500. What I never do is return the exception message or stack trace to clients in production, because it leaks internals. The developer exception page is for Development only."

Code:
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
// after builder.Build():
app.UseExceptionHandler();

public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken ct)
    {
        logger.LogError(exception, "Unhandled exception on {Path}", context.Request.Path);
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        await context.Response.WriteAsJsonAsync(new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title = "Something went wrong.",
            Extensions = { ["traceId"] = context.TraceIdentifier }
        }, ct);
        return true;
    }
}
Red flag to avoid:

Returning exception.Message or the stack trace to the client, or copying try/catch into every controller action.

They may ask next:
  • How would you return 404 for a domain NotFoundException and 409 for a conflict from the same handler?
  • What does returning false from TryHandleAsync do?
Say it in 60 seconds

Security 4 questions

Medium Technical round Mid-level, Senior Practice question

14. Walk me through how JWT bearer authentication works in an ASP.NET Core API. What exactly does the API check on each request?

What the interviewer is really testing:
Whether you know what makes a token trustworthy, how it becomes a user in the app, and common misunderstandings about what a JWT protects.
Answer frame:

Flow: an identity provider issues the token; the client sends it in the Authorization header as Bearer.

Validation: signature, issuer, audience and expiry, with a small clock skew allowance.

Result: claims become HttpContext.User; 401 for no or bad token, 403 for valid token without permission.

Sample spoken answer:

"The client signs in with an identity provider and gets an access token, which is a signed JWT. On each call it sends it in the Authorization header as Bearer. In the API I add authentication with the JWT bearer handler, point it at the authority, and set the expected audience. For every request the handler checks the signature against the provider's signing keys, checks the issuer is the one I trust, the audience is my API, and the token hasn't expired, allowing a small clock skew. If all that passes, the claims become the ClaimsPrincipal on HttpContext.User, and authorization decides what that user may do. A missing or invalid token gives a 401, and a valid token without the right permission gives a 403. One thing I always point out: a JWT is signed, not encrypted, so anyone can read it, and nothing secret should go inside."

Red flag to avoid:

Saying a JWT is encrypted, or disabling issuer and audience validation to make a token work.

They may ask next:
  • A JWT stays valid until it expires. How would you handle logout or a stolen token?
  • Why is turning off audience validation dangerous even if the signature is checked?
Say it in 60 seconds
Hard Coding round Mid-level, Senior Practice question

15. Only the customer who owns an order, or support staff, may view it. How would you enforce that with ASP.NET Core authorization?

What the interviewer is really testing:
Whether you go beyond role checks to policies, requirements and resource-based authorization, and avoid scattering ownership checks through controllers.
Answer frame:

Policies over roles: name the rule once and apply it by name, not with role strings everywhere.

Requirement and handler: a handler that gets the user and the resource and calls Succeed when allowed.

Resource-based: load the order first, then call IAuthorizationService with it; return 403 or 404 if denied.

Sample spoken answer:

"A role check alone can't answer this, because the rule depends on the order itself. So I'd use resource-based authorization. I define a requirement, say CanViewOrder, and a handler for that requirement and the Order type. The handler succeeds if the user has the support role, or if the user's ID claim matches the order's customer ID. I register a policy that uses the requirement and register the handler in DI. In the endpoint, I load the order, then call IAuthorizationService.AuthorizeAsync with the user, the order and the policy name. If it fails, I return Forbid, or sometimes 404 so I don't confirm the order exists. The benefit is one tested place for the rule. Every endpoint that touches orders reuses it, instead of each developer writing their own slightly different if statement."

Code:
public sealed class CanViewOrderRequirement : IAuthorizationRequirement { }

public sealed class CanViewOrderHandler : AuthorizationHandler<CanViewOrderRequirement, Order>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, CanViewOrderRequirement requirement, Order order)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (context.User.IsInRole("Support") || order.CustomerId == userId)
            context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

// Program.cs
builder.Services.AddAuthorization(o =>
    o.AddPolicy("CanViewOrder", p => p.AddRequirements(new CanViewOrderRequirement())));
builder.Services.AddSingleton<IAuthorizationHandler, CanViewOrderHandler>();

// In the endpoint, after loading the order:
var result = await authz.AuthorizeAsync(User, order, "CanViewOrder");
if (!result.Succeeded) return Forbid();
Red flag to avoid:

Trusting a customer ID sent in the request body or query string instead of the claim from the validated token.

They may ask next:
  • When would you return 404 instead of 403 here?
  • How would you unit test the handler without starting the web app?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

16. Tell me about an ASP.NET Core API you secured. What authentication and authorization choices did you make, and why?

What the interviewer is really testing:
Whether your security decisions were deliberate and explainable, and whether you thought about the full picture: tokens, permissions, secrets and mistakes that fail open.
Answer frame:

Context: who called the API, users or other services, and what data it protected.

Choices: token type and provider, policies and permissions, secure by default.

Checks: how you tested it and one thing you would do differently.

Sample spoken answer:

"At my last company I built an API used by a web app and by one internal service. We didn't write our own login. We used an external identity provider, and the API validated JWT bearer tokens, checking issuer, audience and expiry. For the internal service we used client credentials, so it had its own identity and limited scopes instead of sharing a user account. The key decision was making the API secure by default: I added a fallback policy that requires an authenticated user, so a new endpoint without an attribute is protected rather than accidentally open. Permissions were policies based on claims, not role strings scattered through the code, and ownership checks used resource-based handlers. I wrote integration tests that call each endpoint anonymously and with the wrong user. Looking back, I'd have added token revocation earlier."

Red flag to avoid:

Building a home-made password and token system, or leaving endpoints open unless someone remembered to add an attribute.

They may ask next:
  • How would you rotate the signing keys without logging everyone out?
  • How do you stop a new endpoint from shipping without any authorization?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

17. Right after a deploy, every call to your API returns 401, even with tokens that worked an hour ago. What do you do?

What the interviewer is really testing:
Whether you protect users first, then debug authentication calmly from evidence instead of switching validation off.
Answer frame:

Stop the bleeding: everyone is locked out, so roll back first if you can.

Read the reason: the bearer handler logs why it rejected the token; the WWW-Authenticate header often says too.

Usual suspects: issuer or audience config per environment, signing keys, middleware order, server clock.

Sample spoken answer:

"Every user is locked out, so the first move is to roll back to the last good version if I can, and then debug calmly. I'd compare the config and code between the two versions. The JWT bearer handler logs the reason a token failed, like an audience mismatch or a bad signature, and the WWW-Authenticate header on the 401 often names the error too, so I'd look at that before guessing. The usual causes are an environment setting that changed the expected issuer or audience, the authority URL pointing at the wrong environment, a signing key that didn't load, a clock that drifted on the new servers, or someone moving UseAuthorization above UseAuthentication. Once I find it, I fix it and add a test that calls a protected endpoint with a real token in the pipeline. What I would never do is turn off validation to get it working."

Red flag to avoid:

Disabling issuer, audience or signature validation to make the errors stop.

They may ask next:
  • If rollback is not possible, what would you check in the first five minutes?
  • How would a smoke test after deploy have caught this before users did?
Say it in 60 seconds

EF Core 7 questions

Easy Technical round Fresher, Mid-level Practice question

18. What does the change tracker do in EF Core, and when should you use AsNoTracking?

What the interviewer is really testing:
Whether you know how SaveChanges decides what to write, and can make read-heavy endpoints cheaper without breaking updates.
Answer frame:

Tracking: the context remembers each loaded entity and its original values.

SaveChanges: compares current and original values and writes only what changed.

AsNoTracking: for read-only queries; less memory and work, but no automatic updates or identity resolution.

Sample spoken answer:

"When a query returns entities, the DbContext tracks them by default. It keeps a record of each entity and its original values. When I call SaveChanges, it compares what's there now with those originals and generates UPDATE statements only for the columns that changed, plus inserts and deletes for added and removed entities. That's convenient for updates, but it costs memory and CPU. For read-only work, like a list page or a report, I add AsNoTracking, so EF just materialises the objects and forgets them. That's faster, and on big result sets the difference is noticeable. The trade-off is that changes to those objects won't be saved, and if the same row appears twice I get two separate objects unless I use the identity-resolution variant. For a read-heavy context I sometimes make no-tracking the default and opt into tracking where I update."

Red flag to avoid:

Using AsNoTracking and then being surprised that SaveChanges does nothing, or never having heard of the change tracker.

They may ask next:
  • An entity arrives from an API call and was never loaded by this context. How do you update it?
  • Why can a long-lived DbContext get slower over time?
Say it in 60 seconds
Medium Coding round Fresher, Mid-level, Senior Practice question

19. This endpoint runs one query for the orders and then one more for every order. Spot the N+1 problem and fix it.

What the interviewer is really testing:
Whether you recognise the most common EF Core performance bug from the code, know how to confirm it, and can choose between Include and projection.
Answer frame:

Spot it: a loop over parents that touches a navigation or runs a query per item.

Confirm: turn on SQL logging and count the queries for one call.

Fix: Include to load related rows up front, or better, project only the fields you need.

Sample spoken answer:

"The problem is the loop. The first query loads the orders, and then touching the lines of each order, through lazy loading here, sends another query per order. Fifty orders means fifty-one round trips, and it gets worse as the customer grows. First I'd prove it: turn on EF's SQL logging in a dev environment and count the queries for one request. Then I'd fix it one of two ways. Include loads the orders and their lines in one query, which is fine if I actually need the full entities. But here I only need a total per order, so the best fix is a projection with Select. EF translates the sum into SQL, the database does the math, and only the ID and total come back. I'd reach for projection by default on read endpoints, because it moves less data and avoids tracking."

Code:
// Before: one query, then one more per order when Lines is touched (lazy loading on)
var orders = await db.Orders.Where(o => o.CustomerId == customerId).ToListAsync();
foreach (var order in orders)
    totals[order.Id] = order.Lines.Sum(l => l.UnitPrice * l.Quantity);

// Fix 1: load the lines in the same query
var ordersWithLines = await db.Orders
    .Include(o => o.Lines)
    .Where(o => o.CustomerId == customerId)
    .ToListAsync();

// Fix 2: let the database add it up and return only what is needed
var orderTotals = await db.Orders
    .Where(o => o.CustomerId == customerId)
    .Select(o => new { o.Id, Total = o.Lines.Sum(l => l.UnitPrice * l.Quantity) })
    .ToListAsync();
Red flag to avoid:

Suggesting caching the whole table or adding more database capacity without removing the extra queries.

They may ask next:
  • Without lazy loading turned on, what would the original loop have done instead?
  • How would you catch an N+1 like this before it reaches production?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

20. Compare eager, lazy and explicit loading in EF Core. Which do you use by default, and what is a split query for?

What the interviewer is really testing:
Whether you control when related data is fetched, and know the costs hidden in each approach, including row explosion from several collection includes.
Answer frame:

Eager: Include and ThenInclude load related data in the same query.

Lazy: proxies and virtual navigations load data on first access; easy, but hides queries.

Explicit and split: load a navigation on demand with Entry; AsSplitQuery avoids huge joined result sets.

Sample spoken answer:

"Eager loading means I say up front what I need with Include and ThenInclude, and EF fetches it in the same query. Lazy loading means the related data loads the first time I touch the navigation property. The usual setup needs the proxies package and virtual navigations, and it's where most N+1 problems come from, because the extra queries are invisible in the code. Explicit loading is in between: I load the parent, then call Entry, Collection and LoadAsync when I decide I need it. My default is no lazy loading, eager loading only where I really need entities, and projection for reads. A split query matters when I include two or more collections: a single SQL join multiplies the rows, so the result set can explode. AsSplitQuery sends one query per collection instead. It's faster in that case, but the queries aren't one snapshot, so data could change between them."

Red flag to avoid:

Turning lazy loading on everywhere for convenience, or not knowing that it issues a query each time a navigation is first touched.

They may ask next:
  • What happens if lazy loading fires after the DbContext has been disposed?
  • Why can lazy loading cause trouble during JSON serialization of an entity?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

21. How do you create EF Core migrations and roll them out safely to a production database that is serving live traffic?

What the interviewer is really testing:
Whether you treat schema changes as a deployment step with review and a plan for running code, not as something the app does to itself on startup.
Answer frame:

Create and review: add the migration, read the generated code and SQL, commit it with the model change.

Apply deliberately: an idempotent SQL script or a migration bundle run by the pipeline, not Migrate at startup on many instances.

Stay compatible: expand then contract, so old and new app versions both work during the rollout.

Sample spoken answer:

"I create a migration with the EF tools after changing the model, and then I actually read it, plus the SQL it generates. EF sometimes turns a rename into a drop and an add, which loses data, so I fix that by hand. For production, I don't let the app call Migrate on startup. With several instances they can race, and the app's database user shouldn't have rights to change the schema anyway. Instead the pipeline generates an idempotent script or a migration bundle and runs it as its own step, with a backup taken first. For breaking changes I use expand and contract. First add the new column and deploy code that writes both. Then backfill. Only in a later release do I drop the old column. That way the old version still running during the rollout never meets a schema it can't handle."

Red flag to avoid:

Running Migrate on every app start in production across several instances, or shipping a generated migration nobody read.

They may ask next:
  • How would you rename a column on a large, busy table without downtime?
  • Why are Down migrations rarely a real rollback plan once new data has been written?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

22. Why is DbContext usually registered as scoped, and what happens if you run two queries on the same context in parallel?

What the interviewer is really testing:
Whether you understand DbContext as a short-lived unit of work that is not thread-safe, and know the safe options when you do need parallel queries.
Answer frame:

Unit of work: one context per request, holding one change tracker and one connection at a time.

Not thread-safe: a second operation before the first finishes throws an InvalidOperationException.

Options: await queries one by one, use IDbContextFactory for separate contexts, pooling to cut setup cost.

Sample spoken answer:

"A DbContext is meant to be a short unit of work. It holds a change tracker and uses one connection at a time, so it fits one request, which is exactly what scoped gives me. It isn't thread-safe. If I start two queries on the same context and await them together with Task.WhenAll, EF throws an InvalidOperationException saying a second operation started before the previous one completed. That's not a bug in EF; it's protecting the context from corruption. Usually the fix is simply to await the queries one after another. If I really need parallel queries, for example to build a dashboard faster, I inject IDbContextFactory and create a separate context for each, disposing each one when done. If creating contexts shows up as a cost under heavy load, DbContext pooling reuses instances and resets their state between uses."

Red flag to avoid:

Registering DbContext as a singleton, or wrapping it in a lock to allow parallel queries.

They may ask next:
  • What state must you avoid putting on a DbContext if you turn on pooling?
  • How would you use a DbContext safely inside a Blazor Server component or a background loop?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

23. Two admins edit the same product at the same moment and one silently overwrites the other. How do you stop that with EF Core?

What the interviewer is really testing:
Whether you can explain optimistic concurrency end to end: the token, the SQL it changes, the exception and a sensible response to the user.
Answer frame:

Token: a concurrency column, such as a rowversion, marked with Timestamp or configured as a concurrency token.

Check in SQL: the UPDATE includes the original token in the WHERE clause; zero rows means someone else won.

Handle it: catch DbUpdateConcurrencyException, then return 409 or reload, merge and retry.

Sample spoken answer:

"That's a lost update, and the usual fix is optimistic concurrency. I add a concurrency token to the entity. On SQL Server that's a rowversion column marked with the Timestamp attribute, and the database changes it on every update. Other providers have their own options, or I can manage a version number myself. EF then puts the original token value in the WHERE clause of the UPDATE. If another admin saved in between, the token no longer matches, zero rows are affected, and EF throws DbUpdateConcurrencyException. I catch that and decide with the product owner what should happen. For an admin screen I usually return a 409 Conflict and show the latest values so the person can reapply their change. Over HTTP, I send the version as an ETag and expect it back in If-Match, so the check covers the whole round trip, not just the time inside one request."

Code:
public class Product
{
    public int Id { get; set; }
    public decimal Price { get; set; }
    [Timestamp] public byte[] RowVersion { get; set; } = default!;
}

product.Price = request.Price;
try
{
    await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
    return Results.Conflict("This product was changed by someone else. Reload and try again.");
}
Red flag to avoid:

Relying on a transaction alone, which does not stop two read-modify-write requests from overwriting each other.

They may ask next:
  • For a simple stock decrement, why might a single atomic UPDATE be better than a concurrency token?
  • When would you choose pessimistic locking instead?
Say it in 60 seconds
Easy Situational round Fresher, Mid-level, Senior Practice question

24. A teammate proposes turning on EF Core lazy loading for the whole project so nobody has to write Include again. How do you respond?

What the interviewer is really testing:
Whether you can explain a performance trade-off to a colleague with evidence and offer a practical alternative, not just say no.
Answer frame:

Acknowledge: the pain of forgetting Include is real.

The risk: hidden queries, N+1 in loops and during serialization, sync I/O behind a property.

Alternative: projections for reads, Include where entities are needed, a query-count check in tests.

Sample spoken answer:

"I'd start by agreeing the pain is real: forgetting an Include and getting an empty list is annoying. But I'd explain what lazy loading costs us. Every navigation becomes a hidden database call, so a loop over fifty items can quietly become fifty-one queries, and serialising an entity to JSON can wander through navigations and trigger even more. Those lazy loads are also synchronous database calls behind a property, which is bad for a busy API. I'd show it rather than argue: turn on SQL logging, run one list endpoint with lazy loading on, and count the queries together. Then I'd suggest a better fix for the real problem: use projections into response types for reads, so there's nothing to Include, use Include where we really need entities, and add a test that fails if an endpoint runs more queries than expected."

Red flag to avoid:

Agreeing because it saves typing, or refusing without explaining the cost or offering an alternative.

They may ask next:
  • Is there any part of a system where lazy loading would be a reasonable choice?
  • How would you write a test that fails if an endpoint issues too many queries?
Say it in 60 seconds

Performance 3 questions

Hard Technical round Mid-level, Senior Practice question

25. Why can calling .Result or .Wait() on async code make an ASP.NET Core API slow to a crawl under load, even with low CPU?

What the interviewer is really testing:
Whether you can connect blocking calls to thread pool starvation, recognise the symptoms in production and prove it with the right tools.
Answer frame:

Blocking: each blocked request holds a thread pool thread while it waits for I/O.

Starvation: the pool adds threads slowly, so work queues up; latency climbs while CPU stays low.

Prove and fix: thread pool counters and thread dumps show it; async all the way is the real fix.

Sample spoken answer:

"ASP.NET Core doesn't have the synchronization context old ASP.NET had, so .Result usually won't deadlock the classic way. The damage is different. Each request that blocks holds a thread pool thread doing nothing while it waits for the database or an HTTP call. Under load, all the threads end up blocked, and new requests queue up. The pool does add threads, but slowly on purpose, so latency climbs and timeouts appear while CPU looks almost idle. That's the signature. To prove it I'd watch the thread pool counters with dotnet-counters, especially the queue length and thread count, and take a dump or trace to see many threads parked in Wait or Result. The fix is async all the way: make the calling methods async and await. Raising the minimum thread count can buy time in an emergency, but it hides the problem instead of fixing it."

Red flag to avoid:

Saying the fix is to increase the thread pool size, or that blocking is harmless because nothing deadlocked.

They may ask next:
  • What would you do if the blocking call is inside a third-party library that only has a sync API?
  • Why does wrapping the call in Task.Run inside a request not help throughput?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level, Senior Practice question

26. Why is creating a new HttpClient for every outgoing call a problem in a busy API, and what do you use instead?

What the interviewer is really testing:
Whether you know the socket and DNS pitfalls of HttpClient and how IHttpClientFactory, typed clients and handlers solve them.
Answer frame:

New per call: each one opens connections that linger after disposal; under load you run out of sockets.

One static client: reuses connections but can hold on to stale DNS results unless configured.

Factory: IHttpClientFactory pools and recycles handlers, and adds named or typed clients and delegating handlers.

Sample spoken answer:

"HttpClient looks disposable, so people wrap it in a using block per call. The trouble is that each new client opens its own connections, and after disposal those sockets linger for a while in the operating system. Under real traffic the server can run out of sockets and outbound calls start failing. The opposite mistake is one static client forever: connections get reused, but it can keep talking to an old IP after a DNS change unless the connection lifetime is configured. In ASP.NET Core I use IHttpClientFactory. I register a typed client with its base address and timeout, and the factory pools and recycles the underlying handlers, so I get reuse without stale DNS. It also lets me add delegating handlers for things like attaching a token, logging and retry or circuit breaker policies, all configured in one place."

Red flag to avoid:

Wrapping a new HttpClient in using for every request, or adding retries to non-idempotent calls such as payments without a key.

They may ask next:
  • Why is holding a typed client inside a singleton service a mistake?
  • Which calls would you retry automatically, and which would you never retry?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

27. Tell me about a slow ASP.NET Core endpoint you made faster. How did you find where the time was actually going?

What the interviewer is really testing:
Whether you measure before changing code, can read the evidence across app and database, and can show the result in numbers.
Answer frame:

Symptom: which endpoint, how slow, who noticed and how it was measured.

Evidence: traces, SQL logs, query plans, profiler output; what they pointed to.

Fix and proof: the change, the before and after numbers, and what stops it coming back.

Sample spoken answer:

"At my last company the order history endpoint had become slow for our biggest customers, a few seconds at the high end, while small accounts were fine. That pattern suggested work growing with data. I took a trace of one slow request and turned on EF's SQL logging in staging, and one call was sending a couple of hundred queries: the classic N+1, from lazy loading inside a mapping step. There was also a missing index on the customer ID column. I replaced the mapping with a projection that selected just the fields the page showed, which cut it to one query, and added the index after checking the query plan. The slow end dropped to well under half a second. Then I added a test that counts queries for that endpoint and turned lazy loading off, so the same mistake couldn't creep back quietly."

Red flag to avoid:

A story where the fix was a guess, such as adding caching or a bigger server, with no measurement before or after.

They may ask next:
  • How did you make sure the index did not hurt write performance on that table?
  • What would you have done if the database was not the bottleneck?
Say it in 60 seconds

Background Services 2 questions

Medium Coding round Mid-level, Senior Practice question

28. Write a BackgroundService that wakes every ten seconds and processes pending records from the database with EF Core.

What the interviewer is really testing:
Whether you handle the lifetime mismatch between a singleton hosted service and a scoped DbContext, respect shutdown and keep one bad run from killing the worker.
Answer frame:

Scope per run: hosted services are singletons, so create a scope each run and resolve the DbContext from it.

Shutdown: pass the stopping token everywhere so the loop ends cleanly when the host stops.

Resilience: catch and log per run; an unhandled exception stops the host by default in recent versions.

Sample spoken answer:

"A hosted service is registered as a singleton, so I can't inject a DbContext into it directly. That would capture a scoped service. Instead I inject IServiceScopeFactory. In ExecuteAsync I use a PeriodicTimer for the ten-second tick, and on each tick I create an async scope, resolve the DbContext from it, do the work, and let the scope dispose everything at the end of the run. I pass the stopping token into every async call, so when the app shuts down the loop ends promptly instead of being cut off halfway. I wrap each run in a try and catch that logs and moves on, because in recent versions an unhandled exception in a background service stops the whole host by default. Finally I register it with AddHostedService. If the work must never be lost, I'd also make each item idempotent."

Code:
public sealed class OutboxWorker(IServiceScopeFactory scopes, ILogger<OutboxWorker> logger)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
        while (await timer.WaitForNextTickAsync(stoppingToken))
        {
            try
            {
                await using var scope = scopes.CreateAsyncScope();
                var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                int sent = await SendPendingAsync(db, stoppingToken); // your own query and updates
                logger.LogInformation("Outbox run sent {Count} messages", sent);
            }
            catch (Exception ex) when (ex is not OperationCanceledException)
            {
                logger.LogError(ex, "Outbox run failed");
            }
        }
    }
}
// Program.cs: builder.Services.AddHostedService<OutboxWorker>();
Red flag to avoid:

Injecting the DbContext straight into the hosted service, or ignoring the cancellation token so shutdown hangs.

They may ask next:
  • What is the difference between implementing IHostedService directly and deriving from BackgroundService?
  • Why should StartAsync never run a long blocking loop?
Say it in 60 seconds
Hard Situational round Mid-level, Senior Practice question

29. You are asked to add a nightly report job to the API as a BackgroundService. The API runs on four instances. What is your response?

What the interviewer is really testing:
Whether you spot what changes when an in-process job meets scale-out and deploys, and can propose a reliable design without over-engineering.
Answer frame:

Spot the problem: four instances means four runs; a deploy or restart can kill a run halfway.

Options: a separate worker or scheduled job that runs once, or a lock or lease so only one instance runs it.

Make it safe: idempotent work, a record of each run, alerts when a run fails or never starts.

Sample spoken answer:

"My first answer would be that a simple BackgroundService would run four times a night, once per instance, so customers could get four reports. A deploy or a scale-in could also stop a run halfway, and nobody would know. So I'd suggest moving it out of the API. Either a small worker service that runs as a single instance, or a scheduled job on whatever platform we already use, which triggers one run at the right time. If it really has to live in the API, I'd use a scheduler library with a shared database store, or take a lease in the database so only one instance does the work. Either way I'd make the job idempotent, store a record of each run with its status, and alert if a run fails or doesn't happen. That keeps it simple but trustworthy."

Red flag to avoid:

Adding the BackgroundService as asked without noticing it will run once per instance.

They may ask next:
  • How would you build a simple database lease so only one instance runs the job?
  • The report takes an hour and a deploy starts halfway through. What should happen?
Say it in 60 seconds

Testing 1 questions

Medium Technical round Mid-level, Senior Practice question

30. How do you write integration tests for an ASP.NET Core API that run the real pipeline without deploying it anywhere?

What the interviewer is really testing:
Whether you test routing, middleware, DI and serialization together, can swap dependencies for tests, and choose a realistic database for them.
Answer frame:

Host in memory: WebApplicationFactory starts the app on a test server and gives you an HttpClient.

Swap services: ConfigureTestServices replaces things like the clock, email sender or auth.

Real database: the same engine in a container beats the in-memory provider, which behaves differently.

Sample spoken answer:

"I use WebApplicationFactory from the MVC testing package. It boots my real Program, with the same middleware, routing and DI, on an in-memory test server, and gives me an HttpClient to call it. So a test sends a real HTTP request and checks the status code and JSON body, which catches wiring mistakes that unit tests never see. With the minimal hosting model I may need to make the Program class visible to the test project. To control dependencies, I use WithWebHostBuilder and ConfigureTestServices to replace, say, the clock or the email sender with fakes, and I add a test authentication handler so I can act as different users. For the database, I avoid EF's in-memory provider for these tests because it doesn't enforce constraints or behave like SQL. I run the real engine in a throwaway container instead, and reset data between tests."

Code:
public class OrdersApiTests(WebApplicationFactory<Program> factory)
    : IClassFixture<WebApplicationFactory<Program>>
{
    [Fact]
    public async Task Unknown_order_returns_404()
    {
        var client = factory.WithWebHostBuilder(b =>
            b.ConfigureTestServices(s => s.AddSingleton<IClock, FixedClock>()))
            .CreateClient();

        var response = await client.GetAsync("/orders/999999");

        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }
}
Red flag to avoid:

Only testing controllers by calling their methods directly, or trusting the EF in-memory provider to catch SQL and constraint bugs.

They may ask next:
  • How would you test an endpoint that requires an authenticated user with a specific role?
  • What do you unit test, and what do you leave to these integration tests?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

The questions above are the prep. The call has ten more.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.

Download ClapAssist with 10 free minutes
Mac and Windows · Stays out of screen share · No card