📄

Csharp Advanced Senior Theory

Advanced 2 min read 300 words

Teorie Avansată Senior Level - Entity Framework Core și .NET

Cuprins

  1. Entity Framework Core - Deep Dive
  2. .NET Runtime și Performance
  3. Advanced EF Core Patterns
  4. Optimizare și Profiling
  5. Advanced C# Concepts pentru Seniori
  6. Production Patterns și Best Practices

Entity Framework Core - Deep Dive

1. Change Tracking Mechanism

Cum Funcționează Change Tracking

Change tracking este procesul prin care EF Core monitorizează modificările pe entități.

public class ChangeTrackingDemo
{
    private readonly AppDbContext _context;

    // 1. SNAPSHOT TRACKING - default în EF Core
    public void SnapshotTrackingExample()
    {
        var product = new Product { Id = 1, Name = "Laptop", Price = 1000 };

        // EF creează o snapshot a valorilor originale
        _context.Products.Add(product);
        _context.SaveChanges(); // State = Unchanged

        // Modificare
        product.Price = 900;

        // EF compară valoarea curentă cu snapshot-ul
        var entry = _context.Entry(product);
        Console.WriteLine(entry.State); // Modified

        _context.SaveChanges();
    }

    // 2. NOTIFICATION TRACKING - cu INotifyPropertyChanged
    public void NotificationTrackingExample()
    {
        // Entități care implementează INotifyPropertyChanged
        // sunt notificate automat pe schimbări
    }

    // 3. AUTO TRACKING vs UNTRACKED QUERIES
    public async Task TrackingVsUntracked()
    {
        // Cu tracking (default)
        var product1 = await _context.Products
            .FirstOrDefaultAsync(p => p.Id == 1);
        // Se află în change tracker

        // Fără tracking
        var product2 = await _context.Products
            .AsNoTracking()
            .FirstOrDefaultAsync(p => p.Id == 1);
        // Nu este tracked - mai rapid, RAM-ul mai mic
    }

    // 4. CHANGE TRACKER STATE MACHINE
    public void StateTransitions()
    {
        var product = new Product { Name = "New" };

        Console.WriteLine(_context.Entry(product).State); // Detached

        _context.Products.Add(product);
        Console.WriteLine(_context.Entry(product).State); // Added

        _context.SaveChanges();
        Console.WriteLine(_context.Entry(product).State); // Unchanged

        product.Price = 100;
        Console.WriteLine(_context.Entry(product).State); // Modified

        _context.Products.Remove(product);
        Console.WriteLine(_context.Entry(product).State); // Deleted

        _context.SaveChanges();
        Console.WriteLine(_context.Entry(product).State); // Detached
    }
}

// States: Detached → Added → Unchanged → Modified → Deleted → Detached

Performance Implications

public class ChangeTrackerPerformance
{
    private readonly AppDbContext _context;

    // ❌ SLOW - Tracking 1 milion de entități
    public async Task SlowBulkLoad()
    {
        var products = await _context.Products
            .Where(p => p.Category == "Electronics")
            .ToListAsync(); // Toate sunt tracked

        // Memory spike - fiecare entitate are overhead
    }

    // ✅ FAST - Fără tracking
    public async Task FastBulkLoad()
    {
        var products = await _context.Products
            .AsNoTracking()
            .Where(p => p.Category == "Electronics")
            .Select(p => new ProductDTO { Id = p.Id, Name = p.Name })
            .ToListAsync();

        // Minimal memory usage
    }

    // ✅ OPTIMAL - Bulk operations
    public async Task BulkUpdateEfficient()
    {
        // Utilizarea expresiilor pentru batch updates
        await _context.Products
            .Where(p => p.Price > 1000)
            .ExecuteUpdateAsync(s => s.SetProperty(
                p => p.Price,
                p => p.Price * 0.9m // 10% discount
            ));

        // Direct SQL, nu trec prin change tracker
    }
}

2. Query Compilation și Execution

Expression Tree Compilation

public class QueryCompilation
{
    private readonly AppDbContext _context;

    // 1. COMPILED QUERIES - pentru queries repetate frecvent
    public class CompiledQueries
    {
        private static readonly Func<AppDbContext, int, Task<Product>> GetProductById =
            EF.CompileAsyncQuery((AppDbContext context, int id) =>
                context.Products
                    .Include(p => p.Category)
                    .FirstOrDefault(p => p.Id == id));

        public async Task<Product> GetProduct(int id)
        {
            // Compiled query - nu se recompile de fiecare dată
            return await GetProductById(_context, id);
        }
    }

    // 2. QUERY TAGS - pentru logging și debugging
    public async Task<List<Product>> GetExpensiveProductsWithTag()
    {
        return await _context.Products
            .TagWith("GetExpensiveProducts - from Admin Dashboard")
            .Where(p => p.Price > 5000)
            .ToListAsync();

        // Tag-ul apare în query log
    }

