TECH

The Most Impactful .net core interview questions

|

Sep 5, 2025

The Most Impactful .net core interview questions
The Most Impactful .net core interview questions

Key Takeaways

.NET Core is critical for modern, cross-platform development, running on Windows, Linux, and macOS, and widely adopted by 90%+ of Fortune 500 companies.

Demand is skyrocketing: .NET developer roles are projected to grow 22% through 2029, with salaries between $83K–$142K.

Core skills include advanced C#, ASP.NET Core APIs, Entity Framework Core, dependency injection, and testing frameworks, making them must-check areas in interviews.

Problem-solving matters more than resume buzzwords: debugging, performance optimization, security, and deployment troubleshooting reveal the true experts.

Interview questions should test hands-on skills like DI lifetimes, middleware, async/await, caching, and logging, not just theory.

Strong developers understand advanced patterns like CQRS, event sourcing, SignalR, distributed caching, and microservices resilience strategies.

Why .NET Core Skills Matter Today

Asking the key .net core interview questions is critical to hire the right .NET Core developer, since it can make or break your engineering team's productivity. 


With .NET technologies adopted by over 90% of Fortune 500 companies and developer demand growing by 22% through 2029, finding skilled professionals has become increasingly competitive.


According to Randstad USA's 2024 report, .NET developers topped the list of most in-demand tech jobs, with salaries ranging from $83,000 to $142,000.


Yet most engineering leaders struggle with a fundamental problem: how do you identify developers who can actually deliver, not just interview well?


The challenge isn't finding candidates with .NET Core on their resume. It's separating developers who can solve real problems from those who simply memorized documentation.


This guide provides you with interview questions that reveal genuine problem-solving ability and hands-on experience.

What Does a .NET Core Developer Do and Key Skills They Need to Have

A skilled .NET Core developer builds cross-platform applications using Microsoft's open-source framework. They design APIs, implement business logic, manage data persistence, and ensure applications scale efficiently across Windows, Linux, and macOS environments.


Core Technical Skills:

  • C# Programming: Advanced understanding of async/await, LINQ, generics, and memory management

  • ASP.NET Core: Building RESTful APIs, middleware development, and request pipeline optimization

  • Entity Framework Core: Database operations, migrations, and performance tuning

  • Dependency Injection: Service lifetimes, custom containers, and IoC patterns

  • Testing: Unit testing with xUnit/NUnit, integration testing, and mocking frameworks


Essential Problem-Solving Abilities:

  • Debugging complex multi-threaded applications

  • Optimizing database queries and API performance

  • Implementing security best practices (authentication, authorization, HTTPS)

  • Managing configuration across development, staging, and production environments

  • Troubleshooting deployment issues and monitoring application health

Did you know?

.NET Core was born because Microsoft wanted .NET to run on Linux and macOS, not just Windows.

Looking to hire a .NET Core developer who can do more than just quote documentation?

Utkrusht helps you identify developers who can build scalable APIs, optimize performance, and solve real-world problems. Get started today and skip the resume roulette.

20 Basic .NET Core Interview Questions with Answers

1. What is .NET Core and how does it differ from .NET Framework?

.NET Core is a cross-platform, open-source framework for building modern applications. Unlike .NET Framework (Windows-only), .NET Core runs on Windows, macOS, and Linux. It's modular, faster, and designed for cloud-native development.


What an ideal candidate should discuss: They should mention performance improvements, containerization support, and the unified .NET platform (now just called .NET).

2. Explain the difference between AddTransient, AddScoped, and AddSingleton in dependency injection.

  • AddTransient: Creates a new instance every time it's requested

  • AddScoped: Creates one instance per HTTP request scope

  • AddSingleton: Creates one instance for the application lifetime


What an ideal candidate should discuss: Real-world examples of when to use each lifetime, memory implications, and thread safety considerations.

3. What is middleware in ASP.NET Core?

Middleware are components that form a pipeline to handle HTTP requests and responses. Each middleware can process the request, pass it to the next component, or terminate the pipeline.

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    
    public async Task InvokeAsync(HttpContext context)
    {
        // Process request
        await _next(context);
        // Process response
    }
}

What an ideal candidate should discuss: Middleware order importance, creating custom middleware, and common built-in middleware components.

4. How do you handle configuration in .NET Core?

.NET Core uses the IConfiguration interface to access settings from multiple sources like appsettings.json, environment variables, and command-line arguments. Configuration is injected via dependency injection.

public class MyService
{
    private readonly IConfiguration _config;
    
    public MyService(IConfiguration config)
    {
        _config = config;
        var connectionString = _config.GetConnectionString("DefaultConnection");
    }
}

What an ideal candidate should discuss: Environment-specific configurations, secrets management, and the Options pattern for strongly-typed configuration.

5. What is the difference between IActionResult and ActionResult<T>?

IActionResult is a non-generic interface for action results. ActionResult<T> is a generic wrapper that can return either a specific type T or an IActionResult, providing better type safety and documentation.


What an ideal candidate should discuss: When to use each approach, OpenAPI documentation benefits, and implicit conversion features.

6. Explain async/await in .NET Core.

async/await enables asynchronous programming without blocking threads. The async keyword marks a method as asynchronous, while await suspends execution until the awaited task completes, freeing the thread for other work.

public async Task<string> GetDataAsync()
{
    using var client = new HttpClient();
    return await client.GetStringAsync("https://api.example.com");
}

What an ideal candidate should discuss: Thread pool management, avoiding deadlocks with ConfigureAwait(false), and performance benefits in I/O-bound operations.

7. What is Entity Framework Core?

Entity Framework Core is an object-relational mapping (ORM) framework that allows developers to work with databases using .NET objects instead of writing SQL queries directly.


What an ideal candidate should discuss: Code-first vs database-first approaches, migrations, change tracking, and performance considerations.

8. How do you implement logging in .NET Core?

.NET Core has built-in logging through ILogger interface. You inject ILogger<T> into classes and use methods like LogInformation, LogError, etc.

public class MyController : ControllerBase
{
    private readonly ILogger<MyController> _logger;
    
    public MyController(ILogger<MyController> logger)
    {
        _logger = logger;
    }
    
    public IActionResult Get()
    {
        _logger.LogInformation("Processing GET request");
        return Ok();
    }
}

What an ideal candidate should discuss: Log levels, structured logging, and third-party providers like Serilog.

9. What is Kestrel and when would you use it?

Kestrel is a cross-platform web server included with ASP.NET Core. It can run standalone or behind a reverse proxy like IIS or Nginx for additional features like load balancing and SSL termination.


What an ideal candidate should discuss: Performance characteristics, when to use reverse proxies, and configuration options.

10. Explain model validation in ASP.NET Core.

Model validation ensures incoming data meets specified rules using data annotations or custom validators. ASP.NET Core automatically validates models and populates ModelState.

public class UserModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; }
    
    [Range(18, 100)]
    public int Age { get; set; }
}
[HttpPost]
public IActionResult Create(UserModel model)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);
    
    // Process valid model
    return Ok();
}

What an ideal candidate should discuss: Custom validation attributes, client-side validation, and validation in APIs vs MVC.

11. What are filters in ASP.NET Core?

Filters allow you to run code before or after certain stages in the request pipeline. Types include Authorization, Action, Exception, and Result filters.


What an ideal candidate should discuss: Filter execution order, creating custom filters, and applying filters globally vs per action.

12. How do you handle exceptions in .NET Core applications?

Use try-catch blocks for specific scenarios and global exception handling middleware for unhandled exceptions. You can create custom middleware or use built-in exception handling.

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        context.Response.StatusCode = 500;
        await context.Response.WriteAsync("An error occurred");
    });
});

What an ideal candidate should discuss: Different strategies for development vs production, logging exceptions, and returning appropriate error responses.

13. What is the difference between Task.Run and Task.Factory.StartNew?

Task.Run is simpler and preferred for most scenarios, directly queuing work to the thread pool. Task.Factory.StartNew offers more control but requires careful configuration to avoid pitfalls.


What an ideal candidate should discuss: Default task scheduler differences, TaskCreationOptions, and when each is appropriate.

14. Explain CORS in ASP.NET Core.

CORS (Cross-Origin Resource Sharing) allows controlled access to resources from different domains. ASP.NET Core provides built-in CORS support through policies.

services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigin",
        builder => builder.WithOrigins("https://example.com")
                         .AllowAnyMethod()
                         .AllowAnyHeader());
});
app.UseCors("AllowSpecificOrigin");

What an ideal candidate should discuss: Security implications, preflight requests, and different CORS policy configurations.

15. What is the purpose of Program.cs in .NET Core?

Program.cs contains the application's entry point and configures the host. In .NET 6+, it uses top-level statements and minimal hosting model for simplified configuration.


What an ideal candidate should discuss: Host builder configuration, service registration, and middleware pipeline setup.

16. How do you implement caching in .NET Core?

.NET Core supports in-memory caching via IMemoryCache and distributed caching via IDistributedCache for scenarios like Redis or SQL Server caching.

public class CacheService
{
    private readonly IMemoryCache _cache;
    
