Showing posts with label .Net Core. Show all posts
Showing posts with label .Net Core. Show all posts

Sunday, November 9, 2025

Dotnet Core - Response Caching vs Output Caching

 ⚖️ Response Caching vs Output Caching in ASP.NET Core

AspectResponse CachingOutput Caching
PurposeInstructs clients (like browsers) and proxies to cache responsesCaches the actual server-generated response in memory (on the server)
Caching LocationClient-side (browser, CDN, proxy)Server-side (in ASP.NET Core memory or distributed cache)
ImplementationUses HTTP Cache-Control headers and middlewareUses the new OutputCache Middleware (ASP.NET Core 7+)
How it WorksAdds headers like Cache-Control, Vary, etc. → tells browser/CDN when to reuse responsesMiddleware saves response output → reuses for identical requests without re-running controller/action
ScopeWorks outside server (helps client cache responses)Works inside server (bypasses controller execution)
Configuration Examplecsharp\n[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]\ncsharp\napp.UseOutputCache();\n[OutputCache(Duration = 60)]\n
DependenciesRelies on clients or CDNs honoring headersFully managed by ASP.NET Core runtime
Use CaseStatic or semi-static responses (e.g., images, GET APIs)Heavy computation APIs, views, or controller actions with repeated requests
Introduced in.NET Core 1.0+.NET 7.0+
InvalidationControlled by headers (no-cache, max-age)Controlled programmatically (OutputCacheStore can evict or tag invalidations)


🧩 Example Scenarios

  • Response Caching

    • Browser caches a product list API for 60 seconds.

    • CDN serves cached version — no new request hits your API.

    • Great for read-heavy GET endpoints.

  • Output Caching

    • Server caches rendered HTML for /home/index or JSON for /api/dashboard.

    • Subsequent requests return cached result from memory — controller not even called.

    • Great for high CPU or DB-intensive endpoints.


🔧 Combined Use

You can combine both:

  • Server caches the response using Output Cache.

  • Sends ResponseCache headers to help client/CDN reuse it.

This provides multi-level caching — server + client.

Dotnet Core - Performance Tuning of Applications

 🚀 How to Improve Performance in ASP.NET Core Applications

Performance optimization in .NET Core is multi-layered, covering areas from code-level tuning to infrastructure scaling.
Here’s a structured breakdown:





🧱 1. Use Built-in Caching

🔹 Memory Cache (for small-scale, single-server)

builder.Services.AddMemoryCache(); public class ProductService { private readonly IMemoryCache _cache; public ProductService(IMemoryCache cache) => _cache = cache; public Product GetProduct(int id) { return _cache.GetOrCreate($"product_{id}", entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(5); return LoadProductFromDb(id); }); } }

🔹 Distributed Cache (for load-balanced apps)

Use Redis, SQL Server, or AWS ElastiCache.

builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost:6379"; });

✅ Benefit: Reduces DB hits and speeds up responses.


⚙️ 2. Enable Response Caching

Cache HTTP responses for static or infrequently changing data.

builder.Services.AddResponseCaching(); app.UseResponseCaching();
[ResponseCache(Duration = 60)] public IActionResult GetWeather() => Ok("Sunny");

✅ Benefit: Reuses responses instead of regenerating data.


⚡ 3. Use Asynchronous Code

ASP.NET Core is non-blocking. Always use async/await for I/O operations:

public async Task<IActionResult> GetProductsAsync() => Ok(await _db.Products.ToListAsync());

✅ Benefit: Frees up threads → handles more concurrent requests.


🧰 4. Optimize Entity Framework Core

  • Use AsNoTracking() for read-only queries.
  • Fetch only needed columns via Select().
  • Use compiled queries for hot paths.
  • Use connection pooling.

Example:

var products = await _context.Products .AsNoTracking() .Select(p => new { p.Id, p.Name }) .ToListAsync();

✅ Benefit: Reduces EF overhead and memory allocations.


🧩 5. Compression & Minification

Enable Response Compression middleware:

builder.Services.AddResponseCompression(); app.UseResponseCompression();

Compresses JSON, HTML, CSS, etc., reducing network load.


