# Pytania rekrutacyjne C# i .NET: Kompletny przewodnik 2026 > 17 najczesciej zadawanych pytan rekrutacyjnych z C# i .NET. LINQ, async/await, Dependency Injection, Entity Framework Core i ASP.NET Core ze szczegolowymi odpowiedziami. - Published: 2026-01-25 - Updated: 2026-04-10 - Author: SharpSkill - Tags: csharp, dotnet, interview, aspnet core, technical interview - Reading time: 20 min --- Rozmowy kwalifikacyjne z C# i .NET weryfikuja znajomosc jezyka, ekosystemu Microsoft oraz umiejetnosc projektowania wydajnych i niezawodnych aplikacji. Ponizszy przewodnik obejmuje kluczowe pytania -- od podstaw jezyka po zaawansowane wzorce architektoniczne. > **Wskazowka Rekrutacyjna** > > Rekruterzy cenia odpowiedzi, ktore wykazuja zrozumienie wewnetrznych mechanizmow .NET, a nie tylko skladni. Wyjasnienie "dlaczego" stojacego za kazdym konceptem robi decydujaca roznice. ## Podstawy C# ### Pytanie 1: Jaka jest roznica miedzy typami wartosciowymi a typami referencyjnymi? Ta fundamentalna roznica wplywa na alokacje pamieci, wydajnosc i zachowanie przy przekazywaniu parametrow. ```csharp // ValueVsReference.cs // Demonstrating behavior differences // VALUE TYPES: stored on the Stack, copied by value struct Point { public int X; public int Y; } // REFERENCE TYPES: stored on the Heap, copied by reference class Person { public string Name; } public class Demo { public static void Main() { // Value type: independent copy Point p1 = new Point { X = 10, Y = 20 }; Point p2 = p1; // Complete value copy p2.X = 100; // Does NOT modify p1 Console.WriteLine($"p1.X = {p1.X}"); // 10 // Reference type: same object in memory Person person1 = new Person { Name = "Alice" }; Person person2 = person1; // Reference copy person2.Name = "Bob"; // MODIFIES person1 too Console.WriteLine($"person1.Name = {person1.Name}"); // Bob // Special case: string is immutable string s1 = "Hello"; string s2 = s1; s2 = "World"; // Creates a new string Console.WriteLine($"s1 = {s1}"); // Hello } } ``` Typy wartosciowe (int, struct, enum) sa alokowane na stosie i automatycznie zwalniane. Typy referencyjne (class, interface, delegate) trafiaja na sterte i sa zarzadzane przez Garbage Collector. ### Pytanie 2: Omow slowa kluczowe `ref`, `out` i `in` Modyfikatory te kontroluja sposob przekazywania parametrow do metod, co wplywa na wydajnosc i mutowalnosc. ```csharp // ParameterModifiers.cs // The three pass-by-reference modifiers public class ParameterDemo { // REF: variable MUST be initialized before the call // Can be read AND modified in the method public static void ModifyWithRef(ref int value) { Console.WriteLine($"Received value: {value}"); value = value * 2; // Modification visible to caller } // OUT: variable does NOT need to be initialized // MUST be assigned before method exits public static bool TryParse(string input, out int result) { // result MUST be assigned in all execution paths if (int.TryParse(input, out result)) { return true; } result = 0; // Required assignment return false; } // IN: read-only pass-by-reference (C# 7.2+) // Avoids copying for large structs without allowing modification public static double CalculateDistance(in Point3D p1, in Point3D p2) { // p1.X = 10; // ERROR: cannot modify 'in' parameter return Math.Sqrt( Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2) + Math.Pow(p2.Z - p1.Z, 2) ); } public static void Main() { // Using ref int number = 5; ModifyWithRef(ref number); Console.WriteLine($"After ref: {number}"); // 10 // Using out if (TryParse("123", out int parsed)) { Console.WriteLine($"Parsed: {parsed}"); // 123 } // Using in (optimal for large structs) var point1 = new Point3D(0, 0, 0); var point2 = new Point3D(3, 4, 0); var distance = CalculateDistance(in point1, in point2); } } public readonly struct Point3D { public readonly double X, Y, Z; public Point3D(double x, double y, double z) => (X, Y, Z) = (x, y, z); } ``` Modyfikator `in` jest szczegolnie przydatny dla duzych struktur, poniewaz eliminuje kopiowanie przy jednoczesnym zagwarantowaniu niemutowalnosci. To czesty wzorzec w kodzie o wysokiej wydajnosci. > **Wydajnosc z in** > > Uzycie `in` dla struktur wiekszych niz 16 bajtow poprawia wydajnosc dzieki uniknieciu kopiowania. Dla malych struktur przekazywanie przez wartosc pozostaje bardziej efektywne. ### Pytanie 3: Jak dziala Garbage Collector w .NET? Garbage Collector w .NET uzywa algorytmu generacyjnego do optymalizacji automatycznego zarzadzania pamiecia. ```csharp // GarbageCollectorDemo.cs // Understanding GC behavior public class GCDemo { public static void DemonstrateGenerations() { // Generation 0: newly allocated objects var shortLived = new byte[1000]; Console.WriteLine($"Generation: {GC.GetGeneration(shortLived)}"); // 0 // Force collection to promote the object GC.Collect(); Console.WriteLine($"After GC: {GC.GetGeneration(shortLived)}"); // 1 GC.Collect(); Console.WriteLine($"After 2nd GC: {GC.GetGeneration(shortLived)}"); // 2 // Memory statistics var info = GC.GetGCMemoryInfo(); Console.WriteLine($"Total heap: {info.HeapSizeBytes / 1024 / 1024}MB"); } // IDisposable pattern for unmanaged resources public class DatabaseConnection : IDisposable { private IntPtr _nativeHandle; private bool _disposed = false; public DatabaseConnection() { _nativeHandle = AllocateNativeResource(); } // Public Dispose method public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); // Prevents finalizer call } // Protected Dispose pattern protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // Free managed resources } // Free unmanaged resources if (_nativeHandle != IntPtr.Zero) { FreeNativeResource(_nativeHandle); _nativeHandle = IntPtr.Zero; } _disposed = true; } } // Finalizer (destructor) - called by GC if Dispose wasn't called ~DatabaseConnection() { Dispose(disposing: false); } private IntPtr AllocateNativeResource() => IntPtr.Zero; private void FreeNativeResource(IntPtr handle) { } } } // Recommended usage with using public class Usage { public void Example() { // C# 8+: using declaration using var connection = new GCDemo.DatabaseConnection(); // ... usage // Dispose() called automatically at end of scope } } ``` GC zbiera generacje 0 czesto (milisekundy), generacje 1 okazjonalnie, a generacje 2 rzadko. Obiekty na LOH (Large Object Heap > 85KB) sa obslugiwane osobno. ## LINQ i Kolekcje ### Pytanie 4: Jaka jest roznica miedzy IEnumerable a IQueryable? To pytanie jest kluczowe dla zrozumienia odroczonego wykonania i wydajnosci zapytan. ```csharp // EnumerableVsQueryable.cs // Fundamental execution differences public class LinqDemo { public static void CompareExecution(AppDbContext context) { // IEnumerable: executes IN MEMORY (client-side) IEnumerable enumerable = context.Products.AsEnumerable(); var filteredEnum = enumerable .Where(p => p.Price > 100) // Filtering in C# .ToList(); // Generated SQL: SELECT * FROM Products (ALL loaded) // IQueryable: executes on DATABASE (server-side) IQueryable queryable = context.Products; var filteredQuery = queryable .Where(p => p.Price > 100) // Translated to SQL WHERE .ToList(); // Generated SQL: SELECT * FROM Products WHERE Price > 100 // Query composition with IQueryable var query = context.Products.AsQueryable(); // Each operation adds to the Expression Tree query = query.Where(p => p.IsActive); query = query.Where(p => p.CategoryId == 5); query = query.OrderBy(p => p.Name); // Execution happens HERE, with a single optimized SQL query var results = query.ToList(); } // Generic method that works with both public static IEnumerable FilterByCondition( IEnumerable source, Func predicate) { return source.Where(predicate); } // Optimized version for IQueryable public static IQueryable FilterByCondition( IQueryable source, Expression> predicate) { // Expression> enables SQL translation return source.Where(predicate); } } ``` Nalezy uzywac `IQueryable` z Entity Framework, aby filtrowanie odbywalo sie po stronie bazy danych. `IEnumerable` sprawdza sie dla kolekcji w pamieci lub gdy wszystkie dane sa juz zaladowane. ### Pytanie 5: Omow odroczone wykonanie w LINQ Odroczone wykonanie to fundamentalny koncept wplywajacy na wydajnosc i zachowanie zapytan. ```csharp // DeferredExecution.cs // Understanding when queries actually execute public class DeferredExecutionDemo { public static void Demonstrate() { var numbers = new List { 1, 2, 3, 4, 5 }; // Query is DEFINED but NOT EXECUTED var query = numbers.Where(n => { Console.WriteLine($"Evaluating {n}"); return n > 2; }); Console.WriteLine("Query defined, but nothing happened yet"); // Modifying source BEFORE execution numbers.Add(6); numbers.Add(7); Console.WriteLine("Starting iteration:"); // EXECUTION happens HERE during enumeration foreach (var n in query) { Console.WriteLine($"Result: {n}"); } // Output includes 6 and 7 because they were added before execution } // Methods that FORCE immediate execution public static void ImmediateExecution() { var numbers = new List { 1, 2, 3, 4, 5 }; // ToList(), ToArray(), ToDictionary() = immediate execution var list = numbers.Where(n => n > 2).ToList(); // Count(), First(), Single(), Any() = immediate execution var count = numbers.Where(n => n > 2).Count(); var first = numbers.First(n => n > 2); // Aggregate(), Sum(), Max(), Min() = immediate execution var sum = numbers.Where(n => n > 2).Sum(); } // Danger: multiple enumeration public static void MultipleEnumerationProblem() { var numbers = GetNumbers(); // IEnumerable returned by yield // WARNING: EACH use re-executes the query var count = numbers.Count(); // 1st enumeration var first = numbers.First(); // 2nd enumeration // SOLUTION: materialize once var materializedList = numbers.ToList(); var countOk = materializedList.Count; // No re-execution var firstOk = materializedList.First(); // No re-execution } private static IEnumerable GetNumbers() { Console.WriteLine("GetNumbers called"); yield return 1; yield return 2; yield return 3; } } ``` > **Wielokrotna Enumeracja** > > Warto uzywac analizatorow takich jak ReSharper lub Rider do wykrywania problemow z wielokrotna enumeracja, ktore moga powodowac subtelne bledy i problemy z wydajnoscia. ## Async/Await i Wielowatkowosc ### Pytanie 6: Omow async/await i dzialanie Task Programowanie asynchroniczne jest niezbedne w nowoczesnych aplikacjach. Zrozumienie wewnetrznych mechanizmow swiadczy o zaawansowanej wiedzy. ```csharp // AsyncAwaitDemo.cs // Internal mechanisms of asynchronous programming public class AsyncDemo { // async transforms the method into a state machine public async Task FetchDataAsync(string url) { using var client = new HttpClient(); // await releases the thread during I/O wait // Thread returns to pool and can process other requests var response = await client.GetStringAsync(url); // After await, execution resumes (possibly on different thread) return ProcessData(response); } // Pattern for parallel execution public async Task<(User, List)> GetUserWithOrdersAsync(int userId) { // Both calls start SIMULTANEOUSLY var userTask = GetUserAsync(userId); var ordersTask = GetOrdersAsync(userId); // await waits for both results await Task.WhenAll(userTask, ordersTask); return (userTask.Result, ordersTask.Result); } // ConfigureAwait for libraries public async Task LibraryMethodAsync() { // ConfigureAwait(false) avoids capturing SynchronizationContext // Recommended in libraries to avoid deadlocks var data = await FetchDataAsync("https://api.example.com") .ConfigureAwait(false); return data.ToUpper(); } // Anti-pattern: async void (except for event handlers) public async void BadAsyncMethod() { // Exceptions cannot be caught // Impossible to await completion await Task.Delay(100); } // Correct: async Task public async Task GoodAsyncMethod() { await Task.Delay(100); } private Task GetUserAsync(int id) => Task.FromResult(new User()); private Task> GetOrdersAsync(int id) => Task.FromResult(new List()); private string ProcessData(string data) => data; } public class User { } public class Order { } ``` Kompilator przeksztalca metody async w maszyny stanowe. Kazdy `await` stanowi punkt zawieszenia, w ktorym watek jest zwalniany. ### Pytanie 7: Jak unikac deadlockow przy async/await? Deadlocki asynchroniczne to klasyczna pulapka, szczegolnie w aplikacjach z SynchronizationContext (UI, klasyczny ASP.NET). ```csharp // DeadlockPrevention.cs // Patterns to avoid deadlocks public class DeadlockDemo { private readonly IDataService _service; // DEADLOCK in classic ASP.NET or WinForms/WPF public string GetDataDeadlock() { // .Result or .Wait() blocks the UI/Request thread // async tries to resume on that same thread = deadlock return _service.FetchAsync().Result; } // Solution 1: async all the way public async Task GetDataAsync() { return await _service.FetchAsync(); } // Solution 2: ConfigureAwait(false) in the library public async Task FetchAsync() { var data = await HttpClient.GetStringAsync("url") .ConfigureAwait(false); // Don't capture context return data; } // Solution 3: Task.Run to isolate (if really necessary) public string GetDataWithTaskRun() { // Runs on thread pool without SynchronizationContext return Task.Run(async () => await _service.FetchAsync()).Result; } // Pattern for proper cancellation public async Task FetchWithCancellation(CancellationToken cancellationToken) { using var client = new HttpClient(); try { var response = await client.GetStringAsync("url", cancellationToken); return response; } catch (OperationCanceledException) { // Handle cancellation gracefully return string.Empty; } } // Timeout pattern public async Task FetchWithTimeout(TimeSpan timeout) { using var cts = new CancellationTokenSource(timeout); try { return await FetchWithCancellation(cts.Token); } catch (OperationCanceledException) { throw new TimeoutException("Request timed out"); } } private static readonly HttpClient HttpClient = new(); } public interface IDataService { Task FetchAsync(); } ``` Zlota zasada brzmi: "async na calej dlugosci" (async all the way). Nalezy unikac mieszania kodu synchronicznego z asynchronicznym. W ASP.NET Core SynchronizationContext nie istnieje, co redukuje ryzyko deadlockow. ## Dependency Injection i Architektura ### Pytanie 8: Omow rozne czasy zycia DI (Scoped, Transient, Singleton) Zrozumienie czasow zycia jest kluczowe dla unikania bledow wspolbieznosci i wyciekow pamieci. ```csharp // DependencyInjectionLifetimes.cs // The three lifetimes and their implications // SINGLETON: single instance for the entire application public class SingletonService { private readonly Guid _id = Guid.NewGuid(); public Guid Id => _id; // DANGER: no mutable state without synchronization // private int _counter; // Possible race conditions } // SCOPED: one instance per HTTP request (or scope) public class ScopedService { private readonly Guid _id = Guid.NewGuid(); public Guid Id => _id; // Safe: each request has its own instance // Ideal for DbContext, UnitOfWork } // TRANSIENT: new instance on every injection public class TransientService { private readonly Guid _id = Guid.NewGuid(); public Guid Id => _id; // Ideal for lightweight, stateless services } // Configuration in Program.cs public static class ServiceConfiguration { public static void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddScoped(); services.AddTransient(); // Entity Framework: ALWAYS Scoped services.AddDbContext(options => options.UseSqlServer(connectionString)); // HttpClient: use IHttpClientFactory services.AddHttpClient(); } } // CAPTIVE DEPENDENCY: Singleton depending on Scoped public class BadSingletonService { // WARNING: ScopedService will be captured and reused indefinitely // Causes concurrency bugs and stale data private readonly ScopedService _scoped; public BadSingletonService(ScopedService scoped) { _scoped = scoped; } } // SOLUTION: use IServiceScopeFactory public class GoodSingletonService { private readonly IServiceScopeFactory _scopeFactory; public GoodSingletonService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public async Task DoWork() { // Create explicit scope to get fresh ScopedService using var scope = _scopeFactory.CreateScope(); var scoped = scope.ServiceProvider.GetRequiredService(); // Use scoped... } } ``` Zasada: serwis nigdy nie powinien zalezec od serwisu o krotszym czasie zycia. Hierarchia to Singleton -> Scoped -> Transient. ### Pytanie 9: Jakie sa glowne wzorce projektowe w .NET? Rekruterzy oczekuja praktycznej znajomosci wzorcow, nie tylko definicji. ```csharp // DesignPatterns.cs // Common patterns in C#/.NET // REPOSITORY: data access abstraction public interface IUserRepository { Task GetByIdAsync(int id); Task> GetAllAsync(); Task AddAsync(User user); Task UpdateAsync(User user); Task DeleteAsync(int id); } public class UserRepository : IUserRepository { private readonly AppDbContext _context; public UserRepository(AppDbContext context) => _context = context; public async Task GetByIdAsync(int id) => await _context.Users.FindAsync(id); public async Task> GetAllAsync() => await _context.Users.ToListAsync(); public async Task AddAsync(User user) => await _context.Users.AddAsync(user); public async Task UpdateAsync(User user) => _context.Users.Update(user); public async Task DeleteAsync(int id) { var user = await GetByIdAsync(id); if (user != null) _context.Users.Remove(user); } } // UNIT OF WORK: transaction coordination public interface IUnitOfWork : IDisposable { IUserRepository Users { get; } IOrderRepository Orders { get; } Task SaveChangesAsync(); } public class UnitOfWork : IUnitOfWork { private readonly AppDbContext _context; public UnitOfWork(AppDbContext context) { _context = context; Users = new UserRepository(context); Orders = new OrderRepository(context); } public IUserRepository Users { get; } public IOrderRepository Orders { get; } public async Task SaveChangesAsync() => await _context.SaveChangesAsync(); public void Dispose() => _context.Dispose(); } // FACTORY: complex object creation public interface INotificationFactory { INotification Create(NotificationType type); } public class NotificationFactory : INotificationFactory { public INotification Create(NotificationType type) => type switch { NotificationType.Email => new EmailNotification(), NotificationType.Sms => new SmsNotification(), NotificationType.Push => new PushNotification(), _ => throw new ArgumentException($"Unknown type: {type}") }; } // DECORATOR: adding behaviors dynamically public interface IUserService { Task GetUserAsync(int id); } public class UserService : IUserService { private readonly IUserRepository _repository; public UserService(IUserRepository repository) => _repository = repository; public async Task GetUserAsync(int id) => await _repository.GetByIdAsync(id) ?? throw new NotFoundException($"User {id} not found"); } // Decorator that adds caching public class CachedUserService : IUserService { private readonly IUserService _inner; private readonly IMemoryCache _cache; public CachedUserService(IUserService inner, IMemoryCache cache) { _inner = inner; _cache = cache; } public async Task GetUserAsync(int id) { var cacheKey = $"user:{id}"; if (_cache.TryGetValue(cacheKey, out User? cached)) return cached!; var user = await _inner.GetUserAsync(id); _cache.Set(cacheKey, user, TimeSpan.FromMinutes(5)); return user; } } ``` Wzorce te sa stosowane codziennie w profesjonalnych aplikacjach .NET. Wzorzec Repository z Unit of Work jest szczegolnie powszechny w polaczeniu z Entity Framework. ## Entity Framework Core ### Pytanie 10: Jak optymalizowac wydajnosc z EF Core? EF Core moze byc bardzo szybki lub bardzo wolny w zaleznosci od sposobu uzycia. To pytanie weryfikuje znajomosc najlepszych praktyk. ```csharp // EFCoreOptimization.cs // Query optimization techniques public class EFCorePerformance { private readonly AppDbContext _context; // N+1 problem: one query per order public async Task> GetUsersWithOrdersBad() { var users = await _context.Users.ToListAsync(); foreach (var user in users) { // N additional queries! var orders = await _context.Orders .Where(o => o.UserId == user.Id) .ToListAsync(); } return users; } // Eager Loading with Include public async Task> GetUsersWithOrdersGood() { return await _context.Users .Include(u => u.Orders) // SQL JOIN .ThenInclude(o => o.Products) // Nested include .ToListAsync(); } // Projection to load only necessary data public async Task> GetUserSummaries() { return await _context.Users .Select(u => new UserDto { Id = u.Id, Name = u.Name, OrderCount = u.Orders.Count, // Calculated SQL-side TotalSpent = u.Orders.Sum(o => o.Total) }) .ToListAsync(); } // Split Query for large collections public async Task> GetUsersWithSplitQuery() { return await _context.Users .Include(u => u.Orders) .AsSplitQuery() // Generates separate queries instead of large JOIN .ToListAsync(); } // No Tracking for read-only operations public async Task> GetUsersReadOnly() { return await _context.Users .AsNoTracking() // No change tracking = faster .ToListAsync(); } // Batch operations (EF Core 7+) public async Task DeleteInactiveUsers() { // Single DELETE query instead of load then delete await _context.Users .Where(u => !u.IsActive && u.LastLoginAt < DateTime.UtcNow.AddYears(-1)) .ExecuteDeleteAsync(); } // Bulk update public async Task DeactivateOldUsers() { await _context.Users .Where(u => u.LastLoginAt < DateTime.UtcNow.AddMonths(-6)) .ExecuteUpdateAsync(u => u.SetProperty(x => x.IsActive, false)); } // Compiled Queries for frequent queries private static readonly Func> GetUserById = EF.CompileAsyncQuery((AppDbContext ctx, int id) => ctx.Users.FirstOrDefault(u => u.Id == id)); public async Task GetUserOptimized(int id) { return await GetUserById(_context, id); } } ``` > **Monitorowanie Zapytan** > > Warto wlaczyc logowanie SQL w srodowisku developerskim za pomoca `optionsBuilder.LogTo(Console.WriteLine)`, aby identyfikowac problematyczne zapytania. W produkcji nalezy uzywac narzedzi takich jak MiniProfiler lub Application Insights. ### Pytanie 11: Omow migracje i zarzadzanie schematem Zarzadzanie migracjami jest krytyczne dla wdrozen produkcyjnych. ```csharp // MigrationStrategies.cs // Professional EF Core migration management // DbContext configuration with conventions public class AppDbContext : DbContext { public DbSet Users => Set(); public DbSet Orders => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { // Apply all IEntityTypeConfiguration configurations modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); // Global convention for dates foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(DateTime)) { property.SetColumnType("datetime2"); } } } } } // Separate fluent configuration public class UserConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("Users"); builder.HasKey(u => u.Id); builder.Property(u => u.Email) .IsRequired() .HasMaxLength(256); builder.HasIndex(u => u.Email) .IsUnique(); builder.HasMany(u => u.Orders) .WithOne(o => o.User) .HasForeignKey(o => o.UserId) .OnDelete(DeleteBehavior.Cascade); } } // Data seeding public class DataSeeder { public static void Seed(ModelBuilder modelBuilder) { modelBuilder.Entity().HasData( new Role { Id = 1, Name = "Admin" }, new Role { Id = 2, Name = "User" } ); } } ``` Najwazniejsze polecenia migracji: - `dotnet ef migrations add MigrationName` -- utworzenie migracji - `dotnet ef database update` -- zastosowanie migracji - `dotnet ef migrations script` -- wygenerowanie skryptu SQL - `dotnet ef migrations remove` -- usuniecie ostatniej migracji ## ASP.NET Core ### Pytanie 12: Omow potok middleware w ASP.NET Core Potok middleware stanowi serce ASP.NET Core. Zrozumienie jego dzialania jest niezbedne. ```csharp // MiddlewarePipeline.cs // Request pipeline architecture // Custom Middleware - full class public class RequestLoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public RequestLoggingMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { // BEFORE: executed on the way in (request) var stopwatch = Stopwatch.StartNew(); _logger.LogInformation("Request: {Method} {Path}", context.Request.Method, context.Request.Path); try { // Pass to next middleware await _next(context); } finally { // AFTER: executed on the way out (response) stopwatch.Stop(); _logger.LogInformation("Response: {StatusCode} in {ElapsedMs}ms", context.Response.StatusCode, stopwatch.ElapsedMilliseconds); } } } // Extension for registration public static class MiddlewareExtensions { public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app) { return app.UseMiddleware(); } } // Pipeline configuration in Program.cs public class Startup { public void Configure(IApplicationBuilder app) { // ORDER is CRITICAL! // 1. Exception handling (must be first) app.UseExceptionHandler("/error"); // 2. HTTPS Redirection app.UseHttpsRedirection(); // 3. Static files (short-circuits if found) app.UseStaticFiles(); // 4. Routing (determines endpoint) app.UseRouting(); // 5. CORS (must be between Routing and Auth) app.UseCors(); // 6. Authentication (who are you?) app.UseAuthentication(); // 7. Authorization (are you allowed?) app.UseAuthorization(); // 8. Custom middleware app.UseRequestLogging(); // 9. Endpoints (executes controller/action) app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapRazorPages(); }); } } // Conditional middleware public static class ConditionalMiddleware { public static IApplicationBuilder UseWhen( this IApplicationBuilder app, Func predicate, Action configuration) { // Conditional branch of the pipeline return app.UseWhen(predicate, configuration); } public static void Example(IApplicationBuilder app) { // Apply middleware only for /api/* app.UseWhen( context => context.Request.Path.StartsWithSegments("/api"), apiApp => apiApp.UseMiddleware() ); } } ``` Middleware wykonuje sie w kolejnosci rejestracji na drodze do srodka (request) i w odwrotnej kolejnosci na drodze na zewnatrz (response). ### Pytanie 13: Jak zaimplementowac uwierzytelnianie JWT? Uwierzytelnianie JWT to standard dla nowoczesnych API REST. ```csharp // JwtAuthentication.cs // Complete JWT authentication configuration public static class JwtConfiguration { public static void AddJwtAuthentication(this IServiceCollection services, IConfiguration config) { var jwtSettings = config.GetSection("Jwt").Get()!; services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings.Issuer, ValidAudience = jwtSettings.Audience, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtSettings.SecretKey)), ClockSkew = TimeSpan.Zero // No tolerance on expiration }; // Events for logging/debugging options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { if (context.Exception is SecurityTokenExpiredException) { context.Response.Headers.Add("Token-Expired", "true"); } return Task.CompletedTask; } }; }); } } public class JwtSettings { public string SecretKey { get; set; } = string.Empty; public string Issuer { get; set; } = string.Empty; public string Audience { get; set; } = string.Empty; public int ExpirationMinutes { get; set; } = 60; } // Token generation service public class TokenService { private readonly JwtSettings _settings; public TokenService(IOptions settings) { _settings = settings.Value; } public string GenerateToken(User user, IEnumerable roles) { var securityKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(_settings.SecretKey)); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); var claims = new List { new(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new(JwtRegisteredClaimNames.Email, user.Email), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new("name", user.Name) }; // Add roles as claims claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); var token = new JwtSecurityToken( issuer: _settings.Issuer, audience: _settings.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_settings.ExpirationMinutes), signingCredentials: credentials ); return new JwtSecurityTokenHandler().WriteToken(token); } public ClaimsPrincipal? ValidateToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_settings.SecretKey); try { var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = true, ValidIssuer = _settings.Issuer, ValidateAudience = true, ValidAudience = _settings.Audience, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }, out _); return principal; } catch { return null; } } } // Usage in a controller [ApiController] [Route("api/[controller]")] public class AuthController : ControllerBase { private readonly TokenService _tokenService; private readonly IUserService _userService; [HttpPost("login")] public async Task Login([FromBody] LoginDto dto) { var user = await _userService.ValidateCredentialsAsync(dto.Email, dto.Password); if (user == null) return Unauthorized(new { message = "Invalid credentials" }); var roles = await _userService.GetRolesAsync(user.Id); var token = _tokenService.GenerateToken(user, roles); return Ok(new { token, expiresIn = 3600 }); } [Authorize] // Requires valid token [HttpGet("profile")] public IActionResult GetProfile() { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; return Ok(new { userId }); } [Authorize(Roles = "Admin")] // Requires Admin role [HttpGet("admin")] public IActionResult AdminOnly() { return Ok(new { message = "Welcome, Admin!" }); } } ``` ## Zaawansowane Pytania ### Pytanie 14: Czym sa Span`` i Memory``? Typy te umozliwiaja manipulacje pamiecia bez alokacji, co jest kluczowe dla kodu o wysokiej wydajnosci. ```csharp // SpanAndMemory.cs // Types for performant memory manipulation public class HighPerformanceDemo { // Span``: view over contiguous memory region (stack only) public static void SpanBasics() { // Span over an array int[] numbers = { 1, 2, 3, 4, 5 }; Span span = numbers.AsSpan(); // Slice without allocation Span slice = span.Slice(1, 3); // [2, 3, 4] // Modification affects original array slice[0] = 100; Console.WriteLine(numbers[1]); // 100 // Span on the stack (stackalloc) Span stackSpan = stackalloc int[100]; stackSpan.Fill(42); } // Parsing without allocation using Span public static bool TryParseDate(ReadOnlySpan input, out DateTime date) { // Format: "2024-01-15" date = default; if (input.Length != 10) return false; // Slicing without creating new strings var yearSpan = input.Slice(0, 4); var monthSpan = input.Slice(5, 2); var daySpan = input.Slice(8, 2); if (!int.TryParse(yearSpan, out int year)) return false; if (!int.TryParse(monthSpan, out int month)) return false; if (!int.TryParse(daySpan, out int day)) return false; date = new DateTime(year, month, day); return true; } // Memory``: like Span but can be stored on the heap public async Task ProcessDataAsync(Memory buffer) { // Memory can cross async boundaries await Task.Delay(100); // Convert to Span for processing Span span = buffer.Span; int sum = 0; foreach (var b in span) { sum += b; } return sum; } // ArrayPool: array reuse to avoid allocations public static void UseArrayPool() { // Rent an array from the pool byte[] buffer = ArrayPool.Shared.Rent(1024); try { // Use the buffer... // Note: may be larger than requested Console.WriteLine($"Buffer size: {buffer.Length}"); } finally { // ALWAYS return to pool ArrayPool.Shared.Return(buffer, clearArray: true); } } // Comparative benchmark public static string SubstringTraditional(string input, int start, int length) { // Creates new string = allocation return input.Substring(start, length); } public static ReadOnlySpan SubstringWithSpan(ReadOnlySpan input, int start, int length) { // Returns a view = NO allocation return input.Slice(start, length); } } ``` Span`` jest idealny do przetwarzania lancuchow znakow, parsowania i operacji na tablicach bez alokacji. ### Pytanie 15: Omow rekordy i ich zastosowania Rekordy (C# 9+) to niemutowalny typ referencyjny z rownoscia oparta na wartosciach, idealny dla DTO i obiektow wartosci. ```csharp // RecordsDemo.cs // Features and use cases for records // Record class (reference, immutable by default) public record Person(string FirstName, string LastName, DateOnly BirthDate) { // Computed property public int Age => DateTime.Today.Year - BirthDate.Year; // Additional method public string FullName => $"{FirstName} {LastName}"; } // Record with validation public record Email { public string Value { get; } public Email(string value) { if (!IsValidEmail(value)) throw new ArgumentException("Invalid email format"); Value = value; } private static bool IsValidEmail(string email) => !string.IsNullOrEmpty(email) && email.Contains('@'); } // Record struct (value, C# 10+) public readonly record struct Point(double X, double Y) { public double Distance => Math.Sqrt(X * X + Y * Y); } public class RecordUsageDemo { public void DemonstrateFeatures() { // Creation var person1 = new Person("John", "Doe", new DateOnly(1990, 5, 15)); // Value-based equality (not reference) var person2 = new Person("John", "Doe", new DateOnly(1990, 5, 15)); Console.WriteLine(person1 == person2); // True // Mutation with 'with' (creates a copy) var person3 = person1 with { LastName = "Smith" }; Console.WriteLine(person1.LastName); // "Doe" (unchanged) Console.WriteLine(person3.LastName); // "Smith" // Deconstruction var (firstName, lastName, _) = person1; Console.WriteLine($"{firstName} {lastName}"); // Auto-generated ToString() Console.WriteLine(person1); // Output: Person { FirstName = John, LastName = Doe, BirthDate = 15/05/1990 } } // Records as DTOs (data transfer) public record CreateUserRequest(string Email, string Password, string Name); public record UserResponse(int Id, string Email, string Name, DateTime CreatedAt); // Records as Value Objects (DDD) public record Money(decimal Amount, string Currency) { public static Money operator +(Money a, Money b) { if (a.Currency != b.Currency) throw new InvalidOperationException("Currency mismatch"); return new Money(a.Amount + b.Amount, a.Currency); } } // Record with inheritance public abstract record Shape(string Color); public record Circle(string Color, double Radius) : Shape(Color); public record Rectangle(string Color, double Width, double Height) : Shape(Color); } ``` Rekordy sa idealne dla: DTO, obiektow wartosci, niemutowalnych konfiguracji i kazdego obiektu, gdzie tozsamosc opiera sie na wartosciach zamiast referencji. ### Pytanie 16: Jak zaimplementowac rozproszony system cache? Cache jest kluczowy dla wydajnosci aplikacji dzialajacych na duzej skali. ```csharp // DistributedCaching.cs // Cache implementation with Redis public interface ICacheService { Task GetAsync(string key); Task SetAsync(string key, T value, TimeSpan? expiration = null); Task RemoveAsync(string key); Task GetOrSetAsync(string key, Func> factory, TimeSpan? expiration = null); } public class RedisCacheService : ICacheService { private readonly IDistributedCache _cache; private readonly JsonSerializerOptions _jsonOptions; public RedisCacheService(IDistributedCache cache) { _cache = cache; _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; } public async Task GetAsync(string key) { var data = await _cache.GetStringAsync(key); if (string.IsNullOrEmpty(data)) return default; return JsonSerializer.Deserialize(data, _jsonOptions); } public async Task SetAsync(string key, T value, TimeSpan? expiration = null) { var options = new DistributedCacheEntryOptions(); if (expiration.HasValue) { options.AbsoluteExpirationRelativeToNow = expiration; } else { options.SlidingExpiration = TimeSpan.FromMinutes(10); } var json = JsonSerializer.Serialize(value, _jsonOptions); await _cache.SetStringAsync(key, json, options); } public async Task RemoveAsync(string key) { await _cache.RemoveAsync(key); } // Cache-Aside pattern with factory public async Task GetOrSetAsync( string key, Func> factory, TimeSpan? expiration = null) { var cached = await GetAsync(key); if (cached != null) return cached; var value = await factory(); await SetAsync(key, value, expiration); return value; } } // Usage in a service public class ProductService { private readonly ICacheService _cache; private readonly IProductRepository _repository; public ProductService(ICacheService cache, IProductRepository repository) { _cache = cache; _repository = repository; } public async Task GetProductAsync(int id) { var cacheKey = $"product:{id}"; return await _cache.GetOrSetAsync( cacheKey, async () => await _repository.GetByIdAsync(id), TimeSpan.FromMinutes(30) ); } // Cache invalidation public async Task UpdateProductAsync(int id, UpdateProductDto dto) { await _repository.UpdateAsync(id, dto); // Invalidate cache await _cache.RemoveAsync($"product:{id}"); } } // Configuration in Program.cs public static class CacheConfiguration { public static void AddCaching(this IServiceCollection services, IConfiguration config) { services.AddStackExchangeRedisCache(options => { options.Configuration = config.GetConnectionString("Redis"); options.InstanceName = "MyApp:"; }); services.AddSingleton(); } } ``` > **Uniewaznanie Cache** > > "W informatyce sa tylko dwie trudne rzeczy: uniewaznanie cache i nazewnictwo." Zdefiniowanie jasnej strategii uniewaznania cache jest niezbedne, aby unikac nieaktualnych danych. ### Pytanie 17: Jak obslugiwac transakcje rozproszone? W architekturach mikroserwisowych transakcje rozproszone wymagaja specyficznych wzorcow. ```csharp // DistributedTransactions.cs // Patterns for consistency in distributed systems // SAGA Pattern with Orchestration public class OrderSaga { private readonly IOrderRepository _orderRepository; private readonly IPaymentService _paymentService; private readonly IInventoryService _inventoryService; private readonly INotificationService _notificationService; public async Task ProcessOrderAsync(CreateOrderCommand command) { Order? order = null; PaymentResult? payment = null; InventoryReservation? reservation = null; try { // Step 1: Create order order = await _orderRepository.CreateAsync(command); // Step 2: Reserve inventory reservation = await _inventoryService.ReserveAsync(order.Items); // Step 3: Process payment payment = await _paymentService.ProcessAsync(order.Total, command.PaymentMethod); // Step 4: Confirm order await _orderRepository.ConfirmAsync(order.Id); // Step 5: Notification (non-critical) await _notificationService.SendOrderConfirmationAsync(order); return OrderResult.Success(order.Id); } catch (Exception ex) { // COMPENSATION: undo previous steps in reverse order if (payment?.IsSuccessful == true) { await _paymentService.RefundAsync(payment.TransactionId); } if (reservation != null) { await _inventoryService.ReleaseReservationAsync(reservation.Id); } if (order != null) { await _orderRepository.CancelAsync(order.Id, ex.Message); } return OrderResult.Failure(ex.Message); } } } // Outbox Pattern for reliable event publishing public class OutboxProcessor { private readonly AppDbContext _context; private readonly IMessageBus _messageBus; public async Task ProcessOutboxAsync() { var pendingMessages = await _context.OutboxMessages .Where(m => m.ProcessedAt == null) .OrderBy(m => m.CreatedAt) .Take(100) .ToListAsync(); foreach (var message in pendingMessages) { try { // Publish message await _messageBus.PublishAsync(message.Type, message.Payload); // Mark as processed message.ProcessedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); } catch (Exception ex) { message.RetryCount++; message.Error = ex.Message; await _context.SaveChangesAsync(); } } } } // Outbox model public class OutboxMessage { public Guid Id { get; set; } public string Type { get; set; } = string.Empty; public string Payload { get; set; } = string.Empty; public DateTime CreatedAt { get; set; } public DateTime? ProcessedAt { get; set; } public int RetryCount { get; set; } public string? Error { get; set; } } // Extension to add outbox message within a transaction public static class DbContextExtensions { public static void AddOutboxMessage(this AppDbContext context, T @event) { var message = new OutboxMessage { Id = Guid.NewGuid(), Type = typeof(T).Name, Payload = JsonSerializer.Serialize(@event), CreatedAt = DateTime.UtcNow }; context.OutboxMessages.Add(message); } } ``` Wzorzec SAGA gwarantuje ostateczna spojnosc w systemach rozproszonych. Wzorzec Outbox zapewnia niezawodne publikowanie zdarzen nawet w przypadku awarii. ## Podsumowanie Rozmowy kwalifikacyjne z C# i .NET weryfikuja polaczenie wiedzy teoretycznej o runtime i jezyku z praktycznymi umiejetnosciami w zakresie architektury i budowy aplikacji. Opanowanie fundamentalnych konceptow przy jednoczesnym zrozumieniu zaawansowanych wzorcow wyroznia seniorow. ### Lista kontrolna przygotowania - Rozumienie roznicy miedzy typami wartosciowymi a referencyjnymi - Biegla znajomosc async/await i unikanie deadlockow - Znajomosc roznic miedzy IEnumerable a IQueryable - Optymalizacja zapytan Entity Framework Core - Poprawna implementacja wzorca IDisposable - Konfiguracja Dependency Injection z wlasciwymi czasami zycia - Zabezpieczanie API za pomoca JWT - Uzycie Span`` i Memory`` w kodzie o wysokiej wydajnosci Przygotowanie powinno laczyc teorie z praktyka. Budowanie wlasnych projektow, kontrybuowanie do ekosystemu open source .NET oraz rozwiazywanie zadan na platformach takich jak HackerRank czy LeetCode utrwala te wiedze na potrzeby najbardziej wymagajacych rozmow kwalifikacyjnych. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/dotnet/csharp-dotnet-interview-questions