    public string GetOrSetCache(string key, Func<string> getItem)
    {
        if (!_cache.TryGetValue(key, out string cachedValue))
        {
            cachedValue = getItem()

What an ideal candidate should discuss: Cache expiration strategies, distributed caching for microservices, and cache invalidation patterns.

17. What are ViewComponents in ASP.NET Core?

ViewComponents are reusable UI components that encapsulate rendering logic and data retrieval. They're similar to partial views but can contain complex logic and are invoked from views.


What an ideal candidate should discuss: Differences from partial views, use cases for complex UI components, and ViewComponent lifecycle.

18. Explain routing in ASP.NET Core.

Routing maps incoming HTTP requests to controller actions or endpoints. ASP.NET Core supports both convention-based routing and attribute routing.

// Convention-based
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
// Attribute routing
[Route("api/[controller]")]
[HttpGet("{id:int}")]
public IActionResult Get(int id) => Ok();

What an ideal candidate should discuss: Route constraints, route precedence, and endpoint routing benefits.

19. What is the difference between IHostedService and BackgroundService?

IHostedService is an interface for long-running background tasks. BackgroundService is an abstract base class that implements IHostedService and simplifies creating background services.


What an ideal candidate should discuss: Hosted service lifecycle, cancellation token handling, and use cases for background processing.

20. How do you test .NET Core applications?

Use testing frameworks like xUnit, NUnit, or MSTest for unit tests. ASP.NET Core provides TestServer for integration testing and supports mocking frameworks like Moq.

public void CalculateSum_ReturnsCorrectValue()
{
    // Arrange
    var calculator = new Calculator();
    
    // Act
    var result = calculator.Add(2, 3);
    
    // Assert
    Assert.Equal(5, result);
}

What an ideal candidate should discuss: Test isolation, mocking dependencies, and integration testing strategies.

Did you know?

Kestrel web server in ASP.NET Core is so fast it broke benchmarks when it was first released.

20 Intermediate .NET Core Interview Questions with Answers

21. Explain the Options pattern in .NET Core.

The Options pattern provides strongly-typed configuration binding, allowing you to bind configuration sections to classes and inject them using IOptions<T>, IOptionsSnapshot<T>, or IOptionsMonitor<T>.

public class DatabaseOptions
{
    public string ConnectionString { get; set; }
    public int TimeoutSeconds { get; set; }
}
// Registration
services.Configure<DatabaseOptions>(configuration.GetSection("Database"));
// Usage
public class DataService
{
    private readonly DatabaseOptions _options;
    
    public DataService(IOptions<DatabaseOptions> options)
    {
        _options = options.Value

What an ideal candidate should discuss: Differences between IOptions interfaces, configuration reloading, and validation with IValidateOptions.

22. How does garbage collection work in .NET Core?

.NET Core uses a generational garbage collector with three generations (0, 1, 2) plus a Large Object Heap (LOH). Objects start in Gen 0 and are promoted to higher generations if they survive collections.


What an ideal candidate should discuss: GC tuning options, memory pressure, and how to minimize allocations for better performance.

23. Explain JWT authentication in ASP.NET Core.

JWT (JSON Web Token) authentication involves validating tokens containing claims about the user. ASP.NET Core provides built-in JWT Bearer authentication middleware.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "your-issuer",
            ValidAudience = "your-audience",
            IssuerSigningKey = new SymmetricSecurityKey(key)
        };
    });

What an ideal candidate should discuss: Token validation parameters, refresh tokens, and security best practices for JWT handling.

24. What is the difference between synchronous and asynchronous programming in .NET Core?

Synchronous programming blocks the calling thread until the operation completes. Asynchronous programming allows the thread to handle other work while waiting for I/O operations to complete.


What an ideal candidate should discuss: When to use async/await, thread pool considerations, and avoiding async in CPU-bound operations.

25. How do you implement custom authentication in ASP.NET Core?

Create a custom authentication handler by implementing AuthenticationHandler<T> and registering it with the authentication system.

public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public CustomAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock) { }
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // Custom authentication logic
        var token = Request.Headers["Authorization"].FirstOrDefault();
        
        if (string.IsNullOrEmpty(token))
            return AuthenticateResult.Fail("Missing token");
            
        // Validate token and create claims
        var claims = new[] { new Claim(ClaimTypes.Name, "user") };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        
        return AuthenticateResult.Success(ticket);
    }
}

What an ideal candidate should discuss: Authentication vs authorization, claims-based identity, and integrating with external providers.

26. Explain the Repository pattern in .NET Core.

The Repository pattern abstracts data access logic, providing a consistent interface for data operations regardless of the underlying storage mechanism.

public interface IUserRepository
{
    Task<User> GetByIdAsync(int id);
    Task<IEnumerable<User>> GetAllAsync();
    Task AddAsync(User user);
    Task UpdateAsync(User user);
    Task DeleteAsync(int id);
}
public class UserRepository : IUserRepository
{
    private readonly ApplicationDbContext _context;
    
    public UserRepository(ApplicationDbContext context)
    {
        _context = context;
    }
    
    public async Task<User> GetByIdAsync(int id)
    {
        return await _context.Users.FindAsync(id);
    }
    
    // Other implementations...
}

What an ideal candidate should discuss: Unit of Work pattern, generic repositories, and when Repository pattern adds value vs direct EF Core usage.

27. How do you handle database migrations in Entity Framework Core?

EF Core migrations allow you to evolve your database schema over time. Use dotnet ef migrations add to create migrations and dotnet ef database update to apply them.

// Creating a migration
dotnet ef migrations add InitialCreate
// Applying migrations
dotnet ef database update
// Migration class example
public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Users",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Users", x => x.Id);
            });
    }
}

What an ideal candidate should discuss: Production migration strategies, data seeding, and handling breaking changes.

28. What is SignalR and how do you use it in .NET Core?

SignalR enables real-time web functionality, allowing server-side code to push content to clients instantly. It automatically chooses the best transport method (WebSockets, Server-Sent Events, Long Polling).

// Hub
public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
// Startup configuration
services.AddSignalR();
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("/chathub");
});

What an ideal candidate should discuss: Connection management, scaling with Redis backplane, and different client types.

29. Explain health checks in ASP.NET Core.

Health checks provide a way to monitor application and dependency health. They return status information that can be consumed by monitoring systems.

// Registration
services.AddHealthChecks()
    .AddDbContext<ApplicationDbContext>()
    .AddRedis(connectionString)
    .AddCheck<CustomHealthCheck>("custom");
// Custom health check
public class CustomHealthCheck : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        // Custom health logic
        var isHealthy = await CheckServiceHealthAsync();
        
        return isHealthy 
            ? HealthCheckResult.Healthy("Service is running")
            : HealthCheckResult.Unhealthy("Service is down");
    }
}

What an ideal candidate should discuss: Health check endpoints, custom health checks, and integration with monitoring tools.

30. How do you implement API versioning in ASP.NET Core?

API versioning allows you to maintain multiple API versions simultaneously. Use the Microsoft.AspNetCore.Mvc.Versioning package for implementation.

services.AddApiVersioning(opt =>
{
    opt.DefaultApiVersion = new ApiVersion(1, 0);
    opt.AssumeDefaultVersionWhenUnspecified = true;
    opt.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader("version"),
        new HeaderApiVersionReader("X-Version"),
        new UrlSegmentApiVersionReader()
    );
});
// Controller versioning
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersV1Controller : ControllerBase { }
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersV2Controller : ControllerBase { }

What an ideal candidate should discuss: Versioning strategies, backward compatibility, and deprecation handling.

31. Explain the concept of scoped services in dependency injection.

Scoped services are created once per HTTP request in web applications. The same instance is shared throughout the request's lifetime but new instances are created for each new request.


What an ideal candidate should discuss: Scoped lifetime management, disposal patterns, and avoiding captured dependencies in singletons.

32. How do you implement rate limiting in ASP.NET Core?

Rate limiting controls how many requests a client can make in a given time period. Use middleware or action filters to implement rate limiting.

// Custom rate limiting middleware
public class RateLimitingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly MemoryCache _cache;
    private readonly int _maxRequests = 100;
    private readonly TimeSpan _timeWindow = TimeSpan.FromMinutes(1);
    public async Task InvokeAsync(HttpContext context)
    {
        var clientId = context.Connection.RemoteIpAddress?.ToString();
        var key = $"rate_limit_{clientId}";
        
        if (_cache.TryGetValue(key, out int requestCount))
        {
            if (requestCount >= _maxRequests)
            {
                context.Response.StatusCode = 429;
                await context.Response.WriteAsync("Rate limit exceeded");
                return;
            }
            _cache.Set(key, requestCount + 1, _timeWindow);
        }
        else
        {
            _cache.Set(key, 1, _timeWindow);
        }
        
        await _next(context);
    }
}

What an ideal candidate should discuss: Different rate limiting algorithms, distributed rate limiting, and client identification strategies.

33. What is the difference between IMemoryCache and IDistributedCache?

IMemoryCache stores data in the application's memory on the same server. IDistributedCache allows storing data across multiple servers using providers like Redis or SQL Server.


What an ideal candidate should discuss: When to use each caching approach, serialization requirements for distributed cache, and cache consistency patterns.

34. How do you implement custom model binding in ASP.NET Core?

Custom model binding allows you to control how request data is converted to action method parameters by implementing IModelBinder.

public class CustomModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        
        if (value == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }
        
        // Custom binding logic
        var customModel = ParseCustomFormat(value.FirstValue);
        bindingContext.Result = ModelBindingResult.Successful(customModel);
        
        return Task.CompletedTask;
    }
}
// Registration
services.AddMvc(options =>
{
    options.ModelBinderProviders.Insert(0, new SimpleTypeModelBinderProvider(
        typeof(CustomType), new CustomModelBinder()));
});

What an ideal candidate should discuss: Model binding lifecycle, value providers, and when custom binding is necessary.

35. Explain the concept of minimal APIs in .NET 6+.

Minimal APIs provide a simplified approach to building HTTP APIs with minimal ceremony, reducing boilerplate code for simple scenarios.

var app = WebApplication.Create(args);
app.MapGet("/users", async (IUserService userService) =>
{
    var users = await userService.GetAllAsync();
    return Results.Ok(users);
});
app.MapPost("/users", async (User user, IUserService userService) =>
{
    await userService.CreateAsync(user);
    return Results.Created($"/users/{user.Id}", user);
});
app.Run();