    // 3. TRANSLATION TO SQL - ce se translate și ce nu
    public void TranslationExamples()
    {
        // ✅ SE TRANSLATE - execută în DB
        var query1 = _context.Products
            .Where(p => p.Price > 100 && p.Category == "Electronics")
            .Select(p => new { p.Id, p.Name });

        // ⚠️ PARTIAL - filtrare în DB, apoi LINQ to Objects
        var query2 = _context.Products
            .Where(p => p.IsAvailable) // DB
            .AsEnumerable()
            .Where(p => SomeClientMethod(p.Name)) // Client-side
            .ToList();

        // ❌ NU SE TRANSLATE - LINQ to Objects pe toți produsele
        var query3 = _context.Products
            .AsEnumerable()
            .Where(p => p.Price > 100)
            .ToList();
    }

    private bool SomeClientMethod(string name) => !string.IsNullOrEmpty(name);
}

3. Relationships și Lazy Loading

Loaded vs Unloaded Navigation Properties

public class RelationshipManagement
{
    private readonly AppDbContext _context;

    // 1. LAZY LOADING - automat încarcă relații
    public void LazyLoadingExample()
    {
        var product = _context.Products.Find(1);

        // Accesul la Category declanșează o query separată
        var categoryName = product.Category.Name; // 2 queries totale
    }

    // 2. EXPLICIT LOADING - manual control
    public async Task ExplicitLoadingExample()
    {
        var product = _context.Products.Find(1);

        // Numai dacă vrem
        await _context.Entry(product)
            .Reference(p => p.Category)
            .LoadAsync();

        var categoryName = product.Category.Name;
    }

    // 3. EAGER LOADING - Include
    public async Task EagerLoadingExample()
    {
        var product = await _context.Products
            .Include(p => p.Category)
            .Include(p => p.Reviews)
            .FirstOrDefaultAsync(p => p.Id == 1);

        // Totul încărcat în 1 query cu JOIN-uri
    }

    // 4. FILTERED INCLUDES (EF Core 5.0+)
    public async Task FilteredIncludesExample()
    {
        var product = await _context.Products
            .Include(p => p.Reviews.Where(r => r.Rating >= 4))
            .FirstOrDefaultAsync(p => p.Id == 1);

        // Numai reviewuri cu 4+ stele
    }

    // 5. DEEP INCLUDES - relații înlănțuite
    public async Task DeepIncludesExample()
    {
        var category = await _context.Categories
            .Include(c => c.Products)
            .ThenInclude(p => p.Reviews)
            .ThenInclude(r => r.Author)
            .FirstOrDefaultAsync(c => c.Id == 1);

        // Category -> Products -> Reviews -> Author
    }

    // 6. COLLECTION VERSUS REFERENCE
    public void CollectionVsReference()
    {
        var product = _context.Products.Find(1);

        // Reference (many-to-one)
        var category = product.Category; // Navigation property scalar

        // Collection (one-to-many)
        var reviews = product.Reviews; // Navigation property collection
    }
}

4. Shadows Properties și Value Conversions

public class AdvancedConfiguration
{
    // SHADOW PROPERTIES - proprietăți care nu sunt în clasă
    public class ShadowPropertyExample
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Product>()
                .Property<DateTime>("CreatedAt")
                .HasDefaultValueSql("GETUTCDATE()");

            modelBuilder.Entity<Product>()
                .Property<byte[]>("RowVersion")
                .IsRowVersion();
        }

        // Accesare shadow property
        public void AccessShadowProperty(Product product)
        {
            var entry = _context.Entry(product);
            var createdAt = entry.Property("CreatedAt").CurrentValue;
            var rowVersion = entry.Property("RowVersion").CurrentValue;
        }

        // Use cases:
        // - Audit fields (CreatedAt, ModifiedAt, CreatedBy)
        // - Concurrency tokens (RowVersion)
        // - Technical fields neexpuse în API
    }

    // VALUE CONVERSIONS - transformare date în DB
    public class ValueConversionExample
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Enum to String conversion
            modelBuilder.Entity<Product>()
                .Property(p => p.Status)
                .HasConversion(
                    v => v.ToString(),
                    v => (ProductStatus)Enum.Parse(typeof(ProductStatus), v));

            // Boolean to int conversion (pentru legacy DB)
            modelBuilder.Entity<Product>()
                .Property(p => p.IsActive)
                .HasConversion(
                    v => v ? 1 : 0,
                    v => v == 1);

            // Complex type to JSON
            modelBuilder.Entity<Product>()
                .Property(p => p.Metadata)
                .HasConversion(
                    v => JsonConvert.SerializeObject(v),
                    v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));

            // Encrypted values
            modelBuilder.Entity<User>()
                .Property(u => u.Email)
                .HasConversion(
                    v => EncryptionService.Encrypt(v),
                    v => EncryptionService.Decrypt(v));
        }
    }
}

5. Query Filters Globali

public class GlobalQueryFiltersExample
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Soft delete - exclude deleted entities automat
        modelBuilder.Entity<Product>()
            .HasQueryFilter(p => !p.IsDeleted);

        // Tenant-aware queries
        modelBuilder.Entity<Order>()
            .HasQueryFilter(o => o.TenantId == _currentTenantId);

        // Published only
        modelBuilder.Entity<BlogPost>()
            .HasQueryFilter(p => p.IsPublished || p.AuthorId == _currentUserId);
    }

    // Disabling global filters când necesar
    public async Task GetAllIncludingDeleted()
    {
        var all = await _context.Products
            .IgnoreQueryFilters()
            .ToListAsync();
    }
}

