Monday, 27 October 2025

Async Errors That can Destroy an Application Built with ASP.NET Core

Leave a Comment

Ten async errors that can destroy an ASP.NET Core application will be covered in this post. Before we begin, please read my previous article.

Let's get started.

1. Blocking on Async Code (.Result, .Wait())

Mistake

var data = httpClient.GetStringAsync(url).Result;  //

Problem: This blocks the thread while waiting for the async task, potentially causing thread starvation and deadlocks under load.

Fix

var data = await httpClient.GetStringAsync(url);  // 

Always use await all the way down the call chain.

2. Mixing Sync and Async Code

Mistake

public IActionResult GetData()
{
    var data = GetDataAsync().Result;
    return Ok(data);
}

Problem: ASP.NET Core uses an async pipeline. Blocking calls in controllers defeats the purpose of async I/O and can freeze requests.

Fix

public async Task<IActionResult> GetData()
{
    var data = await GetDataAsync();
    return Ok(data);
}

3. Not Using ConfigureAwait(false) in Libraries

Mistake

If you write a reusable library that uses async/await, but you rely on the synchronization context:

await SomeOperationAsync();  // 

Problem: In ASP.NET Core it’s less critical (no SynchronizationContext), but in shared code or desktop apps, it can cause context-capturing issues.

Fix

await SomeOperationAsync().ConfigureAwait(false);  // 

4. Fire-and-Forget Tasks

Mistake

Task.Run(() => DoSomethingAsync());  // 

Problem: The task is unobserved — if it throws, the exception is lost or crashes the process.

Fix

If you must run background work:

  • Use IHostedService or BackgroundService.

  • Or handle the task safely

_ = Task.Run(async () =>
{
    try { await DoSomethingAsync(); }
    catch (Exception ex) { _logger.LogError(ex, "Background error"); }
});

5. Over-Awaiting Small Tasks (Async Overhead)

Mistake

Making everything async “just because”:

public async Task<int> AddAsync(int a, int b)
{
    return a + b; // ❌ no real async work
}

Problem: Adds overhead for no reason. Async has context-switch costs.

Fix: Keep it synchronous when no real async I/O is performed.

6. Creating Too Many HttpClient Instances

Mistake

var client = new HttpClient();
 var response = await client.GetAsync(url);

Problem: Causes socket exhaustion and memory leaks.

Fix: Use IHttpClientFactory:

public MyService(HttpClient httpClient)
{
    _httpClient = httpClient;
}

Register with:

services.AddHttpClient<MyService>();

7. Using Task.Run to “Make” Things Async

Mistake

var result = await Task.Run(() => SomeBlockingDatabaseCall());

Problem: Moves blocking code off-thread but doesn’t solve scalability — wastes thread pool threads.

Fix: Make the underlying operation truly async (e.g., EF Core’s ToListAsync())

8. Ignoring Cancellation Tokens

Mistake

public async Task ProcessRequest()
{
    await Task.Delay(5000);
}

Problem: Ignores request cancellation (like when a client disconnects).

Fix

public async Task ProcessRequest(CancellationToken token)
{
    await Task.Delay(5000, token);
}

Always respect HttpContext.RequestAborted.

9. Unobserved Task Exceptions

Mistake

_ = SomeAsyncOperation();  // exception may crash process

Problem: Exceptions in async methods not awaited can bring down your app.

Fix

Always await tasks or handle their exceptions with care.

10. Not Profiling or Measuring Async Performance

Mistake

Assuming async = faster.

Problem: Async helps scalability, not necessarily speed. Excessive async overhead can slow CPU-bound paths.

Fix: Measure using:

  • dotnet-trace

  • Application Insights

  • PerfView

  • BenchmarkDotNet

Bonus Tip: Always “async all the way”

If you start with an async method (like a controller action), propagate async through the entire call chain. Mixing sync/async is the #1 killer.

Conclusion

Here, we tried to cover async mistakes that can kill an ASP.NET Core application.

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 9.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFE.eu, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFE guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.

Read More...

Tuesday, 21 October 2025

ASP.NET Core Web API Real-Time Cache Monitoring and Alerting Using NCache

Leave a Comment

Caching is essential for enhancing responsiveness and scalability in high-performance enterprise applications. Dispersed caching relieves database strain and increases throughput while handling heavy transaction loads or frequent data access. However, real-time monitoring and alerts become essential for stability and performance visibility when your cache expands and supports more APIs or microservices.



NCache, a potent open-source distributed cache for.NET, can help with it. In addition to speeding up your apps, it offers dashboards, real-time monitoring, and performance and cache health notifications.

We will create an ASP.NET Core Web API that is integrated with NCache and set up real-time cache monitoring and alerting in this post.

What is NCache?

NCache is a 100% native .NET distributed caching solution by Alachisoft. It provides:

  • High availability through replication and partitioning

  • In-memory data storage for low latency

  • Real-time monitoring and management

  • Built-in notifications and alerts

It’s widely used in .NET Core microservices, Web APIs, SignalR, and Azure/AWS deployments.

Why Monitor Cache in Real-Time?

Real-time cache monitoring helps you:

  • Detect cache node failures or disconnections immediately

  • Track hit/miss ratios to optimize caching logic

  • Identify memory pressure or expired items

  • Receive alerts before performance degradation impacts users

Setting Up the ASP.NET Core Web API

Let’s create a new ASP.NET Core project and integrate NCache.