What an ideal candidate should discuss: When to use minimal APIs vs controllers, dependency injection in minimal APIs, and organizing larger minimal API applications.

36. How do you implement custom authorization policies?

Custom authorization policies allow you to define complex authorization requirements beyond simple role or claim checks.

// Custom requirement
public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }
    
    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}
// Authorization handler
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        MinimumAgeRequirement requirement)
    {
        var ageClaim = context.User.FindFirst("age");
        
        if (ageClaim != null && int.TryParse(ageClaim.Value, out int age))
        {
            if (age >= requirement.MinimumAge)
            {
                context.Succeed(requirement);
            }
        }
        
        return Task.CompletedTask;
    }
}
// Registration
services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));
});

services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

What an ideal candidate should discuss: Authorization requirements vs handlers, resource-based authorization, and policy combination strategies.

37. What is the difference between Entity Framework Core and Dapper?

EF Core is a full-featured ORM with change tracking, migrations, and LINQ support. Dapper is a lightweight micro-ORM focused on performance with manual SQL queries.


What an ideal candidate should discuss: Performance trade-offs, development speed vs control, and when to choose each approach.

38. How do you implement request/response logging middleware?

Create middleware to log HTTP requests and responses for monitoring and debugging purposes.

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestResponseLoggingMiddleware> _logger;
    public async Task InvokeAsync(HttpContext context)
    {
        // Log request
        await LogRequest(context);
        
        // Capture response
        var originalBodyStream = context.Response.Body;
        using var responseBody = new MemoryStream();
        context.Response.Body = responseBody;
        
        await _next(context);
        
        // Log response
        await LogResponse(context, responseBody);
        
        // Copy response back
        await responseBody.CopyToAsync(originalBodyStream);
    }
    
    private async Task LogRequest(HttpContext context)
    {
        context.Request.EnableBuffering();
        var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
        context.Request.Body.Position = 0;
        
        _logger.LogInformation("HTTP {Method} {Path} {Body}",
            context.Request.Method,
            context.Request.Path,
            body);
    }
}

What an ideal candidate should discuss: Performance considerations, sensitive data filtering, and structured logging approaches.

39. Explain connection pooling in .NET Core.

Connection pooling reuses database connections to improve performance by avoiding the overhead of creating new connections for each request.


What an ideal candidate should discuss: Pool size configuration, connection lifecycle management, and troubleshooting connection exhaustion.

40. How do you implement background services for scheduled tasks?

Use IHostedService or BackgroundService to implement long-running background tasks and scheduled operations.

public class ScheduledService : BackgroundService
{
    private readonly ILogger<ScheduledService> _logger;
    private readonly IServiceScopeFactory _scopeFactory;
    
    public ScheduledService(ILogger<ScheduledService> logger, IServiceScopeFactory scopeFactory)
    {
        _logger = logger;
        _scopeFactory = scopeFactory;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var scope = _scopeFactory.CreateScope();
            var service = scope.ServiceProvider.GetRequiredService<IMyService>();
            
            try
            {
                await service.DoWorkAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred executing scheduled task");
            }
            
            await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
        }
    }
}

What an ideal candidate should discuss: Service scope management, error handling in background services, and cancellation token usage.

Did you know?

The famous game Among Us runs on Unity, which is powered by C# and .NET.

20 Advanced .NET Core Interview Questions with Answers

41. Explain the internals of async/await and how it affects thread management.

async/await uses state machines generated by the compiler to manage asynchronous operations. When await is encountered, the method's state is captured, the thread is released back to the thread pool, and execution resumes on a thread pool thread when the awaited operation completes.

// Compiler generates a state machine similar to:
public struct AsyncStateMachine : IAsyncStateMachine
{
    public int state;
    public TaskAwaiter<string> awaiter;
    
    public void MoveNext()
    {
        switch (state)
        {
            case 0:
                awaiter = GetDataAsync().GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    state = 1;
                    builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                    return;
                }
                goto case 1;
            case 1:
                var result = awaiter.GetResult();
                // Continue execution...
                break;
        }
    }
}

What an ideal candidate should discuss: SynchronizationContext, ConfigureAwait(false) usage, and ThreadPool starvation scenarios.

42. How do you implement distributed locking in a microservices architecture?

Distributed locking prevents race conditions across multiple service instances. Common implementations use Redis, SQL Server, or specialized coordination services.

public class RedisDistributedLock
{
    private readonly IDatabase _database;
    private readonly string _key;
    private readonly string _value;
    private readonly TimeSpan _expiry;
    
    public async Task<bool> AcquireAsync()
    {
        return await _database.StringSetAsync(_key, _value, _expiry, When.NotExists);
    }
    
    public async Task ReleaseAsync()
    {
        const string script = @"
            if redis.call('GET', KEYS[1]) == ARGV[1] then
                return redis.call('DEL', KEYS[1])
            else
                return 0
            end";
        
        await _database.ScriptEvaluateAsync(script, new RedisKey[] { _key }, new RedisValue[] { _value });
    }
}
// Usage
public class OrderService
{
    public async Task ProcessOrderAsync(int orderId)
    {
        var lockKey = $"order_lock_{orderId}";
        var distributedLock = new RedisDistributedLock(lockKey, TimeSpan.FromMinutes(5));
        
        if (await distributedLock.AcquireAsync())
        {
            try
            {
                // Process order logic
                await ProcessOrderInternal(orderId);
            }
            finally
            {
                await distributedLock.ReleaseAsync();
            }
        }
        else
        {
            throw new InvalidOperationException("Could not acquire lock for order processing");
        }
    }
}

What an ideal candidate should discuss: Lock timeout strategies, deadlock prevention, and alternatives like optimistic concurrency control.

43. Describe memory management optimization techniques in .NET Core.

Memory optimization involves minimizing allocations, understanding garbage collection behavior, and using efficient data structures.

// Use Span<T> to avoid allocations
public void ProcessData(ReadOnlySpan<byte> data)
{
    // Works with stack-allocated or sliced memory without heap allocation
    Span<byte> buffer = stackalloc byte[256];
    data.Slice(0, Math.Min(data.Length, buffer.Length)).CopyTo(buffer);
}
// Object pooling for frequently created objects
public class ObjectPoolService
{
    private readonly ObjectPool<StringBuilder> _stringBuilderPool;
    
    public string BuildString(IEnumerable<string> parts)
    {
        var sb = _stringBuilderPool.Get();
        try
        {
            foreach (var part in parts)
                sb.Append(part);
            return sb.ToString();
        }
        finally
        {
            _stringBuilderPool.Return(sb);
        }
    }
}
// ArrayPool for temporary arrays
public byte[] ProcessLargeData(byte[] input)
{
    var tempArray = ArrayPool<byte>.Shared.Rent(input.Length * 2);
    try
    {
        // Process data using rented array
        return ProcessInternal(input, tempArray);
    }
    finally
    {
        ArrayPool<byte>.Shared.Return(tempArray)

What an ideal candidate should discuss: Large Object Heap (LOH) considerations, GC tuning parameters, and profiling tools for memory analysis.

44. How do you implement event sourcing in .NET Core?

Event sourcing stores application state as a sequence of events rather than current state snapshots.

public abstract class Event
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public DateTime Timestamp { get; init; } = DateTime.UtcNow;
    public string EventType { get; init; }
}
public class OrderCreated : Event
{
    public Guid OrderId { get; init; }
    public decimal Amount { get; init; }
    public string CustomerId { get; init; }
}
public class Order
{
    public Guid Id { get; private set; }
    public decimal Amount { get; private set; }
    public string Status { get; private set; }
    
    private readonly List<Event> _events = new();
    
    public static Order FromEvents(IEnumerable<Event> events)
    {
        var order = new Order();
        foreach (var @event in events)
        {
            order.Apply(@event);
        }
        return order;
    }
    
    private void Apply(Event @event)
    {
        switch (@event)
        {
            case OrderCreated created:
                Id = created.OrderId;
                Amount = created.Amount;
                Status = "Created";
                break;
            case OrderConfirmed confirmed:
                Status = "Confirmed";
                break;
        }
    }
    
    public void CreateOrder(decimal amount, string customerId)
    {
        var @event = new OrderCreated { OrderId = Guid.NewGuid(), Amount = amount, CustomerId = customerId };
        Apply(@event);
        _events.Add(@event);
    }
}
public class EventStore
{
    public async Task SaveEventsAsync(Guid aggregateId, IEnumerable<Event> events)
    {
        // Store events in database
        foreach (var @event in events)
        {
            await StoreEvent(aggregateId, @event);
        }
    }
    
    public async Task<IEnumerable<Event>> GetEventsAsync(Guid aggregateId)
    {
        // Retrieve events from database
        return await LoadEvents(aggregateId);
    }
}

What an ideal candidate should discuss: Event versioning, snapshots for performance, and eventual consistency implications.

45. Explain CQRS (Command Query Responsibility Segregation) implementation.

CQRS separates read and write operations into different models, allowing independent optimization of each side.

// Command side
public class CreateProductCommand
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}
public class CreateProductCommandHandler
{
    private readonly IProductWriteRepository _repository;
    private readonly IEventBus _eventBus;
    
    public async Task<Guid> HandleAsync(CreateProductCommand command)
    {
        var product = new Product(command.Name, command.Price);
        await _repository.SaveAsync(product);
        
        await _eventBus.PublishAsync(new ProductCreated
        {
            ProductId = product.Id,
            Name = product.Name,
            Price = product.Price
        });
        
        return product.Id;
    }
}
// Query side
public class ProductQueryModel
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
    public decimal AverageRating { get; set; }
}
public class GetProductsQuery
{
    public string Category { get; set; }
    public decimal? MinPrice { get; set; }
    public decimal? MaxPrice { get; set; }
}
public class GetProductsQueryHandler
{
    private readonly IProductReadRepository _repository;
    