6. Batch Operations și Performance

public class BatchOperationsPerformance
{
    private readonly AppDbContext _context;

    // BULK INSERT - cea mai rapidă metodă
    public async Task BulkInsertOptimal()
    {
        var products = GenerateLargeList(100_000);

        // Chunking - insert-uri în loturi
        foreach (var chunk in products.Chunk(5000))
        {
            await _context.Products.AddRangeAsync(chunk);
            await _context.SaveChangesAsync();
        }
    }

    // BULK UPDATE
    public async Task BulkUpdateOptimal()
    {
        await _context.Products
            .Where(p => p.LastModified < DateTime.UtcNow.AddYears(-1))
            .ExecuteUpdateAsync(s => s
                .SetProperty(p => p.IsArchived, true)
                .SetProperty(p => p.ArchivedDate, DateTime.UtcNow));

        // Direct SQL update, bypasses change tracking
    }

    // BULK DELETE
    public async Task BulkDeleteOptimal()
    {
        await _context.Products
            .Where(p => p.IsArchived && p.ArchivedDate < DateTime.UtcNow.AddYears(-5))
            .ExecuteDeleteAsync();

        // Direct SQL delete
    }

    // COMPARISON - Memory usage
    public class PerformanceComparison
    {
        // ❌ LENT - 100MB RAM, 50s pentru 100k items
        public async Task SlowApproach()
        {
            var products = GenerateLargeList(100_000);
            await _context.Products.AddRangeAsync(products);
            await _context.SaveChangesAsync();
        }

        // ✅ RAPID - 5MB RAM, 2s pentru 100k items
        public async Task FastApproach()
        {
            var products = GenerateLargeList(100_000);
            foreach (var chunk in products.Chunk(1000))
            {
                await _context.Products.AddRangeAsync(chunk);
                await _context.SaveChangesAsync();
                _context.ChangeTracker.Clear();
            }
        }

        // ✅✅ SUPER RAPID - 1MB RAM, 0.5s pentru 100k items
        public async Task UltraFastApproach()
        {
            var bulk = new BulkInsertOperation<Product>(options =>
            {
                options.BatchSize = 1000;
                options.EnableStreaming = true;
            });

            await bulk.InsertAsync(_context, GenerateLargeList(100_000));
        }
    }
}

.NET Runtime și Performance

1. Just-In-Time (JIT) Compilation

public class JITCompilationPatterns
{
    // 1. TIERED JIT (C# 9+) - Two-level compilation
    public class TieredJIT
    {
        // Tier 0: Rapid compilation, non-optimized code
        // Tier 1: Slow compilation, optimized code

        // Profiler-guided optimization (PGO) - .NET 6+
        // Runtime colectează informații de profiling
        // Recompilează cu optimizări specifice runtime-ului real
    }

    // 2. JITTED vs READY-TO-RUN (R2R)
    public class ReadyToRunExample
    {
        // Ahead-of-Time (AOT) compilation
        // .NET 8+ permite full AOT compilation
        // Fără JIT - instant startup, predictable performance

        /*
        .csproj configuration:
        <PublishAot>true</PublishAot>

        Benefits:
        - Instant startup (ms instead of s)
        - No JIT pause times
        - Smaller memory footprint
        - Deterministic performance

        Trade-offs:
        - No reflection (except code-generated)
        - No dynamic code generation
        - Larger executable size
        */
    }

    // 3. HOT/COLD CODE SEPARATION
    public class HotColdSeparation
    {
        public void FrequentCode()
        {
            // Optimized aggressively by JIT
            // Este executat des
        }

        [System.Runtime.CompilerServices.MethodImpl(
            System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public void RareCode()
        {
            // Compilat la Tier 0, rar optimizat la Tier 1
            // Este executat rar
        }
    }
}

2. Garbage Collection Tunning

public class GarbageCollectionTuning
{
    // 1. GC MODES
    public class GCModes
    {
        // Workstation GC (default)
        // - Single concurrent GC thread
        // - Low latency
        // - Minimal resource usage

        // Server GC
        // - One GC thread per logical processor
        // - Higher throughput
        // - More predictable behavior

        // Configuration in csproj:
        /*
        <PropertyGroup>
            <ServerGarbageCollection>true</ServerGarbageCollection>
            <RetainVMMemory>true</RetainVMMemory>
            <GCRetainVMMemory>true</GCRetainVMMemory>
        </PropertyGroup>
        */
    }

    // 2. GC CONFIGURATION
    public class ConfigureGC
    {
        public void SetupGarbageCollection()
        {
            // Limit GC heap size
            GCSettings.IsServerGC; // true/false

            // Memory pressure notifications
            var threshold = GCMemoryInfo.TotalMemory;

            // Register for memory pressure notifications
            GC.RegisterMemoryPressureCallback(MemoryPressureLevel.Medium, () =>
            {
                // Clear caches, cleanup resources
            });
        }
    }

    // 3. GC COLLECTIONS - Optimizare
    public class OptimizeCollections
    {
        private List<byte[]> _cache = new();

        // ❌ Cause excessive GC pressure
        public void CreateGarbageInLoop()
        {
            for (int i = 0; i < 1_000_000; i++)
            {
                var data = new byte[1024]; // Allocated
                _cache.Add(data); // Keeps reference
                // Gen 0 collections spike
            }
        }