Step 1. Create the Project
dotnet new webapi -n NCacheMonitoringDemo
cd NCacheMonitoringDemo
Step 2. Add Required Packages

Install the following NuGet packages:

dotnet add package Alachisoft.NCache.SDK
dotnet add package Alachisoft.NCache.SessionState
dotnet add package Alachisoft.NCache.Runtime
Step 3. Configure NCache in Program.cs

Here’s how you configure NCache in your .NET 8 Web API:

using Alachisoft.NCache.Client;
using Alachisoft.NCache.Runtime.Caching;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

// Configure NCache
builder.Services.AddSingleton<ICache>(provider =>
{
    string cacheName = "demoClusteredCache";
    var cache = CacheManager.GetCache(cacheName);
    return cache;
});

var app = builder.Build();
app.MapControllers();
app.Run();

This connects your API to a clustered cache named demoClusteredCache running on your NCache server(s).

Step 4. Using the Cache in Your Controller

Let’s create a simple way ProductController to interact with NCache.

using Microsoft.AspNetCore.Mvc;
using Alachisoft.NCache.Client;
using Alachisoft.NCache.Runtime.Caching;

namespace NCacheMonitoringDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly ICache _cache;

        public ProductController(ICache cache)
        {
            _cache = cache;
        }

        [HttpPost]
        public IActionResult AddProduct([FromBody] Product product)
        {
            _cache.Insert(product.Id.ToString(), product);
            return Ok("Product cached successfully!");
        }

        [HttpGet("{id}")]
        public IActionResult GetProduct(string id)
        {
            var product = _cache.Get<Product>(id);
            if (product == null)
                return NotFound("Product not found in cache!");

            return Ok(product);
        }
    }

    public record Product(string Id, string Name, decimal Price);
}

Now, whenever you call POST /api/product, your data gets cached in NCache, and can be retrieved with GET /api/product/{id}.

Step 5. Enabling Real-Time Cache Monitoring

NCache provides a web-based monitoring dashboard called NCache Web Manager, as well as a PowerShell-based monitoring tool.