    public async Task<IEnumerable<ProductQueryModel>> HandleAsync(GetProductsQuery query)
    {
        return await _repository.GetProductsAsync(query.Category, query.MinPrice, query.MaxPrice);
    }
}
// Event handler to update read model
public class ProductCreatedEventHandler
{
    private readonly IProductReadRepository _readRepository;
    
    public async Task HandleAsync(ProductCreated @event)
    {
        var queryModel = new ProductQueryModel
        {
            Id = @event.ProductId,
            Name = @event.Name,
            Price = @event.Price
        };
        
        await _readRepository.SaveQueryModelAsync(queryModel);
    }
}

What an ideal candidate should discuss: Read/write model synchronization, eventual consistency, and when CQRS adds complexity vs value.

46. How do you implement circuit breaker pattern in .NET Core?

Circuit breaker prevents cascading failures by monitoring service calls and failing fast when a service is unavailable.

public class CircuitBreaker
{
    private readonly int _failureThreshold;
    private readonly TimeSpan _timeout;
    private int _failureCount;
    private DateTime _lastFailureTime;
    private CircuitBreakerState _state = CircuitBreakerState.Closed;
    
    public async Task<T> ExecuteAsync<T>(Func<Task<T>> operation)
    {
        if (_state == CircuitBreakerState.Open)
        {
            if (DateTime.UtcNow - _lastFailureTime > _timeout)
            {
                _state = CircuitBreakerState.HalfOpen;
            }
            else
            {
                throw new CircuitBreakerOpenException();
            }
        }
        
        try
        {
            var result = await operation();
            OnSuccess();
            return result;
        }
        catch (Exception)
        {
            OnFailure();
            throw;
        }
    }
    
    private void OnSuccess()
    {
        _failureCount = 0;
        _state = CircuitBreakerState.Closed;
    }
    
    private void OnFailure()
    {
        _failureCount++;
        _lastFailureTime = DateTime.UtcNow;
        
        if (_failureCount >= _failureThreshold)
        {
            _state = CircuitBreakerState.Open;
        }
    }
}
// Using with HttpClient
public class ExternalApiService
{
    private readonly HttpClient _httpClient;
    private readonly CircuitBreaker _circuitBreaker;
    
    public async Task<ApiResponse> GetDataAsync(string endpoint)
    {
        return await _circuitBreaker.ExecuteAsync(async () =>
        {
            var response = await _httpClient.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsStringAsync();
            return JsonSerializer.Deserialize<ApiResponse>(content);
        });
    }
}

What an ideal candidate should discuss: Integration with Polly library, metrics collection, and combining with retry patterns.

47. Describe performance optimization strategies for Entity Framework Core.

EF Core performance optimization involves query optimization, change tracking management, and efficient data loading patterns.

// Query optimization
public class OptimizedRepository
{
    private readonly ApplicationDbContext _context;
    
    // Use AsNoTracking for read-only scenarios
    public async Task<IEnumerable<Product>> GetProductsAsync()
    {
        return await _context.Products
            .AsNoTracking()
            .Where(p => p.IsActive)
            .ToListAsync();
    }
    
    // Projection to avoid loading unnecessary data
    public async Task<IEnumerable<ProductSummary>> GetProductSummariesAsync()
    {
        return await _context.Products
            .AsNoTracking()
            .Select(p => new ProductSummary
            {
                Id = p.Id,
                Name = p.Name,
                Price = p.Price
            })
            .ToListAsync();
    }
    
    // Batch operations
    public async Task BulkUpdatePricesAsync(Dictionary<int, decimal> priceUpdates)
    {
        var products = await _context.Products
            .Where(p => priceUpdates.Keys.Contains(p.Id))
            .ToListAsync();
            
        foreach (var product in products)
        {
            product.Price = priceUpdates[product.Id];
        }
        
        await _context.SaveChangesAsync();
    }
    
    // Split queries for multiple includes
    public async Task<Order> GetOrderWithDetailsAsync(int orderId)
    {
        return await _context.Orders
            .AsSplitQuery()
            .Include(o => o.Items)
            .Include(o => o.Customer)
            .FirstOrDefaultAsync(o => o.Id == orderId);
    }
}
// Connection resilience
services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseSqlServer(connectionString, sqlOptions =>
    {
        sqlOptions.EnableRetryOnFailure(
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(30),
            errorNumbersToAdd: null);
    });
});

What an ideal candidate should discuss: Query compilation caching, connection pooling, and using raw SQL for complex queries.

48. How do you implement multi-tenancy in .NET Core applications?

Multi-tenancy allows a single application instance to serve multiple tenants with isolated data and configurations.

// Tenant identification
public interface ITenantProvider
{
    string GetTenantId();
}
public class HeaderTenantProvider : ITenantProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    
    public string GetTenantId()
    {
        return _httpContextAccessor.HttpContext?.Request.Headers["X-Tenant-Id"].FirstOrDefault();
    }
}
// Tenant-aware DbContext
public class MultiTenantDbContext : DbContext
{
    private readonly ITenantProvider _tenantProvider;
    
    public MultiTenantDbContext(DbContextOptions<MultiTenantDbContext> options, ITenantProvider tenantProvider)
        : base(options)
    {
        _tenantProvider = tenantProvider;
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Global query filter for tenant isolation
        modelBuilder.Entity<Product>().HasQueryFilter(p => p.TenantId == _tenantProvider.GetTenantId());
        modelBuilder.Entity<Order>().HasQueryFilter(o => o.TenantId == _tenantProvider.GetTenantId());
    }
    
    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        var tenantId = _tenantProvider.GetTenantId();
        
        foreach (var entry in ChangeTracker.Entries<ITenantEntity>())
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.TenantId = tenantId;
            }
        }
        
        return await base.SaveChangesAsync(cancellationToken);
    }
}
// Tenant-specific configuration
public class TenantConfigurationService
{
    private readonly IConfiguration _configuration;
    private readonly ITenantProvider _tenantProvider;
    
    public string GetConnectionString()
    {
        var tenantId = _tenantProvider.GetTenantId();
        return _configuration.GetConnectionString($"Tenant_{tenantId}");
    }
    
    public T GetTenantSetting<T>(string key)
    {
        var tenantId = _tenantProvider.GetTenantId();
        var tenantKey = $"Tenants:{tenantId}:{key}";
        return _configuration.GetValue<T>(tenantKey);
    }
}

What an ideal candidate should discuss: Database per tenant vs shared database strategies, tenant data isolation, and configuration management.

49. Explain implementing saga pattern for distributed transactions.

Saga pattern manages distributed transactions by breaking them into a series of local transactions with compensation actions.

d// Saga state machine
public class OrderSaga
{
    public Guid SagaId { get; set; }
    public Guid OrderId { get; set; }
    public SagaState State { get; set; }
    public List<SagaStep> CompletedSteps { get; set; } = new();
    
    public async Task HandleAsync(OrderCreated @event, ISagaContext context)
    {
        OrderId = @event.OrderId;
        State = SagaState.Started;
        
        // Step 1: Reserve inventory
        await context.SendAsync(new ReserveInventory { OrderId = @event.OrderId, ProductId = @event.ProductId, Quantity = @event.Quantity });
    }
    
    public async Task HandleAsync(InventoryReserved @event, ISagaContext context)
    {
        CompletedSteps.Add(new SagaStep { StepName = "ReserveInventory", CompensationCommand = new ReleaseInventory { OrderId = OrderId } });
        
        // Step 2: Process payment
        await context.SendAsync(new ProcessPayment { OrderId = @event.OrderId, Amount = @event.Amount });
    }
    
    public async Task HandleAsync(PaymentProcessed @event, ISagaContext context)
    {
        CompletedSteps.Add(new SagaStep { StepName = "ProcessPayment", CompensationCommand = new RefundPayment { OrderId = OrderId } });
        
        // Step 3: Ship order
        await context.SendAsync(new ShipOrder { OrderId = @event.OrderId });
    }
    
    public async Task HandleAsync(OrderShipped @event, ISagaContext context)
    {
        State = SagaState.Completed;
        await context.PublishAsync(new OrderFulfilled { OrderId = @event.OrderId });
    }
    
    // Compensation handling
    public async Task HandleAsync(PaymentFailed @event, ISagaContext context)
    {
        State = SagaState.Compensating;
        
        // Execute compensation actions in reverse order
        foreach (var step in CompletedSteps.AsEnumerable().Reverse())
        {
            await context.SendAsync(step.CompensationCommand);
        }
        
        State = SagaState.Compensated;
        await context.PublishAsync(new OrderFailed { OrderId = @event.OrderId, Reason = "Payment failed" });
    }
}
// Saga orchestrator
public class SagaOrchestrator
{
    private readonly ISagaRepository _sagaRepository;
    private readonly IMessageBus _messageBus;
    
    public async Task ProcessEventAsync<T>(T @event) where T : class
    {
        var saga = await _sagaRepository.GetBySagaIdAsync(@event.SagaId);
        
        var context = new SagaContext(_messageBus);
        
        // Handle the event based on saga type and current state
        await saga.HandleAsync(@event, context);
        
        await _sagaRepository.SaveAsync(saga);
    }
}

What an ideal candidate should discuss: Orchestration vs choreography approaches, handling compensation failures, and saga timeout management.

50. How do you implement custom middleware for request/response transformation?

Custom middleware can transform requests and responses, modify headers, or implement cross-cutting concerns.