        // ✅ Pre-allocate și reuse
        public void OptimizedApproach()
        {
            var buffer = new byte[1024];
            for (int i = 0; i < 1_000_000; i++)
            {
                Array.Clear(buffer);
                ProcessData(buffer);
                // No allocations in loop
            }
        }

        // ✅ Use pooling
        public void PoolingApproach()
        {
            using var pool = ArrayPool<byte>.Shared.Rent(1024);

            for (int i = 0; i < 1_000_000; i++)
            {
                Array.Clear(pool);
                ProcessData(pool);
            }

            ArrayPool<byte>.Shared.Return(pool);
        }
    }
}

3. Memory Optimization Strategies

public class AdvancedMemoryOptimization
{
    // 1. STACKALLOC - allocare pe stack
    public void StackAllocationExample()
    {
        // Stack allocation - fără GC
        Span<int> numbers = stackalloc int[128];

        for (int i = 0; i < numbers.Length; i++)
        {
            numbers[i] = i * i;
        }

        // Automat dealocated la iesire din scope
    }

    // 2. SPAN<T> și MEMORY<T>
    public class SpanMemoryPattern
    {
        // ✅ No allocation - direct memory access
        public void ProcessDataWithSpan(Span<byte> data)
        {
            // data poate fi:
            // - Un array
            // - Stack memory
            // - Unmanaged memory

            for (int i = 0; i < data.Length; i++)
            {
                data[i] = ProcessByte(data[i]);
            }
        }

        // ✅ Owned memory - control lifecycle
        public void ProcessWithOwnedMemory()
        {
            using (var memory = MemoryPool<byte>.Shared.Rent(1024))
            {
                var span = memory.Memory.Span;
                ProcessDataWithSpan(span);
            }
        }
    }

    // 3. READONLY STRUCTS - Zero-copy parameters
    public readonly struct DataPoint
    {
        public readonly int X;
        public readonly int Y;
        public readonly int Z;

        // Passed by reference, no copying
    }

    // 4. STRUCT VS CLASS - Memory layout
    public class MemoryLayout
    {
        // Class - Reference type (Heap)
        // 24 bytes overhead + fields
        // Garbage collected

        // Struct - Value type (Stack or inline)
        // Just the fields
        // No GC overhead

        // Rule: < 16 bytes = struct, > 16 bytes = class
    }
}

4. Async Performance

public class AsyncPerformancePatterns
{
    // 1. TASK POOL vs NEW TASKS
    public async Task OptimalAsyncPattern()
    {
        // ✅ Reuse tasks from pool
        var task = GetFromCacheAsync();
        var result = await task;

        // ❌ Allocate new Task
        var task2 = new Task<int>(async () => await FetchAsync());
    }

    // 2. VALUETASK - Optimization pentru result synchronous
    public class ValueTaskPattern
    {
        // ✅ ValueTask - avoid allocation if completed
        public ValueTask<int> FastOperationAsync()
        {
            if (TryGetCached(out var result))
            {
                // No allocation - return directly
                return new ValueTask<int>(result);
            }

            // Allocate Task only if async needed
            return new ValueTask<int>(FetchFromDbAsync());
        }

        private bool TryGetCached(out int result)
        {
            result = 42;
            return true;
        }

        private Task<int> FetchFromDbAsync()
        {
            return Task.FromResult(42);
        }
    }

    // 3. CONFIGUREAWAIT IMPACT
    public async Task ConfigureAwaitComparison()
    {
        // ❌ Capture context - potential overhead
        var result = await FetchDataAsync();

        // ✅ No context - skip synchronization context
        var result2 = await FetchDataAsync().ConfigureAwait(false);

        // In library code, always use ConfigureAwait(false)
    }

    // 4. CONCURRENT AWAIT vs SEQUENTIAL
    public async Task OptimalConcurrency()
    {
        // ❌ Sequential - 3 seconds
        var r1 = await Task1();
        var r2 = await Task2();
        var r3 = await Task3();

        // ✅ Concurrent - 1 second
        var tasks = new[] { Task1(), Task2(), Task3() };
        var results = await Task.WhenAll(tasks);

        // ✅ Alternative
        var t1 = Task1();
        var t2 = Task2();
        var t3 = Task3();
        var r1_alt = await t1;
        var r2_alt = await t2;
        var r3_alt = await t3;
    }
}

Advanced EF Core Patterns

1. Multi-Tenancy

public class MultiTenancyPattern
{
    // Tenant Context
    public interface ITenantProvider
    {
        string GetTenantId();
    }

    // DbContext extension
    public class MultiTenantDbContext : DbContext
    {
        private readonly ITenantProvider _tenantProvider;

        public MultiTenantDbContext(DbContextOptions options, ITenantProvider tenantProvider)
            : base(options)
        {
            _tenantProvider = tenantProvider;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Automatic tenant filtering
            var tenantId = _tenantProvider.GetTenantId();

            modelBuilder.Entity<Order>()
                .HasQueryFilter(o => o.TenantId == tenantId);

            modelBuilder.Entity<Product>()
                .HasQueryFilter(p => p.TenantId == tenantId);
        }
    }