Option 1: Use NCache Web Manager

  • Launch NCache Web Manager (usually at http://localhost:8251).

  • Navigate to Monitoring > Clustered Cache.

  • Select your cache (e.g., demoClusteredCache).

  • You’ll see live graphs for:

    • Cache Size

    • Requests per second

    • Cache Hits vs Misses

    • Client Connections

    • Server Health

Step 6. Configure Alerts and Notifications

You can configure email, SMS, or webhook-based alerts for cache events such as:

  • Server node down

  • Cache full or eviction started

  • High CPU/Memory usage

  • Client disconnection

Example PowerShell command to add an alert:

Add-Alert -CacheName demoClusteredCache `
  -EventType "NodeStopped" `
  -Action "Email" `
  -Recipients "[email protected]"
PowerShell

Or configure it through NCache Manager UI → Alerts → Add New Alert.

Step 7. Programmatic Event Notifications

NCache also supports runtime event subscriptions from your .NET code.

Example: Receive notification when a cache item is removed or updated.

_cache.MessagingService.RegisterCacheNotification("product-101",
    (key, eventType) =>
    {
        Console.WriteLine($"Cache key '{key}' was {eventType}");
    },
    EventType.ItemRemoved | EventType.ItemUpdated);

This is especially useful for synchronizing distributed APIs or invalidating dependent data in real-time.

Step 8. Visualizing Metrics in Grafana or Prometheus (Optional)

In order to integrate with observability technologies such as Prometheus, Grafana, or Azure Monitor, NCache provides a metrics API.  To see trends in cache health, simply connect your monitoring platform and enable metrics in your NCache setup.

 

Best Practices for Cache Monitoring
AreaRecommendation
Cache SizeAlways monitor memory growth and eviction count
Hit RatioMaintain 85–90% hit rate for efficiency
AlertsSet threshold-based alerts for CPU, memory, and node status
FailoverAlways run at least two cache servers for high availability
SecurityRestrict monitoring access using NCache roles

Conclusion
You can get enterprise-grade monitoring and alerting in addition to a high-performance caching layer by integrating NCache with your ASP.NET Core Web API. Improved user experience, seamless scaling, and preemptive problem identification are all made possible by real-time cache health awareness.

With minimal configuration, you can:

  1. Cache data in-memory for faster access

  2. Track live performance metrics

  3. Get alerts before downtime occurs

  4. Keep your distributed systems stable and predictable

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 9.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFE.eu, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFE guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.

Read More...

Monday, 13 October 2025

Using DataView in C# to Filter Rows in a DataTable

Leave a Comment

Working with DataTable objects in C# frequently requires dynamic data filtering without changing the underlying table. This is why the DataView class was created. You can effectively sort, filter, and search rows with its view of a data table.

 
Understanding the Scenario

Consider you have a DataTable called dt containing columns like:

  • Symbol

  • Category

  • opendatetime

  • closedatetime

  • FloorPrice

  • CeilingPrice

  • asbanonasba

You want to filter rows where a specific column matches a value.

Using DataView to Filter Rows
else
{
    // Create a DataView from the DataTable
    DataView dv = dt.DefaultView;

    // Apply a filter condition
    dv.RowFilter = "asbanonasba = '1'";

    // Convert the filtered view back to a DataTable
    DataTable dts = dv.ToTable();
}
Step-by-Step Explanation
  1. Create a DataView

DataView dv = dt.DefaultView;
  • DefaultView creates a DataView object linked to the DataTable.

  • Changes in the DataView do not modify the original DataTable.

  1. Set the RowFilter

dv.RowFilter = "asbanonasba = '1'";
  • RowFilter is similar to a SQL WHERE clause.

  • Syntax

"ColumnName operator value"
  • Examples

dv.RowFilter = "Category = 'IND'";         // Filter by text
dv.RowFilter = "FloorPrice > 100";         // Filter numeric values
dv.RowFilter = "opendatetime >= '2025-10-01'";  // Filter dates
  1. Convert the Filtered View Back to a DataTable

DataTable dts = dv.ToTable();
  • dv.ToTable() creates a new DataTable containing only the rows that satisfy the RowFilter.

  • You can now use dts for binding to UI controls, exporting, or further processing.

Optional: Selecting Specific Columns and Distinct Rows

You can also select only certain columns or remove duplicates using the overloaded ToTable method:

DataTable distinctTable = dv.ToTable(
    true,                           // distinct rows only
    "Symbol", "Category", "FloorPrice"  // columns to include
);
Practical Example

Suppose your DataTable has the following data:

SymbolCategoryasbanonasba
ABCIND1
DEFIND0
XYZFMCG1

Filtering with

dv.RowFilter = "asbanonasba = '1'";

The resulting DataTable will contain:

SymbolCategoryasbanonasba
ABCIND1
XYZFMCG1

Tips and Best Practices
  1. Always check for column names in the filter string — typos will throw exceptions.

  2. String values must be enclosed in single quotes ' '.

  3. DateTime values must be in MM/dd/yyyy format or use # in US format:

dv.RowFilter = "opendatetime >= #10/01/2025#";
  1. For numeric columns, quotes are not needed:

dv.RowFilter = "CeilingPrice >= 500";
  1. DataView vs LINQ: DataView is faster for UI-bound tables; LINQ to DataSet is more flexible for complex queries.

Conclusion

Using DataView and its RowFilter property is a simple and effective way to filter rows in a DataTable without modifying the original data. It’s particularly useful for binding filtered data to grids, reports, or exporting results.

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 9.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFE.eu, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFEASP.NET guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.

Read More...

Wednesday, 8 October 2025

Support for Cross-Platforms in ASP.NET Core

Leave a Comment

ASP.NET Core is a revolutionary web framework from Microsoft that enables developers to build high-performance, modern web applications that can run on Windows, Linux, and macOS . Its cross-platform support is one of the most transformative aspects of the framework, allowing developers to create applications that are platform-independent, lightweight, and cloud-ready. Let’s explore how ASP.NET Core achieves this level of cross-platform compatibility, its architecture, tools, and examples of how developers can leverage it to build and deploy apps seamlessly across different operating systems.

The Evolution Towards Cross-Platform

Traditionally, ASP.NET applications were tied to the Windows operating system and the .NET Framework, limiting flexibility in deployment and hosting options. This changed with the introduction of .NET Core (now unified under the .NET platform ).

ASP.NET Core was rewritten from scratch to be:

  • Modular and lightweight

  • Open-source and community-driven

  • Platform-independent

  • Cloud-optimized

This means developers can now build once and deploy anywhere — whether on Windows servers, Linux containers, or macOS environments.

2. Cross-Platform Architecture

ASP.NET Core achieves platform independence through a layered architecture :

  • .NET Runtime: Executes code on different OSes.

  • .NET SDK: Provides build and deployment tools for all platforms.

  • Kestrel Web Server: A lightweight, cross-platform web server included by default.

  • Integration with Nginx/Apache: Enables hosting on Linux systems with reverse proxies.

Here’s a simple visual representation:

+---------------------------+
|       ASP.NET Core        |
+---------------------------+
|     .NET Runtime Layer    |
+---------------------------+
|     SDK & CLI Tools       |
+---------------------------+
| Hosting: IIS | Nginx | Kestrel |
+---------------------------+
| Windows | Linux | macOS |
+---------------------------+

This structure ensures that your ASP.NET Core application can run smoothly on any OS that supports the .NET runtime.

3. Cross-Platform Command-Line Tools

One of the key enablers of ASP.NET Core’s cross-platform development is the .NET CLI (Command Line Interface). It allows developers to create, build, run, and publish applications directly from the terminal on any operating system.

Example Commands

Create a new ASP.NET Core Web App

dotnet new webapp -o MyWebApp

Run the application

cd MyWebApp
dotnet run

Publish for Linux

dotnet publish -c Release -r linux-x64 --self-contained

These commands behave identically on Windows, macOS, or Linux , showcasing the framework’s uniformity.

4. Writing a Cross-Platform ASP.NET Core Application

Here’s an example of a simple ASP.NET Core Web API that runs seamlessly on any platform.

Program.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello, ASP.NET Core Cross-Platform World!");

app.Run();
Run on Windows
dotnet run

Output:

Now listening on: http://localhost:5000
Run on Linux or macOS

Same command, same output.
You can test it by visiting:
http://localhost:5000 in any browser.

This example demonstrates that no OS-specific changes are required. The same source code runs identically everywhere.

5. Cross-Platform Hosting and Deployment

ASP.NET Core applications can be hosted using multiple approaches:

a. Kestrel Web Server

Kestrel is a built-in, cross-platform web server that serves as the backbone for hosting ASP.NET Core apps. It’s fast, lightweight, and designed for both development and production.

Example appsettings.json configuration:

{"Kestrel": {
"Endpoints": {
  "Http": {
    "Url": "http://0.0.0.0:5000"
  }
}}}
b. Reverse Proxy with Nginx (Linux)

You can host ASP.NET Core apps on Linux with Nginx as a reverse proxy to Kestrel.

Example Nginx Configuration:

server {
    listen 80;
    server_name myapp.com;

    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

This setup allows you to take advantage of Linux’s stability and performance while running ASP.NET Core.

6. Self-Contained and Framework-Dependent Deployments

ASP.NET Core supports two major deployment models, enhancing its cross-platform flexibility:

Framework-Dependent Deployment (FDD)

The application uses the system-wide installed .NET runtime.

dotnet publish -c Release

Self-Contained Deployment (SCD)

The .NET runtime and all dependencies are bundled with your app, allowing it to run even on systems without .NET installed.

dotnet publish -c Release -r linux-x64 --self-contained true

This approach is ideal for Linux servers or containerized environments.

7. Cross-Platform Development Environments

You can build ASP.NET Core apps using a variety of tools on different platforms:

Operating SystemIDE/Editor OptionsExample
WindowsVisual Studio, VS Code, RiderFull feature-rich experience
macOSVisual Studio for Mac, VS Code, RiderDebugging, testing, publishing
LinuxVisual Studio Code, Rider, CLI toolsLightweight and efficient

For instance, on Linux , you can install .NET SDK and start developing directly from the terminal:

sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0

Then use:

dotnet new mvc -o CrossPlatformApp
cd CrossPlatformApp
dotnet run

8. Cross-Platform Testing and CI/CD

ASP.NET Core integrates smoothly with cross-platform CI/CD pipelines using tools like:

  • GitHub Actions

  • Azure DevOps

  • Jenkins

  • GitLab CI

Example GitHub Action Workflow

name: Build and Test ASP.NET Core App

on: [push]

jobs:build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x
      - name: Restore dependencies
        run: dotnet restore
      - name: Build
        run: dotnet build --configuration Release
      - name: Test
        run: dotnet test --no-build --verbosity normal

This configuration builds and tests your ASP.NET Core app on Linux (Ubuntu), but can just as easily run on Windows or macOS agents — truly cross-platform automation.

9. Containers and Cloud Deployments

ASP.NET Core’s portability makes it perfect for containerized and cloud-native applications.

Dockerfile Example

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyWebApp.dll"]

You can build and run it on any platform that supports Docker:

docker build -t mywebapp .
docker run -d -p 8080:80 mywebapp

This container can then be deployed to Azure , AWS , or Google Cloud , independent of the underlying OS.

10. Benefits of Cross-Platform ASP.NET Core

  • Freedom of Choice: Use any OS, IDE, or cloud provider.

  • Cost Savings: Host apps on Linux servers, which are often cheaper.

  • Performance: The Kestrel server and runtime optimizations ensure fast execution.

  • DevOps Friendly: Unified CI/CD pipelines work on all major platforms.

  • Open Source Ecosystem: Continuous improvements from Microsoft and the global community.

11. Real-World Applications

Many global companies leverage ASP.NET Core for cross-platform projects, including:

  • Stack Overflow

  • Alibaba Cloud

  • Siemens

  • GoDaddy

These organizations benefit from improved performance, reduced infrastructure costs, and easier scalability.

Conclusion

ASP.NET Core’s cross-platform support empowers developers to build and deploy web applications that are truly platform-agnostic. Whether you’re developing on Windows, deploying to Linux containers, or testing on macOS, the framework ensures consistency, performance, and reliability across all environments.

By combining its modular architecture, cross-platform SDK, Kestrel web server, and seamless container integration, ASP.NET Core has redefined modern web development — proving that the future of software is “build anywhere, run everywhere.”

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 9.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFE.eu, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFEASP.NET guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.

Read More...

Thursday, 2 October 2025

Simple Examples to Help You Understand Dependency Injection (DI) in.NET Core

Leave a Comment

Writing code that is flexible, easy to maintain, and easy to test is crucial for developing contemporary apps. Dependency Injection (DI) assists us in doing just that. DI makes the code clearer and easier to maintain by allowing dependencies to be supplied from the outside rather than requiring a class to create everything it needs on its own.

 

In this article, we will learn:
  • What is Dependency Injection?

  • Why do we need it?

  • A simple example (Car and Engine)

  • A real-world example ( ILogger<T> in .NET Core)

  • What Happens Behind the Scenes with ILogger<T>

  • Types of DI and Service Lifetimes

  • Benefits of using DI

What is Dependency Injection?

Dependency Injection (DI) is a design pattern where a class receives its dependencies from the outside , instead of creating them inside.

In simple words:

  • A Car needs an Engine

  • Instead of the car creating the Engine, we inject the Engine from outside.

This makes code flexible, maintainable, and testable .

Without DI (Tight Coupling):

public class Engine
{
    public string Start() => "Engine started.";
}

public class Car
{
    private Engine _engine = new Engine(); // Car is tightly bound to Engine

    public string Run()
    {
        return _engine.Start() + " Car is running.";
    }
}

Problem: If we want to use a PetrolEngine or ElectricCar, we must modify the Car class. This breaks the Open/Closed Principle (OCP) of SOLID.

With Dependency Injection (Loose Coupling)

Step 1. Create an Interface

public interface IEngine
{
    string Start();
}

Step 2. Implement Engines

public class PetrolEngine : IEngine
{
    public string Start() => "Petrol engine started.";
}

public class ElectricEngine : IEngine
{
    public string Start() => "Electric engine started.";
}

Step 3. Inject Engine into Car

public class Car
{
    private readonly IEngine _engine;

    // Constructor Injection
    public Car(IEngine engine)
    {
        _engine = engine;
    }

    public string Run()
    {
        return _engine.Start() + " Car is running.";
    }
}

Step 4. Register in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Registering the DI services
builder.Services.AddScoped<IEngine, ElectricEngine>();
builder.Services.AddScoped<Car>();

var app = builder.Build();

app.MapGet("/run", (Car car) => car.Run());

app.Run();

Now, if you hit /run, you will see:
Electric engine started. Car is running.

If tomorrow you want a petrol engine, just change the registration line:

// ElectricEngine can be replaced with PetrolEngine easily.
builder.Services.AddScoped<IEngine, PetrolEngine>();
Real-World Example: ILogger<T> in .NET Core

One of the best practical uses of DI is logging.

ILogger<T> in .NET Core is a built-in service that helps you write logs for your application, like messages, warnings, or errors. You don’t have to create it yourself—.NET Core provides it automatically using dependency injection. The <T> tells the logger which class it’s coming from, so when you look at the logs, you can easily see where each message originated. It’s a simple and clean way to keep track of what’s happening in your app while keeping your code organized.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger) // Constructor Injection: ILogger is passed in automatically

    {
        _logger = logger;
    }

    [HttpGet("welcome")]
    public string GetWelcome()
    {
         // Using the injected logger
        _logger.LogInformation("Welcome Rudra! endpoint was called!");
        return "Hello, Dependency Injection in .NET Core!";
    }
}

Here, ILogger<HomeController> is automatically injected by the DI container.
No need to create a logger manually i.e. .NET handles it for us.

When you call /home/welcome, you’ll see a log like this in the console:

info: MyApp.Controllers.HomeController[0]
       Welcome endpoint was called!

Important: What Happens Behind the Scenes with ILogger<T>

When you access the URL /home/welcome in your browser, ASP .NET Core creates a new instance of the HomeController. It notices that the controller’s constructor requires an ILogger<HomeController>. At this point, it asks the Dependency Injection (DI) container:

Do we have a service for ILogger<HomeController>?”

The DI container already has it registered internally, so it automatically provides an instance of ILogger<HomeController> to the controller. When the GetWelcome() method is called, the log message is sent to the console (or whichever logging provider is configured).

By default, in .NET 6/7/8 Web API, you’ll see something like this in your console:

info: MyApp.Controllers.HomeController[0]
      Welcome endpoint was called!

This output comes directly from the code:

_logger.LogInformation("Welcome endpoint was called!");

The key point here is that you never manually create the logger and you don’t have to configure how the logs are written—the DI container does it for you. Instead of the class being responsible for creating its own dependencies, it simply asks for them in the constructor. This is exactly what Dependency Injection is all about: your class declares what it needs, and the framework provides it automatically.

Types of Dependency Injection

1. Constructor Injection (Most Common)

We already followed constructor injection in example above. In this approach, dependencies are passed into a class through its constructor. It ensures that the dependency is available as soon as the object is created. This is the most widely used method in .NET Core and is also considered best practice.

2. Method Injection

Method Injection is when a class receives a dependency only when a specific method is called, instead of getting it through the constructor or a property. In other words, the dependency is passed as a parameter to the method that needs it. This is useful when the dependency is needed only occasionally or for certain actions. However, if multiple methods need the same dependency, you may end up passing it repeatedly, so it’s less commonly used than constructor injection.

3. Property Injection

Property Injection allows you to provide a class with its dependency through a public property instead of the constructor. In other words, you first create the object and then assign the dependency to its property. This can be useful when the dependency is optional or only needed later. However, you need to be careful—if the property is not set before using it, it can cause errors, which is why this approach is used less often than constructor injection.

Service Lifetimes in .NET Core

Service lifetimes in .NET Core determine how long a dependency object lives when it’s provided by the DI container. There are three main types:

  • Singleton: A single instance is created and shared across the entire application. For example, a configuration service that never changes can be registered as singleton.

  • Scoped: A new instance is created for each HTTP request. For example, a user session service can be scoped so each request gets its own instance.

  • Transient: A new instance is created every time the dependency is requested. For example, a lightweight helper service that performs simple calculations can be transient.

Choosing the right lifetime ensures efficient resource use, predictable behavior, and cleaner code while using dependency injection effectively.

builder.Services.AddSingleton<IEngine, PetrolEngine>();  // same instance for whole app
builder.Services.AddScoped<IEngine, PetrolEngine>();     // one per request
builder.Services.AddTransient<IEngine, PetrolEngine>();  // new every time

Benefits of Dependency Injection

  • Makes code cleaner and easier to maintain.

  • Reduces tight coupling between classes.

  • Allows you to swap implementations without changing your code.

  • Makes unit testing easier by enabling mock dependencies.

  • Leads to organized, reusable, and testable code.

  • Helps build scalable and flexible applications, especially in large projects.

Conclusion

Dependency Injection (DI) is a powerful design pattern in .NET Core that helps make your applications cleaner, more maintainable, and easier to test. By letting the framework provide the dependencies, you reduce tight coupling, improve flexibility, and can easily swap implementations without changing your classes. We saw how DI works with a simple Car and Engine example and also in a real-world scenario using ILogger<T>, along with the different service lifetimes—Singleton, Scoped, and Transient. Understanding and using DI effectively is a key step toward writing professional, robust, and scalable .NET Core applications.

What’s Next?

In the next article, we will dive into Microservices in .NET Core. You’ll learn how to design and implement small, independent services that work together to form a larger application. We’ll also go through a practical example to see how microservices communicate, how to manage dependencies, and how DI plays a role in building scalable and maintainable applications.

In this article, we will learn:

  • What is Dependency Injection?

  • Why do we need it?

  • A simple example (Car and Engine)

  • A real-world example ( ILogger<T> in .NET Core)

  • What Happens Behind the Scenes with ILogger<T>

  • Types of DI and Service Lifetimes

  • Benefits of using DI

What is Dependency Injection?

Dependency Injection (DI) is a design pattern where a class receives its dependencies from the outside , instead of creating them inside.

In simple words:

  • A Car needs an Engine

  • Instead of the car creating the Engine, we inject the Engine from outside.

This makes code flexible, maintainable, and testable .

Without DI (Tight Coupling):
public class Engine
{
    public string Start() => "Engine started.";
}

public class Car
{
    private Engine _engine = new Engine(); // ❌ Car is tightly bound to Engine

    public string Run()
    {
        return _engine.Start() + " Car is running.";
    }
}

Problem: If we want to use a PetrolEngine or ElectricCar, we must modify the Car class. This breaks the Open/Closed Principle (OCP) of SOLID.

With Dependency Injection (Loose Coupling)

Step 1. Create an Interface

public interface IEngine
{
    string Start();
}
Step 2. Implement Engines
public class PetrolEngine : IEngine
{
    public string Start() => "Petrol engine started.";
}

public class ElectricEngine : IEngine
{
    public string Start() => "Electric engine started.";
}
Step 3. Inject Engine into Car
public class Car
{
    private readonly IEngine _engine;

    // Constructor Injection
    public Car(IEngine engine)
    {
        _engine = engine;
    }

    public string Run()
    {
        return _engine.Start() + " Car is running.";
    }
}
Step 4. Register in Program.cs
var builder = WebApplication.CreateBuilder(args);

// Registering the DI services
builder.Services.AddScoped<IEngine, ElectricEngine>();
builder.Services.AddScoped<Car>();

var app = builder.Build();

app.MapGet("/run", (Car car) => car.Run());

app.Run();

Now, if you hit /run, you will see:
Electric engine started. Car is running.

If tomorrow you want a petrol engine, just change the registration line:

// ElectricEngine can be replaced with PetrolEngine easily.
builder.Services.AddScoped<IEngine, PetrolEngine>();
Real-World Example: ILogger<T> in .NET Core

One of the best practical uses of DI is logging.

ILogger<T> in .NET Core is a built-in service that helps you write logs for your application, like messages, warnings, or errors. You don’t have to create it yourself—.NET Core provides it automatically using dependency injection. The <T> tells the logger which class it’s coming from, so when you look at the logs, you can easily see where each message originated. It’s a simple and clean way to keep track of what’s happening in your app while keeping your code organized.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger) // Constructor Injection: ILogger is passed in automatically

    {
        _logger = logger;
    }

    [HttpGet("welcome")]
    public string GetWelcome()
    {
         // Using the injected logger
        _logger.LogInformation("Welcome Rudra! endpoint was called!");
        return "Hello, Dependency Injection in .NET Core!";
    }
}

Here, ILogger<HomeController> is automatically injected by the DI container.
No need to create a logger manually i.e. .NET handles it for us.

When you call /home/welcome, you’ll see a log like this in the console:

info: MyApp.Controllers.HomeController[0]
       Welcome endpoint was called!
Important: What Happens Behind the Scenes with ILogger<T>

When you access the URL /home/welcome in your browser, ASP .NET Core creates a new instance of the HomeController. It notices that the controller’s constructor requires an ILogger<HomeController>. At this point, it asks the Dependency Injection (DI) container:

Do we have a service for ILogger<HomeController>?”

The DI container already has it registered internally, so it automatically provides an instance of ILogger<HomeController> to the controller. When the GetWelcome() method is called, the log message is sent to the console (or whichever logging provider is configured).

By default, in .NET 6/7/8 Web API, you’ll see something like this in your console:

info: MyApp.Controllers.HomeController[0]
      Welcome endpoint was called!

This output comes directly from the code:

_logger.LogInformation("Welcome endpoint was called!");

The key point here is that you never manually create the logger and you don’t have to configure how the logs are written—the DI container does it for you. Instead of the class being responsible for creating its own dependencies, it simply asks for them in the constructor. This is exactly what Dependency Injection is all about: your class declares what it needs, and the framework provides it automatically.

Types of Dependency Injection

1. Constructor Injection (Most Common)

We already followed constructor injection in example above. In this approach, dependencies are passed into a class through its constructor. It ensures that the dependency is available as soon as the object is created. This is the most widely used method in .NET Core and is also considered best practice.

2. Method Injection

Method Injection is when a class receives a dependency only when a specific method is called, instead of getting it through the constructor or a property. In other words, the dependency is passed as a parameter to the method that needs it. This is useful when the dependency is needed only occasionally or for certain actions. However, if multiple methods need the same dependency, you may end up passing it repeatedly, so it’s less commonly used than constructor injection.

3. Property Injection

Property Injection allows you to provide a class with its dependency through a public property instead of the constructor. In other words, you first create the object and then assign the dependency to its property. This can be useful when the dependency is optional or only needed later. However, you need to be careful—if the property is not set before using it, it can cause errors, which is why this approach is used less often than constructor injection.

Service Lifetimes in .NET Core

Service lifetimes in .NET Core determine how long a dependency object lives when it’s provided by the DI container. There are three main types:

  • Singleton: A single instance is created and shared across the entire application. For example, a configuration service that never changes can be registered as singleton.

  • Scoped: A new instance is created for each HTTP request. For example, a user session service can be scoped so each request gets its own instance.

  • Transient: A new instance is created every time the dependency is requested. For example, a lightweight helper service that performs simple calculations can be transient.

Choosing the right lifetime ensures efficient resource use, predictable behavior, and cleaner code while using dependency injection effectively.

builder.Services.AddSingleton<IEngine, PetrolEngine>();  // same instance for whole app
builder.Services.AddScoped<IEngine, PetrolEngine>();     // one per request
builder.Services.AddTransient<IEngine, PetrolEngine>();  // new every time
Benefits of Dependency Injection
  • Makes code cleaner and easier to maintain.

  • Reduces tight coupling between classes.

  • Allows you to swap implementations without changing your code.

  • Makes unit testing easier by enabling mock dependencies.

  • Leads to organized, reusable, and testable code.

  • Helps build scalable and flexible applications, especially in large projects.

Conclusion

Dependency Injection (DI) is a powerful design pattern in .NET Core that helps make your applications cleaner, more maintainable, and easier to test. By letting the framework provide the dependencies, you reduce tight coupling, improve flexibility, and can easily swap implementations without changing your classes. We saw how DI works with a simple Car and Engine example and also in a real-world scenario using ILogger<T>, along with the different service lifetimes—Singleton, Scoped, and Transient. Understanding and using DI effectively is a key step toward writing professional, robust, and scalable .NET Core applications.

What’s Next?

In the next article, we will dive into Microservices in .NET Core. You’ll learn how to design and implement small, independent services that work together to form a larger application. We’ll also go through a practical example to see how microservices communicate, how to manage dependencies, and how DI plays a role in building scalable and maintainable applications.

ASP.NET Core 9.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 9.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFE.eu, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFEASP.NET guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.

 

Read More...

Tuesday, 23 September 2025

Passkeys in ASP.NET Core

Leave a Comment

I've created this tutorial to help you implement passkeys in ASP.NET Core. It offers a complete, executable code example and goes over the fundamental ideas. The most recent ASP.NET Core updates, on which the entire system is based, provide native passkey support, which facilitates much easier integration.


This is a comprehensive tutorial on how to use passkeys in ASP.NET Core.

Implementing Passkeys in ASP.NET Core

1. Introduction: The Dawn of a Passwordless Future

In an increasingly security-conscious digital landscape, passwords have become a liability. They are susceptible to phishing, credential stuffing, and data breaches. Passkeys, built on the Web Authentication (WebAuthn) standard, offer a revolutionary alternative. They replace traditional passwords with a secure, phishing-resistant public-private key pair. In this guide, we will explore how to integrate this powerful authentication method into your ASP.NET Core application, leveraging the framework's built-in Identity features and the WebAuthn API.

2. Understanding the Core Concepts

Passkeys rely on a simple yet robust cryptographic model. This model involves three key components:

  • Relying Party (RP): This is your ASP.NET Core server. It is the entity that you, the developer, control, and it's the server the user is trying to authenticate with.

  • Authenticator: This is the user's device—a smartphone, laptop, or a physical security key (like a YubiKey). It securely holds the private key and performs the cryptographic signing.

  • WebAuthn API: A JavaScript API in the browser that acts as the bridge between the Relying Party and the Authenticator, orchestrating the registration and authentication ceremonies.

When a user registers a passkey, their authenticator generates a unique public-private key pair for your website (the Relying Party). The private key remains on the device, never leaving it. The public key is securely sent to your server, where it is stored and linked to the user's account. For subsequent logins, the server sends a unique, cryptographic challenge to the user's browser. The browser then prompts the authenticator to use the private key to sign the challenge. The signed challenge is sent back to the server, which verifies it using the stored public key. If the signature is valid, the user is authenticated.

This process is inherently phishing-resistant because the user never types a secret that can be stolen, and the cryptographic signature is bound to your specific website.

3. Server-Side Implementation with ASP.NET Core Identity

The introduction of native passkey support in ASP.NET Core Identity simplifies the server-side implementation significantly, eliminating the need for complex external libraries for most use cases.

Step 1. Project Setup

Begin by creating a new ASP.NET Core Web App with the "Individual Accounts" authentication type. This template automatically includes ASP.NET Core Identity and a database context.

Step 2. Configure Passkeys

In your Program.cs file, you need to enable passkey support within the Identity configuration. This is a simple, one-line addition.

// Add services to the container.
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddPasskeys() // New in .NET 10 or later
    .AddDefaultTokenProviders();

The AddPasskeys() extension method handles all the necessary service registrations and middleware for WebAuthn.

Step 3. Registration Endpoint

The registration process requires two parts: generating the creation options and verifying the attestation response.

A. Generate Creation Options: This API endpoint is responsible for creating a PublicKeyCredentialCreationOptions object, which is a JSON payload that the browser needs to create the passkey.

[HttpPost("login/finish")]
public async Task<IActionResult> FinishAuthentication([FromBody] JsonElement credential)
{
    var result = await _signInManager.SignInWithPasskeyAsync(credential.GetRawText());
    if (result.Succeeded)
    {
        return Ok("Login successful.");
    }
    return Unauthorized(result.ToString());
}

B. Verify Attestation Response: After the browser receives the options and the user creates the passkey, it sends the result (the attestation) back to your server. This endpoint verifies the attestation and saves the new credential.

Step 4. Authentication Endpoint

The login process is similar to registration, but it uses the navigator.credentials.get() method.

4. Client-Side Implementation (JavaScript)

The front-end code is responsible for calling the server endpoints and interacting with the WebAuthn API.

const bufferToBase64Url = (buffer) => {
    const bytes = new Uint8Array(buffer);
    let str = '';
    for (const char of bytes) {
        str += String.fromCharCode(char);
    }
    return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
};

const base64UrlToBuffer = (base64Url) => {
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const binary = atob(base64);
    const len = binary.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
};

// Function to handle passkey registration
const registerPasskey = async (username) => {
    try {
        // Step 1: Get registration options from the server
        const response = await fetch('/Passkeys/register/start', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(username),
        });

        if (!response.ok) {
            throw new Error(`Server error: ${response.statusText}`);
        }

        const options = await response.json();

        // Convert base64Url-encoded fields to ArrayBuffers
        options.publicKey.challenge = base64UrlToBuffer(options.publicKey.challenge);
        options.publicKey.user.id = base64UrlToBuffer(options.publicKey.user.id);

        if (options.publicKey.excludeCredentials) {
            options.publicKey.excludeCredentials.forEach(cred => {
                cred.id = base64UrlToBuffer(cred.id);
            });
        }

        // Step 2: Call the WebAuthn API to create the credential
        const credential = await navigator.credentials.create(options);

        // Step 3: Serialize and send the credential to the server
        const attestationResponse = {
            id: credential.id,
            rawId: bufferToBase64Url(credential.rawId),
            type: credential.type,
            response: {
                attestationObject: bufferToBase64Url(credential.response.attestationObject),
                clientDataJSON: bufferToBase64Url(credential.response.clientDataJSON),
            },
        };

        const verificationResponse = await fetch('/Passkeys/register/finish', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(attestationResponse),
        });

        if (verificationResponse.ok) {
            console.log('Passkey registered successfully!');
        } else {
            const error = await verificationResponse.text();
            console.error(`Passkey registration failed: ${error}`);
        }

    } catch (error) {
        console.error('Registration failed:', error);
    }
};

// Function to handle passkey authentication
const authenticatePasskey = async (username) => {
    try {
        // Step 1: Get authentication options from the server
        const response = await fetch('/Passkeys/login/start', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(username),
        });

        if (!response.ok) {
            throw new Error(`Server error: ${response.statusText}`);
        }

        const options = await response.json();

        // Convert base64Url-encoded fields to ArrayBuffers
        options.publicKey.challenge = base64UrlToBuffer(options.publicKey.challenge);
        options.publicKey.allowCredentials.forEach(cred => {
            cred.id = base64UrlToBuffer(cred.id);
        });

        // Step 2: Call the WebAuthn API to get the assertion
        const credential = await navigator.credentials.get(options);

        // Step 3: Serialize and send the assertion to the server
        const assertionResponse = {
            id: credential.id,
            rawId: bufferToBase64Url(credential.rawId),
            type: credential.type,
            response: {
                authenticatorData: bufferToBase64Url(credential.response.authenticatorData),
                clientDataJSON: bufferToBase64Url(credential.response.clientDataJSON),
                signature: bufferToBase64Url(credential.response.signature),
                userHandle: bufferToBase64Url(credential.response.userHandle),
            },
        };

        const verificationResponse = await fetch('/Passkeys/login/finish', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(assertionResponse),
        });

        if (verificationResponse.ok) {
            console.log('Passkey login successful!');
        } else {
            const error = await verificationResponse.text();
            console.error(`Passkey login failed: ${error}`);
        }

    } catch (error) {
        console.error('Login failed:', error);
    }
};

// Example usage
// await registerPasskey('testuser');
// await authenticatePasskey('testuser');
```
Conclusion: A Simpler, More Secure Future

Implementing passkeys in ASP.NET Core has never been easier, especially with the framework's native support. By adopting this technology, you are not only providing a more secure login experience that is resistant to the most common attack vectors, but you are also dramatically improving user convenience. This passwordless flow removes the cognitive burden of remembering complex passwords and the friction of forgotten password resets. It's a powerful step toward a more secure, user-friendly web.

This guide provides a solid foundation for your project. You can now build upon this knowledge by adding features like multi-factor authentication, account management pages to view and revoke passkeys, and user interface elements that provide clear feedback during the registration and authentication processes. Let me know if you would like to dive deeper into the fido2-net-lib library for more advanced configurations or explore how to integrate passkeys with existing identity providers.

Best ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 8.0 hosting is the feature and reliability. HostForLIFE is the leading provider of Windows hosting and affordable ASP.NET Core, their servers are optimized for PHP web applications. The performance and the uptime of the hosting service are excellent and the features of the web hosting plan are even greater than what many hosting providers ask you to pay for. 

At HostForLIFEASP.NET, customers can also experience fast ASP.NET Core hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFEASP.NET guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.
Read More...