d            context.Request.EnableBuffering();
            
            var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
            context.Request.Body.Position = 0;
            
            // Apply request transformations
            var transformedRequest = TransformRequestJson(requestBody);
            
            var bytes = Encoding.UTF8.GetBytes(transformedRequest);
            context.Request.Body = new MemoryStream(bytes);
        }
    }
    
    private async Task TransformResponseAsync(HttpContext context, MemoryStream newResponseBody, Stream originalResponseBody)
    {
        newResponseBody.Position = 0;
        var responseContent = await new StreamReader(newResponseBody).ReadToEndAsync();
        
        if (context.Response.ContentType?.Contains("application/json") == true)
        {
            // Apply response transformations
            var transformedResponse = TransformResponseJson(responseContent);
            
            // Add custom headers
            context.Response.Headers.Add("X-Transformation-Applied", "true");
            
            var transformedBytes = Encoding.UTF8.GetBytes(transformedResponse);
            context.Response.ContentLength = transformedBytes.Length;
            
            await originalResponseBody.WriteAsync(transformedBytes);
        }
        else
        {
            newResponseBody.Position = 0;
            await newResponseBody.CopyToAsync(originalResponseBody);
        }
    }
    
    private string TransformRequestJson(string json)
    {
        var jsonDocument = JsonDocument.Parse(json);
        var root = jsonDocument.RootElement;
        
        var transformedObject = new Dictionary<string, object>();
        
        foreach (var property in root.EnumerateObject())
        {
            // Apply naming convention transformation
            var transformedKey = ConvertToCamelCase(property.Name);
            transformedObject[transformedKey] = property.Value.GetRawText();
        }
        
        return JsonSerializer.Serialize(transformedObject);
    }
    
    private string TransformResponseJson(string json)
    {
        // Implement response transformation logic
        var response = JsonSerializer.Deserialize<object>(json);
        
        var envelope = new
        {
            data = response,
            timestamp = DateTime.UtcNow,
            version = "1.0"
        };
        
        return JsonSerializer.Serialize(envelope);
    }
}

What an ideal candidate should discuss: Stream handling best practices, memory management, and conditional transformation logic.

51. Describe implementing real-time notifications with SignalR and scalability considerations.

SignalR enables real-time communication but requires careful consideration for scalability across multiple server instances.

// Hub with user management
public class NotificationHub : Hub
{
    private readonly IUserConnectionManager _connectionManager;
    private readonly INotificationService _notificationService;
    
    public override async Task OnConnectedAsync()
    {
        var userId = Context.UserIdentifier;
        await _connectionManager.AddConnectionAsync(userId, Context.ConnectionId);
        
        // Join user-specific group
        await Groups.AddToGroupAsync(Context.ConnectionId, $"user_{userId}");
        
        // Send missed notifications
        var missedNotifications = await _notificationService.GetMissedNotificationsAsync(userId);
        foreach (var notification in missedNotifications)
        {
            await Clients.Caller.SendAsync("ReceiveNotification", notification);
        }
        
        await base.OnConnectedAsync();
    }
    
    public override async Task OnDisconnectedAsync(Exception exception)
    {
        var userId = Context.UserIdentifier;
        await _connectionManager.RemoveConnectionAsync(userId, Context.ConnectionId);
        await base.OnDisconnectedAsync(exception);
    }
    
    public async Task JoinGroup(string groupName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
    }
}
// Scalable notification service
public class ScalableNotificationService
{
    private readonly IHubContext<NotificationHub> _hubContext;
    private readonly IDistributedCache _cache;
    private readonly IMessageBus _messageBus;
    
    public async Task SendNotificationToUserAsync(string userId, Notification notification)
    {
        // Try to send directly if user is connected to this instance
        await _hubContext.Clients.Group($"user_{userId}").SendAsync("ReceiveNotification", notification);
        
        // Also publish to message bus for other instances
        await _messageBus.PublishAsync(new UserNotificationMessage
        {
            UserId = userId,
            Notification = notification
        });
        
        // Store notification for offline users
        await StoreNotificationAsync(userId, notification);
    }
    
    public async Task SendNotificationToGroupAsync(string groupName, Notification notification)
    {
        await _hubContext.Clients.Group(groupName).SendAsync("ReceiveNotification", notification);
        
        // Publish to other instances
        await _messageBus.PublishAsync(new GroupNotificationMessage
        {
            GroupName = groupName,
            Notification = notification
        });
    }
}
// Redis backplane configuration
services.AddSignalR()
    .AddStackExchangeRedis(configuration.GetConnectionString("Redis"));
// Message handler for cross-instance notifications
public class NotificationMessageHandler
{
    private readonly IHubContext<NotificationHub> _hubContext;
    
    public async Task HandleAsync(UserNotificationMessage message)
    {
        await _hubContext.Clients.Group($"user_{message.UserId}")
            .SendAsync("ReceiveNotification", message.Notification);
    }
}
// Connection manager for tracking user connections
public class UserConnectionManager
{
    private readonly IDistributedCache _cache;
    
    public async Task AddConnectionAsync(string userId, string connectionId)
    {
        var connections = await GetConnectionsAsync(userId);
        connections.Add(connectionId);
        await SetConnectionsAsync(userId, connections);
    }
    
    public async Task<bool> IsUserOnlineAsync(string userId)
    {
        var connections = await GetConnectionsAsync(userId);
        return connections.Any();
    }
    
    private async Task<HashSet<string>> GetConnectionsAsync(string userId)
    {
        var connectionsJson = await _cache.GetStringAsync($"user_connections_{userId}");
        return connectionsJson != null 
            ? JsonSerializer.Deserialize<HashSet<string>>(connectionsJson)
            : new HashSet<string>();
    }
}

What an ideal candidate should discuss: Redis backplane configuration, connection state management, and handling connection drops.

52. How do you implement custom authentication schemes in ASP.NET Core?

Custom authentication schemes handle specialized authentication requirements beyond standard JWT or cookie authentication.

// Custom authentication scheme options
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public const string DefaultScheme = "ApiKeyAuthentication";
    public string ApiKeyHeaderName { get; set; } = "X-API-Key";
}
// Authentication handler
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
    private readonly IApiKeyValidator _apiKeyValidator;
    
    public ApiKeyAuthenticationHandler(
        IOptionsMonitor<ApiKeyAuthenticationOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IApiKeyValidator apiKeyValidator)
        : base(options, logger, encoder, clock)
    {
        _apiKeyValidator = apiKeyValidator;
    }
    
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey(Options.ApiKeyHeaderName))
        {
            return AuthenticateResult.Fail("API Key header not found");
        }
        
        var apiKey = Request.Headers[Options.ApiKeyHeaderName].FirstOrDefault();
        
        if (string.IsNullOrEmpty(apiKey))
        {
            return AuthenticateResult.Fail("API Key is empty");
        }
        
        var validationResult = await _apiKeyValidator.ValidateAsync(apiKey);
        
        if (!validationResult.IsValid)
        {
            return AuthenticateResult.Fail("Invalid API Key");
        }
        
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, validationResult.ClientName),
            new Claim(ClaimTypes.NameIdentifier, validationResult.ClientId),
            new Claim("ApiKeyId", validationResult.ApiKeyId)
        };
        
        // Add role claims
        foreach (var role in validationResult.Roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, role));
        }
        
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        
        return AuthenticateResult.Success(ticket);
    }
    
    protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;
        Response.Headers.Add("WWW-Authenticate", $"{Scheme.Name} realm=\"API\"");
        await Response.WriteAsync("API Key authentication required");
    }
    
    protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 403;
        await Response.WriteAsync("Insufficient permissions");
    }
}
// API Key validator service
public interface IApiKeyValidator
{
    Task<ApiKeyValidationResult> ValidateAsync(string apiKey);
}
public class ApiKeyValidator : IApiKeyValidator
{
    private readonly IApiKeyRepository _repository;
    private readonly IMemoryCache _cache;
    
    public async Task<ApiKeyValidationResult> ValidateAsync(string apiKey)
    {
        // Check cache first
        var cacheKey = $"apikey_{apiKey}";
        if (_cache.TryGetValue(cacheKey, out ApiKeyValidationResult cachedResult))
        {
            return cachedResult;
        }
        
        // Validate against database
        var apiKeyEntity = await _repository.GetByKeyAsync(apiKey);
        
        if (apiKeyEntity == null || !apiKeyEntity.IsActive || apiKeyEntity.ExpiresAt < DateTime.UtcNow)
        {
            var invalidResult = new ApiKeyValidationResult { IsValid = false };
            _cache.Set(cacheKey, invalidResult, TimeSpan.FromMinutes(5));
            return invalidResult;
        }
        
        var validResult = new ApiKeyValidationResult
        {
            IsValid = true,
            ClientId = apiKeyEntity.ClientId,
            ClientName = apiKeyEntity.ClientName,
            ApiKeyId = apiKeyEntity.Id,
            Roles = apiKeyEntity.Roles.Split(',')
        };
        
        _cache.Set(cacheKey, validResult, TimeSpan.FromMinutes(30));
        return validResult;
    }
}
// Registration
services.AddAuthentication()
    .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
        ApiKeyAuthenticationOptions.DefaultScheme, 
        options => { });
// Usage
[Authorize(AuthenticationSchemes = ApiKeyAuthenticationOptions.DefaultScheme)]
[ApiController]
public class SecureApiController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        var clientId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return Ok($"Secure data for client: {clientId}");
    }
}

What an ideal candidate should discuss: Authentication vs authorization separation, caching strategies for performance, and combining multiple authentication schemes.

53. Describe implementing retry patterns with exponential backoff using Polly.