    // Usage
    public class OrderService
    {
        private readonly MultiTenantDbContext _context;
        private readonly ITenantProvider _tenantProvider;

        public async Task<List<Order>> GetOrdersAsync()
        {
            // Automatically filtered by tenant
            return await _context.Orders.ToListAsync();
        }
    }
}

2. Event Sourcing Integration

public class EventSourcingPattern
{
    // Event store
    public class EventStore
    {
        public int Id { get; set; }
        public string AggregateId { get; set; }
        public string EventType { get; set; }
        public string EventData { get; set; }
        public DateTime CreatedAt { get; set; }
        public int Version { get; set; }
    }

    // Entity reconstruction from events
    public class AggregateRoot
    {
        private List<Event> _uncommittedEvents = new();

        protected void RaiseEvent(Event @event)
        {
            _uncommittedEvents.Add(@event);
            Apply(@event);
        }

        protected virtual void Apply(Event @event)
        {
            // Override in subclasses
        }

        public IReadOnlyList<Event> GetUncommittedEvents() => _uncommittedEvents.AsReadOnly();

        public void MarkEventsAsCommitted()
        {
            _uncommittedEvents.Clear();
        }
    }

    // Persistence
    public class EventSourcingRepository<T> where T : AggregateRoot, new()
    {
        private readonly AppDbContext _context;

        public async Task SaveAsync(T aggregate)
        {
            var events = aggregate.GetUncommittedEvents();

            foreach (var @event in events)
            {
                _context.EventStore.Add(new EventStore
                {
                    AggregateId = aggregate.Id.ToString(),
                    EventType = @event.GetType().Name,
                    EventData = JsonConvert.SerializeObject(@event),
                    CreatedAt = DateTime.UtcNow
                });
            }

            await _context.SaveChangesAsync();
            aggregate.MarkEventsAsCommitted();
        }

        public async Task<T> GetAsync(string id)
        {
            var events = await _context.EventStore
                .Where(e => e.AggregateId == id)
                .OrderBy(e => e.Version)
                .ToListAsync();

            var aggregate = new T();
            foreach (var storedEvent in events)
            {
                var @event = JsonConvert.DeserializeObject(
                    storedEvent.EventData,
                    Type.GetType(storedEvent.EventType));

                // Reconstruct from events
            }

            return aggregate;
        }
    }
}

3. CQRS Pattern

public class CQRSPattern
{
    // Command - mutație
    public class CreateOrderCommand
    {
        public string CustomerId { get; set; }
        public List<OrderLineDTO> Lines { get; set; }
    }

    // Command Handler
    public class CreateOrderCommandHandler
    {
        private readonly AppDbContext _context;
        private readonly IMediator _mediator;

        public async Task HandleAsync(CreateOrderCommand command)
        {
            var order = new Order
            {
                CustomerId = command.CustomerId,
                Lines = command.Lines.Select(l => new OrderLine
                {
                    ProductId = l.ProductId,
                    Quantity = l.Quantity
                }).ToList()
            };

            _context.Orders.Add(order);
            await _context.SaveChangesAsync();

            // Publish event
            await _mediator.Publish(new OrderCreatedEvent { OrderId = order.Id });
        }
    }

    // Query - citire, separate read model
    public class GetOrdersQuery
    {
        public string CustomerId { get; set; }
    }

    // Query Handler - utiliza read model optimizat
    public class GetOrdersQueryHandler
    {
        private readonly IQueryStore _queryStore;

        public async Task<List<OrderDTO>> HandleAsync(GetOrdersQuery query)
        {
            // Citire din replicated, denormalized read model
            return await _queryStore.Orders
                .Where(o => o.CustomerId == query.CustomerId)
                .ToListAsync();
        }
    }
}

4. Specification Pattern

public class SpecificationPattern
{
    // Specification interface
    public interface ISpecification<T>
    {
        Expression<Func<T, bool>> Criteria { get; }
        List<Expression<Func<T, object>>> Includes { get; }
        List<string> IncludeStrings { get; }
        Expression<Func<T, object>> OrderBy { get; }
        Expression<Func<T, object>> OrderByDescending { get; }
        int? Take { get; }
        int? Skip { get; }
        bool IsPagingEnabled { get; }
    }

    // Base specification
    public abstract class BaseSpecification<T> : ISpecification<T>
    {
        public Expression<Func<T, bool>> Criteria { get; protected set; }
        public List<Expression<Func<T, object>>> Includes { get; } = new();
        public List<string> IncludeStrings { get; } = new();
        public Expression<Func<T, object>> OrderBy { get; protected set; }
        public Expression<Func<T, object>> OrderByDescending { get; protected set; }
        public int? Take { get; protected set; }
        public int? Skip { get; protected set; }
        public bool IsPagingEnabled { get; protected set; }

        protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
        {
            Includes.Add(includeExpression);
        }

        protected virtual void AddInclude(string includeString)
        {
            IncludeStrings.Add(includeString);
        }

        protected virtual void ApplyPaging(int skip, int take)
        {
            Skip = skip;
            Take = take;
            IsPagingEnabled = true;
        }
    }