💾 6. Use Data Transfer Optimization

  • Use DTOs to avoid over fetching large models.
  • Paginate results.
  • Use gzip/brotli compression and HTTP/2.

🌐 7. Use CDN & Static File Optimization

Host static assets (JS, CSS, images) on a CDN like CloudFront.
Also, use cache headers:

app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => { ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=604800"); } });

🧮 8. Connection & Pooling

Use IHttpClientFactory for efficient, pooled HTTP calls:

builder.Services.AddHttpClient("MyClient");

Avoids socket exhaustion caused by frequent new HttpClient instances.


🧠 9. Reduce Middleware Overhead

Register only necessary middleware, and put frequently used ones first (e.g., authentication before routing).


🧩 10. Use Output Caching (ASP.NET Core 7+)

Output caching stores full HTTP responses:

builder.Services.AddOutputCache(); app.UseOutputCache();
[OutputCache(Duration = 60)] public IActionResult GetProducts() => Ok(Products);

✅ Much faster than recomputing each request.


☁️ 11. Use Cloud & Deployment Optimization (AWS Context)

On AWS, you can:

  • Use Elastic Beanstalk or ECS/EKS for autoscaling.
  • Add AWS ElastiCache for distributed caching.
  • Use AWS CloudFront CDN.
  • Add Application Load Balancer for traffic balancing.
  • Store assets in S3 instead of app server.

✅ You can also enable AOT (Ahead-of-Time Compilation) for startup performance improvements.


🧪 12. Profiling & Diagnostics

Use tools like:

  • dotnet-trace, dotnet-counters
  • Application Insights
  • MiniProfiler
  • AWS X-Ray (for distributed tracing)

✅ Identify slow methods, DB queries, or excessive GC pressure.


🧠 13. Memory Management

  • Use Span<T>, Memory<T> for high-performance code.
  • Avoid large object allocations.
  • Prefer pooled objects (ArrayPool<T>).
  • Release disposable resources properly.

✅ Summary Table

AreaTechniqueBenefit
CachingMemory, Redis, Output CacheReduce recomputation
Async I/Oasync/awaitHandle more requests
EF CoreAsNoTracking, ProjectionOptimize DB performance
CompressionGzip, BrotliReduce payload
MiddlewareOptimize pipelineReduce latency
CDN/StaticCache headersReduce load
DiagnosticsProfilers, LogsIdentify bottlenecks

💡Summary (Short Answer)

"In ASP.NET Core, I improve performance using multi-level caching (in-memory, distributed, and output), async programming, optimized EF Core queries, response compression, and CDN-based static content delivery. I also use IHttpClientFactory for efficient HTTP calls, limit middleware overhead, and profile apps with Application Insights or AWS X-Ray to find hot spots. In cloud deployments, I leverage autoscaling and caching via AWS ElastiCache and CloudFront."

Saturday, November 8, 2025

Dotnet Core - DI(Dependency Injection) vs DIP(Dependency Inversion Principle)

 🧩 The “D” in SOLID — Dependency Inversion Principle (DIP)

Dependency Inversion Principle (DIP) is the design principle behind the idea of Dependency Injection.




🧠 What DIP States

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

In simpler words:

  • Instead of classes depending on concrete implementations, they should depend on interfaces or abstract classes.
  • This makes the system more flexible and loosely coupled.


🧱 Example Without DIP (Bad Design)

public class NotificationService { private EmailService _emailService = new EmailService(); public void Notify(string message) { _emailService.SendEmail(message); } } public class EmailService { public void SendEmail(string message) { Console.WriteLine($"Email sent: {message}"); } }

Problem:

  • NotificationService depends directly on a concrete EmailService.
  • If you want to switch to SmsService, you must modify NotificationService.


✅ Example With DIP (Good Design)

public interface IMessageService { void Send(string message); } public class EmailService : IMessageService { public void Send(string message) => Console.WriteLine($"Email: {message}"); } public class SmsService : IMessageService { public void Send(string message) => Console.WriteLine($"SMS: {message}"); } public class NotificationService { private readonly IMessageService _messageService; public NotificationService(IMessageService messageService) { _messageService = messageService; } public void Notify(string message) { _messageService.Send(message); } }

Now:

  • NotificationService depends on the abstraction IMessageService.
  • You can easily switch between implementations (Email, SMS, Push).

✅ This is Dependency Inversion Principle in action.


🧩 How Dependency Injection (DI) Fits In

Now that we understand DIP as a principle, here’s where DI comes in:

ConceptWhat it is
DIP (Principle)A guideline that says “depend on abstractions, not concretions.”
DI (Pattern)A technique or mechanism to implement DIP in code.

🔄 Relationship Between DIP and DI

Let’s visualize it:

DIP → is the principle (what you should do) DI → is the method (how you do it)

So:

  • DIP is about how classes should relate to each other (architectural rule).
  • DI is about how to supply those dependencies (technical mechanism).


🧰 Example: Applying DI to Implement DIP

In .NET Core:

var builder = WebApplication.CreateBuilder(args); builder.Services.AddScoped<IMessageService, EmailService>(); builder.Services.AddScoped<NotificationService>(); var app = builder.Build();

  • Here, DI container ensures NotificationService receives an implementation of IMessageService.
  • That’s Dependency Injection in practice.
  • And since both classes depend on abstractions, that’s Dependency Inversion Principle applied.

🧠 Summary Comparison

AspectDependency Inversion Principle (DIP)Dependency Injection (DI)
TypePrinciple (part of SOLID)Design Pattern / Technique
PurposeTo reduce coupling between high-level and low-level modulesTo supply dependencies to classes at runtime
FocusArchitecture & relationships between classesImplementation & object creation
Enforced ByDeveloper’s designFramework or DI container
In .NET CoreInterfaces like IService, IRepositoryAddScoped, AddSingleton, AddTransient

💬 Analogy

ConceptAnalogy
DIP“Don’t depend on specific brands; depend on a plug standard.”
DI“Use a socket that provides power to any plug that fits.”

🧾 Quick Recap

✅ DIP (Dependency Inversion Principle) = design principle (the “D” in SOLID)
✅ DI (Dependency Injection) = pattern / implementation of that principle
💡 DIP gives the rule, DI provides the tool to achieve it

Friday, November 7, 2025

Dotnet Core - Dependency Injection (DI)

 🧩 What is Dependency Injection (DI)?

Dependency Injection (DI) is a design pattern used to achieve loose coupling between classes and their dependencies.

In simple terms:

Instead of a class creating its own dependencies, they are provided (injected) from the outside — typically by a framework or container.

 




🧠 Example Without DI (Tight Coupling)

public class NotificationService { private readonly EmailService _emailService = new EmailService(); public void Notify(string message) { _emailService.Send(message); } }

Here:

  • NotificationService directly depends on EmailService.
  • If we want to replace EmailService with, say, SmsService, we must change the code inside NotificationService.


✅ Example With DI (Loose Coupling)

public interface IMessageService { void Send(string message); } public class EmailService : IMessageService { public void Send(string message) => Console.WriteLine($"Email: {message}"); } public class NotificationService { private readonly IMessageService _messageService; // Dependency is injected via constructor public NotificationService(IMessageService messageService) { _messageService = messageService; } public void Notify(string message) { _messageService.Send(message); } }

Now you can plug in any implementation (Email, SMS, Push) without changing NotificationService.


🧱 Dependency Injection in .NET Core

.NET Core has built-in support for Dependency Injection via the Microsoft.Extensions.DependencyInjection namespace.

You configure DI inside the Program.cs or Startup.cs (depending on .NET version).


🧰 Typical .NET Core DI Setup

Example — in Program.cs (.NET 6/7/8 minimal hosting model)

var builder = WebApplication.CreateBuilder(args);

// 1️⃣ Register services in DI container builder.Services.AddScoped<IMessageService, EmailService>(); builder.Services.AddScoped<NotificationService>(); var app = builder.Build(); // 2️⃣ Resolve and use service app.MapGet("/notify", (NotificationService service) => { service.Notify("Hello, .NET Core DI!"); return "Notification Sent!"; }); app.Run();

🔍 How DI Works in .NET Core

When you run the app:

  1. The service container is created by WebApplication.CreateBuilder.

  2. Services are registered using builder.Services.Add...().

  3. When a class (e.g., controller, page, or middleware) requests a dependency, the DI container creates and injects it automatically.


🔄 Service Lifetimes

LifetimeDescriptionUse Case
TransientNew instance created every time it’s requestedLightweight, stateless services
ScopedOne instance per request (HTTP scope)Web request-specific services
SingletonSingle instance for the entire app lifetimeConfiguration, caching, logging

Example:

builder.Services.AddTransient<IEmailService, EmailService>(); builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddSingleton<ILoggingService, LoggingService>();

🧠 DI Example in ASP.NET Core Controller

public interface ICustomerService { IEnumerable<string> GetCustomers(); } public class CustomerService : ICustomerService { public IEnumerable<string> GetCustomers() => new[] { "John", "Jane" }; } [ApiController] [Route("[controller]")] public class CustomerController : ControllerBase { private readonly ICustomerService _service; // Dependency is injected automatically public CustomerController(ICustomerService service) { _service = service; } [HttpGet] public IActionResult Get() { return Ok(_service.GetCustomers()); } }

✅ ASP.NET Core automatically injects CustomerService because it was registered in the DI container.


⚙️ Advanced DI Topics

1️⃣ Constructor Injection (most common)

Dependencies passed via constructor.

2️⃣ Method Injection

Dependencies passed as method parameters.

public void SendNotification([FromServices] IMessageService service) { service.Send("Message"); }

3️⃣ Property Injection

Dependency assigned via property (not common in .NET Core DI by default).


🔩 Built-in DI Container Features

  • Open Generics Support
    builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

  • Service Replacement:  Simply remove MyService registration with IMyService and add new registration MyMockService with IMyService.
    This is useful when you are using some library and that has registered IMyService with MyService, so now Replace statement will replace all previous registration with new one.

    builder.Services.AddSingleton<IMyService, MyService>();
    builder.Services.Replace(ServiceDescriptor.Singleton<IMyService, MyMockService>());

  • Configuration + Options Pattern
    builder.Services.Configure<MyConfig>(builder.Configuration.GetSection("MySection"));


🧰 DI in Non-Web .NET Apps

You can also use DI in:

  • Console apps
  • Worker services
  • Background jobs

Example:

var services = new ServiceCollection(); services.AddScoped<IMessageService, EmailService>(); var provider = services.BuildServiceProvider(); var messageService = provider.GetRequiredService<IMessageService>(); messageService.Send("Hello from Console App!");

💡 Benefits of Using DI

BenefitDescription
Loose CouplingEasier to swap implementations
TestabilityUse mocks in unit tests easily
ReusabilityServices can be reused across components
MaintainabilityReduces hard-coded dependencies
ScalabilityCentralized lifecycle management

⚠️ Common Pitfalls

MistakeImpact
Registering service with wrong lifetimeMemory leaks or inconsistent behavior
Creating new instances manually inside serviceBreaks DI chain
Overusing SingletonCan cause thread-safety issues
Circular dependenciesTwo services depend on each other — runtime error

🧾 Summary

AspectDetails
PatternDependency Injection (DI)
Built-in ContainerMicrosoft.Extensions.DependencyInjection
Registration MethodsAddSingleton, AddScoped, AddTransient
GoalLoose coupling, better testability, and flexibility
Injection MethodsConstructor (primary), Method, Property
Used InASP.NET Core, Worker Services, Blazor, Console Apps

Dotnet Core - CoreCLR Vs NativeAOT Runtimes

When you compile with NativeAOT (PublishAot=true), your application does not use CoreCLR at all at runtime.

Instead, it uses a minimal, statically linked runtime that’s fundamentally different from CoreCLR, that is a lightweight static runtime called the NativeAOT Runtime (derived from the old CoreRT project)


🔍 How it works conceptually

When you publish a .NET app normally:

Standard .NET (JIT/CoreCLR path):

Your C# code ↓ (compiled by Roslyn) Intermediate Language (IL) ↓ (executed by CoreCLR at runtime) CoreCLR JIT compiles IL → Native code (on the fly)

When you publish using NativeAOT, the process changes drastically:

NativeAOT (AOT path):

Your C# code ↓ (compiled by Roslyn) Intermediate Language (IL) ↓ (compiled AOT by crossgen2) Native Machine Code (.exe) ↓ Executed directly by OS — No CoreCLR, no JIT





⚙️ What replaces CoreCLR in NativeAOT?

Instead of the CoreCLR runtime, NativeAOT uses a lightweight static runtime called the NativeAOT Runtime (derived from the old CoreRT project).

This runtime:

  • Includes a minimal GC, type system, and exception handling system.
  • Does not include JIT compilation logic.
  • Does not support full reflection or dynamic assembly loading.

It’s statically linked into the final executable, so the entire runtime becomes part of your .exe file.


🧠 Deeper Comparison: CoreCLR vs NativeAOT Runtime

FeatureCoreCLRNativeAOT Runtime
JIT Compiler✅ Present (RyuJIT)❌ Not included
GC (Garbage Collector)✅ Full-featured, generational✅ Simplified GC (same core concepts, smaller)
Type Loader✅ Dynamic✅ Static (known at build time)
Reflection✅ Full⚠️ Limited (metadata stripped)
Dynamic Assembly Load✅ Supported❌ Not supported
Code Generation at Runtime (Emit)✅ Supported❌ Not supported
Startup Time⚡ Moderate⚡⚡ Super fast
Binary Size🧱 Larger💡 Smaller
Runtime Required.NET runtime (CoreCLR)None — self-contained
Use CasesGeneral-purpose appsCloud, microservices, serverless, CLI tools

🧮 Example Build Flow

🔸 CoreCLR-based publish:

dotnet publish -r linux-x64 -c Release

Produces:

  • app.dll

  • dotnet host loads CoreCLR runtime

  • JIT compiles IL on startup

🔸 NativeAOT publish:

dotnet publish -r linux-x64 -c Release /p:PublishAot=true

Produces:

  • A single app executable

  • Contains native code + stripped-down runtime

  • Runs directly via ./app (no dotnet needed)


🧩 Why this matters

✅ Advantages:
  • Blazing-fast cold starts (critical for AWS Lambda, Azure Functions, CLI tools)
  • No runtime dependency (great for container deployment)
  • Reduced attack surface (no JIT, less metadata)
⚠️ Trade-offs:
  • No runtime code generation (System.Reflection.Emit)
  • Limited dynamic loading
  • Reflection-heavy frameworks (like some ORMs or JSON serializers) need trimming-safe versions

💡 Internal Implementation Hint

In .NET 7/8, the AOT toolchain (crossgen2) builds an object file (.obj) that includes:

  • Compiled IL → native code

  • Static GC and metadata tables

  • Lightweight runtime startup code (from NativeAOT runtime)

Then it uses the system’s C++ linker to produce a final executable:

App.obj + NativeAOT runtime libs → FinalApp.exe

So the “runtime” is baked in, not “loaded dynamically” like CoreCLR.


🔧 Analogy

ModeAnalogy
CoreCLR (JIT)Like a chef who cooks each dish when ordered — flexible, but slower startup.
R2R (Partial AOT)Chef preps ingredients ahead of time — faster serving.
NativeAOT (Full AOT)Meals fully cooked, sealed, and ready to serve — instant serving, no kitchen needed.

🧾 Summary

AspectCoreCLR (.NET Runtime)NativeAOT
Uses JIT?✅ Yes❌ No
Runtime EngineCoreCLRNativeAOT runtime (based on CoreRT)
Startup Speed⚡ Moderate⚡⚡ Instant
Reflection Support✅ Full⚠️ Limited
Runtime Size🧱 Larger💡 Small and static
OutputIL + runtimeNative binary
Target ScenariosGeneral apps, APIsCloud-native, microservices, serverless, tools

Thursday, November 6, 2025

Dotnet Core - AOT ("Ahead of Time" compilation)

 What is AOT (Ahead-of-Time) Compilation?

AOT (Ahead-of-Time) compilation means compiling your .NET code into native machine code before the application runs, instead of at runtime.

Normally, .NET applications are JIT-compiled (Just-In-Time) — the CLR (Common Language Runtime) compiles IL (Intermediate Language) into machine code as the program executes.

With AOT, this process happens at build or publish time, so when you run your app, it executes directly as native code, without requiring a JIT compiler at runtime.


Difference Between JIT and AOT

FeatureJIT (Just-In-Time)AOT (Ahead-of-Time)
When compilation happensAt runtimeBefore runtime (during build/publish)
Startup timeSlower (needs to compile IL)Much faster (already native code)
Memory usageHigher (stores IL + JIT cache)Lower
PortabilityRequires .NET runtime on target machineSelf-contained executable
Binary sizeSmaller IL assembliesLarger native binaries
PerformanceOptimized per platform at runtimeOptimized in advance, faster startup
DebuggingEasierLimited (no reflection emit, dynamic compilation)

How AOT Works in .NET

When you build your app using AOT, the compiler:

  1. Takes your C# → IL (Intermediate Language).

  2. Then uses NativeAOT (built into .NET SDK) to convert IL → machine code.

  3. Produces a native executable (e.g., .exe on Windows, ELF on Linux).


Why AOT is Useful

1. Faster startup time
Critical for microservices, serverless (like AWS Lambda), and desktop/mobile apps.

2. No need for .NET runtime on target
  • Useful when deploying to restricted environments or containers.
  • You can distribute a fully self-contained native app.
3. Better performance for short-lived processes
Great for Lambda functions or CLI tools, where cold start matters.

4. Smaller runtime footprint
Only necessary parts of the .NET runtime are included.


Example — Using AOT in .NET 8

1️⃣ Normal Publish (JIT)

dotnet publish -c Release

Output: IL-based app requiring dotnet runtime.

2️⃣ Native AOT Publish

dotnet publish -c Release -r win-x64 -p:PublishAot=true

Output:

  • A single native executable, e.g., MyApp.exe

  • No .dll files or dotnet runtime needed

  • Starts instantly


Example in a .NET 8 Project File (.csproj)

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <PublishAot>true</PublishAot> </PropertyGroup> </Project>

Then run:

dotnet publish -c Release

Example Scenarios Where AOT Is Great

ScenarioBenefit of AOT
AWS Lambda (serverless)Reduces cold start latency significantly
MicroservicesFaster container start-up and smaller images
CLI toolsLightweight, standalone binaries
Desktop appsNo dependency on installed .NET runtime
IoT / edge devicesLimited memory, needs fast startup

Limitations / Trade-offs

While AOT gives performance and portability, it has some caveats:

LimitationExplanation
Reflection limitationsAOT doesn’t support runtime code generation or heavy reflection (System.Reflection.Emit)
Larger binary sizeIncludes all required runtime dependencies
Slower build timeNative compilation takes longer
Debugging harderHarder to inspect IL or runtime behavior

Related Technologies

TechnologyDescription
ReadyToRun (R2R)Partial AOT (used by default in .NET). Compiles IL → native code ahead of time, but still needs JIT at runtime for some parts.
NativeAOTFull AOT; produces a single native binary (new in .NET 7+).
CoreRTEarly prototype of NativeAOT (now merged into .NET SDK).

Real-World Example — AWS Lambda + .NET AOT

AWS Lambda cold starts can be slow for .NET apps because of JIT compilation.
With .NET 7/8 Native AOT, you can build native binaries that load in milliseconds.

dotnet publish -r linux-x64 -c Release -p:PublishAot=true

Then deploy the generated binary to Lambda — startup time can drop by up to 80–90%.


 Summary

AspectDescription
Full FormAhead-of-Time Compilation
Introduced.NET 7+ (built-in NativeAOT support)
GoalPrecompile IL → native machine code for faster startup & smaller footprint
Usagedotnet publish -p:PublishAot=true
Best ForServerless, Microservices, CLI tools, Edge computing
Trade-offsLimited reflection, longer build time, larger binaries

CI/CD - Safe DB Changes/Migrations

Safe DB Migrations means updating your database schema without breaking the running application and without downtime . In real systems (A...