Polly provides resilience patterns including retry with exponential backoff to handle transient failures gracefully.

// Advanced retry policy with exponential backoff
public class ResilientHttpService
{
    private readonly HttpClient _httpClient;
    private readonly IAsyncPolicy<HttpResponseMessage> _retryPolicy;
    private readonly ILogger<ResilientHttpService> _logger;
    
    public ResilientHttpService(HttpClient httpClient, ILogger<ResilientHttpService> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
        
        // Retry policy with exponential backoff and jitter
        _retryPolicy = Policy
            .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
            .Or<HttpRequestException>()
            .Or<TaskCanceledException>()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
                    .Add(TimeSpan.FromMilliseconds(Random.Shared.Next(0, 1000))), // Add jitter
                onRetry: (outcome, timespan, retryCount, context) =>
                {
                    _logger.LogWarning("Retry {RetryCount} for {OperationKey} in {Delay}ms",
                    retryCount, context.OperationKey, timespan.TotalMilliseconds);
            });
    }
    
    public async Task<T> GetAsync<T>(string endpoint, CancellationToken cancellationToken = default)
    {
        var context = new Context(endpoint);
        
        var response = await _retryPolicy.ExecuteAsync(async (ctx) =>
        {
            _logger.LogInformation("Attempting HTTP GET to {Endpoint}", endpoint);
            return await _httpClient.GetAsync(endpoint, cancellationToken);
        }, context);
        
        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<T>(content);
    }
}
// Circuit breaker with retry combination
public class ResilientServiceClient
{
    private readonly HttpClient _httpClient;
    private readonly IAsyncPolicy<HttpResponseMessage> _combinedPolicy;
    
    public ResilientServiceClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        
        // Retry policy
        var retryPolicy = Policy
            .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
        
        // Circuit breaker policy
        var circuitBreakerPolicy = Policy
            .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
            .CircuitBreakerAsync(
                handledEventsAllowedBeforeBreaking: 3,
                durationOfBreak: TimeSpan.FromSeconds(30),
                onBreak: (result, timespan) =>
                {
                    Console.WriteLine($"Circuit breaker opened for {timespan}");
                },
                onReset: () =>
                {
                    Console.WriteLine("Circuit breaker reset");
                });
        
        // Timeout policy
        var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
        
        // Combine policies: Timeout -> Retry -> Circuit Breaker
        _combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy, timeoutPolicy);
    }
    
    public async Task<ApiResponse<T>> CallServiceAsync<T>(string endpoint)
    {
        try
        {
            var response = await _combinedPolicy.ExecuteAsync(async () =>
            {
                return await _httpClient.GetAsync(endpoint);
            });
            
            var content = await response.Content.ReadAsStringAsync();
            var data = JsonSerializer.Deserialize<T>(content);
            
            return new ApiResponse<T>
            {
                IsSuccess = true,
                Data = data
            };
        }
        catch (CircuitBreakerOpenException)
        {
            return new ApiResponse<T>
            {
                IsSuccess = false,
                ErrorMessage = "Service temporarily unavailable"
            };
        }
        catch (TimeoutRejectedException)
        {
            return new ApiResponse<T>
            {
                IsSuccess = false,
                ErrorMessage = "Request timeout"
            };
        }
    }
}
// Polly registry for centralized policy management
public static class PolicyRegistry
{
    public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(string policyName)
    {
        return policyName switch
        {
            "aggressive-retry" => Policy
                .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
                .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))),
                
            "gentle-retry" => Policy
                .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
                .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(retryAttempt)),
                
            _ => Policy.NoOpAsync<HttpResponseMessage>()
        };
    }
}
// Dependency injection setup
services.AddHttpClient<ResilientHttpService>()
    .AddPolicyHandler(PolicyRegistry.GetRetryPolicy("aggressive-retry"));

What an ideal candidate should discuss: Policy composition strategies, handling different exception types, and monitoring policy execution metrics.

54. How do you implement distributed caching strategies for microservices?

Distributed caching in microservices requires coordinated cache management, invalidation strategies, and consistency considerations.

public class DistributedCacheService
{
    private readonly IDistributedCache _distributedCache;
    private readonly IMemoryCache _localCache;
    
    public async Task<T> GetAsync<T>(string key, Func<Task<T>> fallback = null)
    {
        // L1: Check local cache first
        if (_localCache.TryGetValue(key, out T localValue))
            return localValue;
        
        // L2: Check distributed cache
        var distributedValue = await _distributedCache.GetStringAsync(key);
        if (distributedValue != null)
        {
            var deserializedValue = JsonSerializer.Deserialize<T>(distributedValue);
            _localCache.Set(key, deserializedValue, TimeSpan.FromMinutes(5));
            return deserializedValue;
        }
        
        // L3: Execute fallback and cache result
        if (fallback != null)
        {
            var fallbackValue = await fallback();
            await SetAsync(key, fallbackValue, TimeSpan.FromMinutes(30));
            return fallbackValue;
        }
        return default(T);
    }
}

What an ideal candidate should discuss: Cache coherence strategies, cache stampede prevention, and performance monitoring for cache hit rates.

55. Explain implementing custom model validation with complex business rules.

Custom validation enables complex business rules that go beyond simple data annotations.

public class BusinessRuleValidationAttribute : ValidationAttribute
{
    private readonly string _businessRuleType;
    
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var businessRuleValidator = validationContext.GetService<IBusinessRuleValidator>();
        var validationResult = businessRuleValidator.Validate(_businessRuleType, value, validationContext.ObjectInstance);
        
        return validationResult.IsValid 
            ? ValidationResult.Success 
            : new ValidationResult(validationResult.ErrorMessage);
    }
}
// Cross-property validation
[OrderValidation]
public class OrderModel
{
    public decimal TotalAmount { get; set; }
    public decimal DiscountAmount { get; set; }
    public ShippingType ShippingType { get; set; }
    public CustomerModel Customer { get; set; }
}

What an ideal candidate should discuss: Performance implications of async validation, validation caching strategies, and separating validation concerns from business logic.

56. How do you implement graceful shutdown and health monitoring in .NET Core?

Graceful shutdown ensures proper resource cleanup and ongoing request completion, while health monitoring provides visibility into application state.

public class GracefulShutdownService : IHostedService
{
    private readonly IHostApplicationLifetime _appLifetime;
    private readonly IBackgroundTaskService _backgroundTaskService;
    
    public Task StartAsync(CancellationToken cancellationToken)
    {
        _appLifetime.ApplicationStopping.Register(OnStopping);
        return Task.CompletedTask;
    }
    
    private void OnStopping()
    {
        // Stop accepting new requests
        _backgroundTaskService?.StopAcceptingNewTasks();
        
        // Wait for current tasks to complete (with timeout)
        var timeout = TimeSpan.FromSeconds(30);
        var waitTask = _backgroundTaskService?.WaitForTasksToComplete();
        waitTask?.Wait(timeout);
    }
}

What an ideal candidate should discuss: Kubernetes health check integration, monitoring tool compatibility, and load balancer health check configuration.

57. Describe implementing comprehensive logging and monitoring with structured logging.

Structured logging provides searchable, analyzable log data for effective monitoring and troubleshooting.

public class StructuredLoggingService
{
    private readonly ILogger<StructuredLoggingService> _logger;
    
    public async Task ProcessOrderAsync(Order order)
    {
        using var scope = _logger.BeginScope("OrderProcessing");
        
        _logger.LogInformation("Processing order {OrderId} for customer {CustomerId} with amount {Amount:C}",
            order.Id, order.CustomerId, order.Amount);
        
        try
        {
            await ProcessOrderInternal(order);
            _logger.LogInformation("Successfully processed order {OrderId}", order.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to process order {OrderId}", order.Id);
            throw;
        }
    }
}

What an ideal candidate should discuss: Log aggregation strategies, correlation IDs for distributed tracing, and integration with monitoring tools like ELK stack or Application Insights.

58. How do you implement custom middleware for cross-cutting concerns?

Custom middleware handles cross-cutting concerns like logging, authentication, and request/response transformation.

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestResponseLoggingMiddleware> _logger;
    
    public async Task InvokeAsync(HttpContext context)
    {
        // Log request
        _logger.LogInformation("HTTP {Method} {Path} started", 
            context.Request.Method, context.Request.Path);
        
        var stopwatch = Stopwatch.StartNew();
        
        try
        {
            await _next(context);
        }
        finally
        {
            stopwatch.Stop();
            _logger.LogInformation("HTTP {Method} {Path} completed in {Duration}ms with status {StatusCode}",
                context.Request.Method, context.Request.Path, stopwatch.ElapsedMilliseconds, context.Response.StatusCode);
        }
    }
}

What an ideal candidate should discuss: Middleware ordering importance, performance considerations, and conditional middleware execution.

59. How do you implement message queuing and event-driven architecture?

Message queuing enables asynchronous communication between services and supports event-driven patterns for scalable architectures.

public class MessagePublisher
{
    private readonly IMessageBus _messageBus;
    
    public async Task PublishOrderCreatedAsync(Order order)
    {
        var orderCreatedEvent = new OrderCreatedEvent
        {
            OrderId = order.Id,
            CustomerId = order.CustomerId,
            Amount = order.Amount,
            Timestamp = DateTime.UtcNow
        };
        
        await _messageBus.PublishAsync("order.created", orderCreatedEvent);
    }
}
// Message handler
public class OrderCreatedHandler : IMessageHandler<OrderCreatedEvent>
{
    private readonly IInventoryService _inventoryService;
    
    public async Task HandleAsync(OrderCreatedEvent message)
    {
        await _inventoryService.ReserveInventoryAsync(message.OrderId);
    }
}

What an ideal candidate should discuss: Message durability, dead letter queues, and handling message ordering and idempotency.