    // Concrete specification
    public class GetProductsWithCategorySpecification : BaseSpecification<Product>
    {
        public GetProductsWithCategorySpecification(string categoryName, int pageIndex, int pageSize)
        {
            Criteria = p => p.Category.Name == categoryName;
            AddInclude(p => p.Category);
            AddInclude(p => p.Reviews);
            OrderBy = p => p.Name;
            ApplyPaging(pageIndex * pageSize, pageSize);
        }
    }

    // Repository implementation
    public class EFRepository<T> where T : class
    {
        private readonly AppDbContext _context;

        public async Task<List<T>> ListAsync(ISpecification<T> specification)
        {
            return await ApplySpecification(specification).ToListAsync();
        }

        public async Task<int> CountAsync(ISpecification<T> specification)
        {
            return await ApplySpecification(specification).CountAsync();
        }

        private IQueryable<T> ApplySpecification(ISpecification<T> spec)
        {
            var query = _context.Set<T>().AsQueryable();

            if (spec.Criteria != null)
                query = query.Where(spec.Criteria);

            query = spec.Includes.Aggregate(query, (current, include) =>
                current.Include(include));

            query = spec.IncludeStrings.Aggregate(query, (current, include) =>
                current.Include(include));

            if (spec.OrderBy != null)
                query = query.OrderBy(spec.OrderBy);

            if (spec.OrderByDescending != null)
                query = query.OrderByDescending(spec.OrderByDescending);

            if (spec.IsPagingEnabled)
            {
                if (spec.Skip.HasValue)
                    query = query.Skip(spec.Skip.Value);

                if (spec.Take.HasValue)
                    query = query.Take(spec.Take.Value);
            }

            return query;
        }
    }
}

5. Interceptors

public class InterceptorPatterns
{
    // Audit interceptor
    public class AuditInterceptor : SaveChangesInterceptor
    {
        private readonly ICurrentUserService _currentUserService;

        public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
            DbContextEventData eventData,
            InterceptionResult<int> result,
            CancellationToken cancellationToken = default)
        {
            var context = eventData.Context;

            foreach (var entry in context.ChangeTracker.Entries())
            {
                if (entry.Entity is IAuditable auditable)
                {
                    if (entry.State == EntityState.Added)
                    {
                        auditable.CreatedAt = DateTime.UtcNow;
                        auditable.CreatedBy = _currentUserService.GetUserId();
                    }

                    if (entry.State == EntityState.Modified)
                    {
                        auditable.ModifiedAt = DateTime.UtcNow;
                        auditable.ModifiedBy = _currentUserService.GetUserId();
                    }
                }
            }

            return base.SavingChangesAsync(eventData, result, cancellationToken);
        }
    }

    // Query logging interceptor
    public class QueryLoggingInterceptor : DbCommandInterceptor
    {
        private readonly ILogger<QueryLoggingInterceptor> _logger;

        public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutedAsync(
            DbCommand command,
            CommandExecutedEventData eventData,
            InterceptionResult<DbDataReader> result,
            CancellationToken cancellationToken = default)
        {
            _logger.LogInformation($"Query executed: {command.CommandText}");
            _logger.LogInformation($"Duration: {eventData.Duration.TotalMilliseconds}ms");

            return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
        }
    }

    // Retry logic interceptor
    public class RetryInterceptor : DbCommandInterceptor
    {
        public override async ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
            DbCommand command,
            CommandEventData eventData,
            InterceptionResult<DbDataReader> result,
            CancellationToken cancellationToken = default)
        {
            int retryCount = 0;
            const int maxRetries = 3;

            while (retryCount < maxRetries)
            {
                try
                {
                    return await base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
                }
                catch (InvalidOperationException) when (retryCount < maxRetries - 1)
                {
                    retryCount++;
                    await Task.Delay(100 * retryCount);
                }
            }

            return result;
        }
    }
}

Optimizare și Profiling

1. Database Query Optimization

public class DatabaseOptimization
{
    // Query profiling
    public class QueryProfiler
    {
        private readonly AppDbContext _context;

        public async Task<QueryProfile> ProfileQueryAsync<T>(
            IQueryable<T> query) where T : class
        {
            var sw = Stopwatch.StartNew();

            var sql = query.ToQueryString();
            Console.WriteLine($"Generated SQL:\n{sql}");

            var result = await query.ToListAsync();

            sw.Stop();

            return new QueryProfile
            {
                GeneratedSql = sql,
                ExecutionTime = sw.ElapsedMilliseconds,
                RowCount = result.Count
            };
        }
    }

    // Index recommendations
    public class IndexOptimization
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Frequent filters
            modelBuilder.Entity<Product>()
                .HasIndex(p => p.CategoryId)
                .HasName("IX_Product_CategoryId");

            // Composite indexes
            modelBuilder.Entity<OrderLine>()
                .HasIndex(ol => new { ol.OrderId, ol.ProductId })
                .HasName("IX_OrderLine_OrderId_ProductId");

            // Unique indexes
            modelBuilder.Entity<User>()
                .HasIndex(u => u.Email)
                .IsUnique()
                .HasName("IX_User_Email_Unique");

            // Filtered indexes
            modelBuilder.Entity<Order>()
                .HasIndex(o => o.CustomerId)
                .HasFilter("[IsArchived] = 0")
                .HasName("IX_Order_CustomerId_Active");
        }
    }
}

2. Benchmarking