60. Describe implementing custom authorization handlers for complex scenarios.

Custom authorization handlers enable complex business logic for authorization decisions beyond simple role or claim checks.

public class ResourceOwnerRequirement : IAuthorizationRequirement
{
    public string ResourceType { get; }
    public ResourceOwnerRequirement(string resourceType) => ResourceType = resourceType;
}
public class ResourceOwnerHandler : AuthorizationHandler<ResourceOwnerRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IResourceService _resourceService;
    
    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        ResourceOwnerRequirement requirement)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var resourceId = _httpContextAccessor.HttpContext?.Request.RouteValues["id"]?.ToString();
        
        if (await _resourceService.IsOwnerAsync(requirement.ResourceType, resourceId, userId))
        {
            context.Succeed(requirement);
        }
    }
}

What an ideal candidate should discuss: Resource-based authorization, policy combination, and caching authorization results for performance.

Technical Coding Questions with Answers in .NET Core

61. Implement a thread-safe singleton pattern using dependency injection.

Answer:

public interface ISingletonService
{
    string GetInstanceId();
    void ProcessData(string data);
}
public class SingletonService : ISingletonService
{
    private readonly string _instanceId;
    private readonly ConcurrentQueue<string> _dataQueue;
    
    public SingletonService()
    {
        _instanceId = Guid.NewGuid().ToString();
        _dataQueue = new ConcurrentQueue<string>();
    }
    
    public string GetInstanceId() => _instanceId;
    
    public void ProcessData(string data)
    {
        _dataQueue.Enqueue($"{DateTime.UtcNow}: {data}");
    }
}
// Registration
services.AddSingleton<ISingletonService, SingletonService>();

What an ideal candidate should discuss: Thread safety considerations, lazy initialization, and when to use singleton vs scoped services.


62. Create a custom async enumerable for pagination.

Answer:

public static class AsyncEnumerableExtensions
{
    public static async IAsyncEnumerable<T> GetPagedDataAsync<T>(
        this IRepository<T> repository,
        int pageSize = 100,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        int page = 0;
        List<T> batch;
        
        do
        {
            batch = await repository.GetPageAsync(page, pageSize, cancellationToken);
            
            foreach (var item in batch)
            {
                yield return item;
            }
            
            page++;
        } while (batch.Count == pageSize);
    }
}

What an ideal candidate should discuss: IAsyncEnumerable benefits, memory efficiency, and cancellation token propagation.


63. Implement a generic repository with Unit of Work pattern.

Answer:

public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
}
public interface IUnitOfWork : IDisposable
{
    IRepository<T> Repository<T>() where T : class;
    Task<int> SaveChangesAsync();
    Task BeginTransactionAsync();
    Task CommitTransactionAsync();
    Task RollbackTransactionAsync();
}
public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;
    private readonly Dictionary<Type, object> _repositories = new();
    
    public UnitOfWork(ApplicationDbContext context)
    {
        _context = context;
    }
    
    public IRepository<T> Repository<T>() where T : class
    {
        if (_repositories.ContainsKey(typeof(T)))
            return (IRepository<T>)_repositories[typeof(T)];
            
        var repository = new Repository<T>(_context);
        _repositories.Add(typeof(T), repository);
        return repository;
    }
    
    public async Task<int> SaveChangesAsync() => await _context.SaveChangesAsync();
}

What an ideal candidate should discuss: Transaction management, repository caching, and when Unit of Work adds value vs direct DbContext usage.

Did you know?

Stack Overflow—the world’s most visited dev site—is built on .NET.

15 Key Questions with Answers to Ask Freshers and Juniors

64. What is the difference between .NET Core and .NET Framework?

.NET Core is cross-platform and open-source, while .NET Framework is Windows-only. .NET Core is modular, faster, and designed for cloud-native applications.


What an ideal candidate should discuss: Performance improvements, deployment flexibility, and the evolution to unified .NET platform.

65. Explain what dependency injection is and why it's useful.

Dependency injection is a design pattern where objects receive their dependencies from external sources rather than creating them internally. It promotes loose coupling and testability.


What an ideal candidate should discuss: Constructor injection, service lifetimes, and how it enables unit testing with mocks.

66. What are the different service lifetimes in .NET Core DI?

Transient (new instance each time), Scoped (one per request), and Singleton (one for application lifetime).


What an ideal candidate should discuss: When to use each lifetime and memory implications.

67. How do you handle configuration in .NET Core applications?

Use IConfiguration interface to access settings from appsettings.json, environment variables, and other providers.


What an ideal candidate should discuss: Environment-specific configurations and the Options pattern.

68. What is middleware and how does it work?

Middleware components form a pipeline to handle HTTP requests and responses. Each component can process, modify, or terminate the request.


What an ideal candidate should discuss: Middleware order importance and common built-in middleware.

69. Explain the difference between async and await.

async marks a method as asynchronous, await suspends execution until the awaited task completes without blocking the thread.


What an ideal candidate should discuss: Thread pool benefits and avoiding blocking calls.

70. What is Entity Framework Core?

An ORM that allows working with databases using .NET objects instead of writing SQL directly.


What an ideal candidate should discuss: Code-first vs database-first approaches and change tracking.

71. How do you implement logging in .NET Core?

Inject ILogger<T> and use methods like LogInformation, LogError to record events.


What an ideal candidate should discuss: Different log levels and structured logging benefits.

72. What is the purpose of controllers in ASP.NET Core MVC?

Controllers handle HTTP requests, process business logic, and return responses or views to clients.


What an ideal candidate should discuss: Action methods, routing, and separation of concerns.

73. How do you validate models in ASP.NET Core?

Use data annotations like [Required], [Range] and check ModelState.IsValid in controllers.


What an ideal candidate should discuss: Custom validation attributes and client-side validation.

74. What is the difference between IActionResult and specific return types?

IActionResult is flexible for different response types, while specific types provide better type safety and documentation.


What an ideal candidate should discuss: When to use each approach and OpenAPI documentation benefits.

75. Explain routing in ASP.NET Core.

Routing maps incoming requests to controller actions using URL patterns and route parameters.


What an ideal candidate should discuss: Convention-based vs attribute routing.

76. How do you handle exceptions in .NET Core?

Use try-catch blocks for specific scenarios and middleware for global exception handling.


What an ideal candidate should discuss: Different strategies for development vs production environments.

77. What is the purpose of Program.cs?

Contains the application entry point and configures the host, services, and middleware pipeline.


What an ideal candidate should discuss: Host builder configuration and minimal hosting model in .NET 6+.

78. How do you test .NET Core applications?

Use testing frameworks like xUnit with Arrange-Act-Assert pattern and mocking libraries for dependencies.


What an ideal candidate should discuss: Unit tests vs integration tests and test isolation.

Did you know?

.NET’s garbage collector is often called one of the most advanced in the industry, reducing memory headaches for developers.

15 Key Questions with Answers to Ask Seniors and Experienced

79. How do you design microservices architecture with .NET Core?

Design around business capabilities, use API gateways, implement distributed data management, and ensure service independence with proper communication patterns.


What an ideal candidate should discuss: Service boundaries, data consistency patterns, and monitoring strategies.

80. Explain implementing CQRS with Event Sourcing in .NET Core.

Separate read and write models, store events instead of current state, and use event handlers to update read models.


What an ideal candidate should discuss: Event versioning, snapshot strategies, and eventual consistency implications.

81. How do you implement distributed transactions across microservices?

Use saga pattern with orchestration or choreography to manage long-running transactions with compensation actions.


What an ideal candidate should discuss: Two-phase commit limitations, event-driven sagas, and handling partial failures.

82. Describe implementing custom authentication providers.

Create authentication handlers by implementing AuthenticationHandler<T> with custom validation logic and claims creation.


What an ideal candidate should discuss: Token validation, claims transformation, and integration with external identity providers.

83. How do you optimize .NET Core application performance?

Use async/await for I/O operations, implement caching strategies, optimize database queries, and minimize memory allocations.


What an ideal candidate should discuss: Profiling tools, memory management, and specific optimization techniques for high-throughput scenarios.

84. Explain implementing resilience patterns (Circuit Breaker, Retry).

Use libraries like Polly to implement retry with exponential backoff, circuit breakers for failing services, and timeouts for resource protection.


What an ideal candidate should discuss: Policy composition, monitoring circuit breaker state, and fallback strategies.

85. How do you implement multi-tenancy in .NET Core?

Use tenant identification strategies, implement data isolation through query filters or separate databases, and manage tenant-specific configurations.


What an ideal candidate should discuss: Different multi-tenancy approaches, security considerations, and performance implications.

86. Describe implementing event-driven architecture.

Use message buses for asynchronous communication, implement event handlers for domain events, and ensure message durability and ordering.


What an ideal candidate should discuss: Event schema evolution, message deduplication, and handling out-of-order events.

87. How do you implement custom middleware for cross-cutting concerns?

Create middleware classes with InvokeAsync method, handle request/response transformation, and register in the pipeline with proper ordering.


What an ideal candidate should discuss: Middleware performance considerations, conditional execution, and integration with DI container.

88. Explain implementing distributed caching strategies.

Use Redis or SQL Server for distributed cache, implement cache-aside patterns, and handle cache invalidation across service instances.


What an ideal candidate should discuss: Cache coherence, performance vs consistency trade-offs, and cache warming strategies.

89. How do you implement background processing and job scheduling?

Use IHostedService or BackgroundService for long-running tasks, implement queuing mechanisms, and ensure graceful shutdown handling.


What an ideal candidate should discuss: Job persistence, error handling, and scaling background processing.

90. Describe implementing API versioning strategies.