[MemoryDiagnoser]
public class PerformanceBenchmarks
{
    private AppDbContext _context;
    private List<Product> _products;

    [GlobalSetup]
    public void Setup()
    {
        _context = new AppDbContext();
        _products = GenerateTestData(1000);
    }

    [Benchmark]
    public async Task QueryWithTracking()
    {
        var result = await _context.Products
            .Where(p => p.Price > 100)
            .ToListAsync();
    }

    [Benchmark]
    public async Task QueryWithoutTracking()
    {
        var result = await _context.Products
            .AsNoTracking()
            .Where(p => p.Price > 100)
            .ToListAsync();
    }

    [Benchmark]
    public async Task QueryWithProjection()
    {
        var result = await _context.Products
            .Where(p => p.Price > 100)
            .Select(p => new { p.Id, p.Name })
            .ToListAsync();
    }

    // Usage:
    // var summary = BenchmarkRunner.Run<PerformanceBenchmarks>();
}

Advanced C# Concepts pentru Seniori

1. Records - Immutable Reference Types

// Positional record - perfect pentru DTO-uri
public record OrderDto(int Id, string CustomerName, decimal Total);

// Init-only properties
public record ProductRecord
{
    public int Id { get; init; }
    public string Name { get; init; }
    public decimal Price { get; init; }
}

// Record inheritance
public record PremiumOrderDto(int Id, string CustomerName, decimal Total, decimal Discount)
    : OrderDto(Id, CustomerName, Total);

// Value equality - FREE from record syntax
var order1 = new OrderDto(1, "John", 100);
var order2 = new OrderDto(1, "John", 100);
Console.WriteLine(order1 == order2); // true - value equality

// With-expressions - immutable updates
var updatedOrder = order1 with { Total = 150 };

2. Pattern Matching Advanced

public class PatternMatchingAdvanced
{
    // Property patterns
    public string AnalyzeProduct(Product product) =>
        product switch
    {
        { Price: > 1000, Category: "Electronics" } => "Premium Electronics",
        { Price: > 100, IsAvailable: true } => "Available Premium",
        { IsArchived: true } => "Archived",
        _ => "Standard"
    };

    // List patterns (C# 9+)
    public bool CheckPrices(int[] prices) =>
        prices switch
    {
        [100, 200, 300] => true,
        [var first, .. var rest] when rest.All(x => x > first) => true,
        [.., var last] when last > 1000 => true,
        _ => false
    };

    // Relational patterns
    public string CategorizeByPrice(Product product) =>
        product.Price switch
    {
        < 50 => "Cheap",
        >= 50 and < 200 => "Mid-range",
        >= 200 and < 1000 => "Expensive",
        >= 1000 => "Premium",
    };

    // Type patterns with guards
    public void ProcessEntity(object entity) =>
        _ = entity switch
    {
        Customer { Age: < 18 } cust => HandleMinor(cust),
        Customer { Age: >= 65 } cust => HandleSenior(cust),
        Product { Price: > 1000 } prod => HandlePremium(prod),
        Product => HandleRegularProduct(),
        _ => HandleUnknown()
    };
}

3. Source Generators

// Custom attribute for generation
[AttributeUsage(AttributeTargets.Class)]
public class GenerateToStringAttribute : Attribute { }

// Usage
[GenerateToString]
public partial class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

// Source generator generates at compile-time:
// public partial class User
// {
//     public override string ToString()
//         => $"User {{ Id = {Id}, Name = {Name}, Email = {Email} }}";
// }

4. Nullable Reference Types (Advanced)

#nullable enable

public class NullableAdvanced
{
    // Non-null by default
    public string Name { get; set; } // Cannot be null
    public string? Email { get; set; } // Can be null

    // Null-forgiving operator
    public void Process(string? input)
    {
        var length = input!.Length; // Compiler warning suppressed
    }

    // Null coalescing assignment
    public void EnsureValue()
    {
        Name ??= "Default";
    }

    // Pattern matching null checks
    public bool IsValid(User? user) =>
        user is not null && !string.IsNullOrEmpty(user.Email);
}

#nullable disable

5. Structural Types & Performance

// Struct for small data aggregates
public readonly struct CoordinateStruct
{
    public int X { get; }
    public int Y { get; }

    public CoordinateStruct(int x, int y)
    {
        X = x;
        Y = y;
    }
}

// Reference type for larger objects
public class CoordinateClass
{
    public int X { get; set; }
    public int Y { get; set; }
}

public class StructVsClassComparison
{
    public void PerformanceDemo()
    {
        // Struct - value type (usually stack)
        var structs = new CoordinateStruct[1000000];
        // ~8MB (2 ints * 1M)

        // Class - reference type (heap)
        var classes = new CoordinateClass[1000000];
        // ~8MB references + heap allocations
        // = Many MB more due to object overhead
    }
}

Production Patterns și Best Practices

1. Health Checks