Use URL versioning, header versioning, or query parameter versioning with proper content negotiation and deprecation policies.


What an ideal candidate should discuss: Backward compatibility, client migration strategies, and API documentation approaches.

91. How do you implement health checks and monitoring?

Create custom health checks for dependencies, expose health endpoints for load balancers, and implement comprehensive logging with correlation IDs.


What an ideal candidate should discuss: Health check aggregation, monitoring tool integration, and alerting strategies.

92. Explain implementing custom model binding and validation.

Create custom model binders for complex scenarios and implement business rule validation with async validators.


What an ideal candidate should discuss: Model binding performance, validation caching, and separation of validation concerns.

93. How do you implement real-time communication with SignalR at scale?

Use Redis backplane for scaling across instances, implement connection management, and handle reconnection scenarios gracefully.


What an ideal candidate should discuss: Connection state management, message ordering, and integration with authentication systems.

5 Scenario-based Questions with Answers

94. Your application is experiencing memory leaks. How do you identify and fix them?

Answer: Use memory profilers like dotMemory or PerfView to identify objects not being garbage collected. Common causes include event handlers not being unsubscribed, static collections growing indefinitely, or IDisposable objects not being disposed.

What an ideal candidate should discuss: Profiling techniques, common leak patterns, and prevention strategies.


95. A microservice is occasionally timing out. How do you investigate and resolve this?

Answer: Implement distributed tracing with correlation IDs, add performance counters and metrics, check database query performance, and review resource contention. Consider implementing circuit breakers and retry policies.

What an ideal candidate should discuss: Monitoring tools, performance bottleneck identification, and resilience patterns.


96. Your API needs to handle 10,000 concurrent requests. How do you design for this scale?

Answer: Implement async/await throughout, use connection pooling, implement caching strategies, consider read replicas for databases, use load balancing, and implement rate limiting to protect resources.

What an ideal candidate should discuss: Horizontal scaling, database optimization, and performance testing strategies.


97. You need to migrate a monolithic application to microservices. What's your approach?

Answer: Start with the Strangler Fig pattern, identify bounded contexts, extract services incrementally, implement proper service communication, and ensure data consistency patterns.

What an ideal candidate should discuss: Migration strategies, service boundaries, and handling shared data.


98. A critical business process fails intermittently. How do you ensure reliability?

Answer: Implement comprehensive logging with correlation IDs, add retry mechanisms with exponential backoff, implement circuit breakers, create proper monitoring and alerting, and ensure transaction consistency.

What an ideal candidate should discuss: Error handling strategies, monitoring approaches, and business continuity planning.

Did you know?

You can run .NET apps inside a Raspberry Pi, turning a $35 device into a mini server.

12 Key Questions with Answers Engineering Teams Should Ask

99. How do you handle database schema changes in production?

Use Entity Framework migrations with careful planning, implement blue-green deployments for zero downtime, and always have rollback plans ready.


What an ideal candidate should discuss: Migration strategies, data backup procedures, and coordination with DevOps teams.

100. Describe your approach to code reviews and quality assurance.

Focus on logic correctness, performance implications, security concerns, and maintainability. Use automated tools alongside human review for comprehensive coverage.


What an ideal candidate should discuss: Review checklists, automated quality gates, and fostering learning through reviews.

101. How do you ensure security in .NET Core applications?

Implement proper authentication and authorization, validate all inputs, use HTTPS everywhere, keep dependencies updated, and follow OWASP guidelines.


What an ideal candidate should discuss: Threat modeling, security testing, and incident response procedures.

102. What's your strategy for handling technical debt?

Regularly assess and prioritize technical debt, allocate dedicated time for refactoring, and balance new features with maintenance work.


What an ideal candidate should discuss: Debt measurement techniques, stakeholder communication, and prevention strategies.

103. How do you approach performance monitoring and optimization?

Implement Application Performance Monitoring (APM), set up alerting for key metrics, profile applications regularly, and optimize based on real usage patterns.


What an ideal candidate should discuss: Monitoring tools, performance budgets, and optimization prioritization.

104. Describe your testing strategy for complex applications.

Implement testing pyramid with unit tests, integration tests, and end-to-end tests. Use test doubles appropriately and maintain high test coverage for critical paths.


What an ideal candidate should discuss: Test automation, testing in production, and balancing test types.

105. How do you handle configuration management across environments?

Use environment-specific configuration files, leverage Azure Key Vault or similar for secrets, and implement configuration validation at startup.


What an ideal candidate should discuss: Configuration drift prevention, secret rotation, and environment parity.

106. What's your approach to API design and versioning?

Follow RESTful principles, implement consistent error handling, provide comprehensive documentation, and plan versioning strategy from the start.


What an ideal candidate should discuss: API contracts, backward compatibility, and client migration strategies.

107. How do you ensure code maintainability in large teams?

Establish coding standards, implement automated formatting and linting, create comprehensive documentation, and conduct regular architecture reviews.


What an ideal candidate should discuss: Code organization patterns, knowledge sharing practices, and onboarding procedures.

108. Describe your approach to deployment and DevOps practices.

Implement CI/CD pipelines, use infrastructure as code, implement proper testing gates, and ensure rollback capabilities.


What an ideal candidate should discuss: Deployment strategies, monitoring deployment health, and collaboration with operations teams.

109. How do you handle cross-team collaboration and knowledge sharing?

Establish clear communication channels, document architectural decisions, conduct regular knowledge sharing sessions, and create runbooks for critical processes.


What an ideal candidate should discuss: Documentation strategies, mentoring practices, and conflict resolution.

110. What's your strategy for keeping up with .NET Core evolution?

Follow official Microsoft roadmaps, participate in developer communities, evaluate new features in non-production environments, and plan upgrade strategies.


What an ideal candidate should discuss: Learning approaches, experimentation practices, and technology adoption criteria.

Common Interview Mistakes to Avoid

Engineering leaders often make these critical errors when interviewing .NET Core candidates:


Focusing on Memorization Over Problem-Solving Don't ask candidates to recite syntax or framework details they can easily look up. Instead, present real scenarios that test their ability to architect solutions and debug problems.


Ignoring Hands-On Experience Avoid candidates who can discuss theory but struggle with practical implementation. Ask them to walk through actual projects they've built and challenges they've solved.


Overlooking Communication Skills Technical brilliance means nothing if developers can't explain their solutions to team members or stakeholders. Test their ability to break down complex concepts.


Not Testing Under Pressure Real development involves troubleshooting under deadlines. Present debugging scenarios or ask them to optimize poorly performing code during the interview.


Skipping Architecture Questions Senior developers need to think beyond individual features. Ask about system design, scalability considerations, and trade-off decisions.

5 Best Practices to Conduct Successful .NET Core Interviews

1. Use Progressive Complexity

Start with fundamental concepts and gradually increase difficulty. This reveals the candidate's true skill ceiling and identifies knowledge gaps.


2. Focus on Real-World Scenarios

Present actual problems your team faces rather than abstract coding puzzles. This shows how they'll perform in your specific environment.


3. Evaluate Problem-Solving Process

Pay attention to how candidates approach problems, not just their final answers. Look for systematic thinking and debugging methodologies.


4. Test Both Breadth and Depth

Assess their understanding across different .NET Core areas while diving deep into their claimed specializations.


5. Include Collaborative Elements

Have candidates pair program or whiteboard solutions with team members. This reveals their collaboration skills and teaching ability.

The 80/20 - What Key Aspects You Should Assess During Interviews

Focus your interview time on these critical areas that predict success:


20% Core Technical Skills (80% of Performance Impact)

  • C# fundamentals and async programming

  • ASP.NET Core API development

  • Entity Framework Core usage

  • Dependency injection understanding

  • Error handling and logging


System Design Thinking (15% of Time, High Impact)

  • Scalability considerations

  • Database design decisions

  • Caching strategies

  • Security implementation

  • Performance optimization


Problem-Solving Approach (5% of Time, Critical for Senior Roles)

  • Debugging methodology

  • Architecture decision-making

  • Trade-off evaluation

  • Code quality practices

  • Team collaboration skills

Main Red Flags to Watch Out for

These warning signs indicate candidates who will struggle in production environments:


Technical Red Flags:

  • Cannot explain async/await clearly

  • Unfamiliar with dependency injection concepts

  • Lacks understanding of HTTP status codes

  • Cannot debug simple issues systematically

  • Overcomplicates simple solutions


Communication Red Flags:

  • Cannot explain technical concepts simply

  • Defensive about code quality questions

  • Blames tools or teammates for past issues

  • Avoids taking responsibility for failures

  • Shows no curiosity about learning


Experience Red Flags:

  • Cannot describe real projects in detail

  • Focuses only on individual contributions

  • No understanding of business impact

  • Unfamiliar with production challenges

  • Lacks awareness of industry best practices

Did you know?

Entity Framework was once criticized for being too slow—EF Core fixed that with massive performance improvements.

Frequently Asked Questions
Frequently Asked Questions

How important is Entity Framework Core knowledge for .NET Core developers?

How important is Entity Framework Core knowledge for .NET Core developers?

Should junior developers know microservices architecture?

Should junior developers know microservices architecture?

How do I assess a candidate's debugging skills?

How do I assess a candidate's debugging skills?

What's the difference between testing .NET Core skills vs .NET Framework skills?

What's the difference between testing .NET Core skills vs .NET Framework skills?

How technical should non-technical interviewers be in the process?

How technical should non-technical interviewers be in the process?

Did you know?

With Blazor, .NET developers can run C# code in the browser instead of JavaScript.

Founder, Utkrusht AI

Ex. Euler Motors, Oracle

Want to hire

the best talent

with proof

of skill?

Shortlist candidates with

strong proof of skill

in just 48 hours