public class HealthCheckPatterns
{
    // Register in Startup
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddHealthChecks()
            .AddDbContextCheck<AppDbContext>()
            .AddCheck<CustomHealthCheck>("custom");
    }

    // Custom health check
    public class DatabaseHealthCheck : IHealthCheck
    {
        private readonly AppDbContext _context;

        public async Task<HealthCheckResult> CheckHealthAsync(
            HealthCheckContext context,
            CancellationToken cancellationToken = default)
        {
            try
            {
                var canConnect = await _context.Database.CanConnectAsync(cancellationToken);

                if (!canConnect)
                    return HealthCheckResult.Unhealthy("Cannot connect to database");

                var query = await _context.Database
                    .ExecuteQueryRawAsync("SELECT 1", cancellationToken);

                return HealthCheckResult.Healthy("Database is healthy");
            }
            catch (Exception ex)
            {
                return HealthCheckResult.Unhealthy("Database error", ex);
            }
        }
    }
}

2. Circuit Breaker Pattern

public class CircuitBreakerPattern
{
    // Using Polly
    public class ResilientHttpClient
    {
        private readonly IAsyncPolicy<HttpResponseMessage> _policy;

        public ResilientHttpClient()
        {
            var retryPolicy = Policy
                .Handle<HttpRequestException>()
                .Or<TimeoutRejectedException>()
                .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
                .WaitAndRetryAsync(
                    retryCount: 3,
                    sleepDurationProvider: attempt =>
                        TimeSpan.FromSeconds(Math.Pow(2, attempt)));

            var circuitBreakerPolicy = Policy
                .Handle<HttpRequestException>()
                .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
                .CircuitBreakerAsync<HttpResponseMessage>(
                    handledEventsAllowedBeforeBreaking: 5,
                    durationOfBreak: TimeSpan.FromSeconds(30));

            _policy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
        }

        public async Task<T> GetAsync<T>(string url)
        {
            var response = await _policy.ExecuteAsync(() =>
                _client.GetAsync(url));

            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<T>(content);
        }
    }
}

3. Distributed Caching Strategy

public class DistributedCachingStrategy
{
    private readonly IDistributedCache _cache;

    // Cache-aside pattern
    public async Task<Product> GetProductAsync(int id)
    {
        var cacheKey = $"product_{id}";

        // Try get from cache
        var cached = await _cache.GetStringAsync(cacheKey);
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonConvert.DeserializeObject<Product>(cached);
        }

        // Get from database
        var product = await _context.Products.FindAsync(id);

        if (product != null)
        {
            // Store in cache
            await _cache.SetStringAsync(
                cacheKey,
                JsonConvert.SerializeObject(product),
                new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
                });
        }

        return product;
    }

    // Cache invalidation
    public async Task InvalidateProductAsync(int id)
    {
        var cacheKey = $"product_{id}";
        await _cache.RemoveAsync(cacheKey);
    }
}

4. Structured Logging

public class StructuredLoggingBestPractices
{
    private readonly ILogger<OrderService> _logger;

    public async Task ProcessOrderAsync(Order order)
    {
        using (_logger.BeginScope(
            new Dictionary<string, object>
            {
                { "OrderId", order.Id },
                { "CustomerId", order.CustomerId },
                { "CorrelationId", HttpContext.TraceIdentifier }
            }))
        {
            try
            {
                _logger.LogInformation(
                    "Starting order processing. Amount: {Amount}, Items: {ItemCount}",
                    order.Total,
                    order.Lines.Count);

                await ValidateOrderAsync(order);

                _logger.LogDebug("Order validation passed");

                await ProcessPaymentAsync(order);

                _logger.LogInformation("Order processed successfully");
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogError(
                    ex,
                    "Order processing failed. Reason: {Reason}",
                    ex.Message);

                throw;
            }
            catch (Exception ex)
            {
                _logger.LogCritical(
                    ex,
                    "Unexpected error during order processing");

                throw;
            }
        }
    }
}

5. Request/Response Middleware

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestResponseLoggingMiddleware> _logger;

    public RequestResponseLoggingMiddleware(RequestDelegate next, 
        ILogger<RequestResponseLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Capture request
        context.Request.EnableBuffering();
        var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
        context.Request.Body.Position = 0;

        _logger.LogInformation(
            "HTTP {Method} {Path} - Body: {Body}",
            context.Request.Method,
            context.Request.Path,
            requestBody);

        // Capture response
        var originalBodyStream = context.Response.Body;
        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;

            await _next(context);

            var response = await new StreamReader(context.Response.Body).ReadToEndAsync();
            context.Response.Body.Position = 0;

            _logger.LogInformation(
                "HTTP Response {StatusCode} - Body: {Body}",
                context.Response.StatusCode,
                response);

            await responseBody.CopyToAsync(originalBodyStream);
        }
    }
}

Concluzie - Diferențe Senior Developer

Un senior developer cu C# și .NET trebuie să:

  1. Performance First Mindset

    • Cunoaște GC behavior
    • Evită allocations inutile
    • Profile și optimize
  2. Architecture Thinking

    • CQRS, Event Sourcing
    • Microservices patterns
    • Distributed systems
  3. Production Ready Code

    • Error handling robust
    • Logging structured
    • Health checks și monitoring
  4. Database Mastery

    • Query optimization
    • Indexing strategies
    • Transaction handling
  5. Advanced Patterns

    • Multi-tenancy
    • Circuit breakers
    • Resilience patterns
  6. Mentoring Capability

    • Explică complex topics
    • Code review pe inalt nivel
    • Architecture decisions

Data: 2025 C# Version: 12 .NET Version: 8+