Tuesday, 27 January 2026

Task Parallel Library (TPL) in ASP.NET Core

Leave a Comment

The goal of ASP.NET Core is to create scalable, high-performance web applications. Its widespread usage of asynchronous programming is one of the main factors contributing to its efficiency. The Task Parallel Library (TPL) is the core of this programming paradigm.

In ASP.NET Core, the Task Parallel Library (TPL) is a crucial component of request processing and application scalability, offering a uniform solution to write asynchronous and parallel code in.NET.

The function of TPL in ASP.NET Core, its operation, typical usage patterns, and best practices for practical applications are all covered in this article.

Task Parallel Library (TPL): What Is It?
A collection of APIs called the Task Parallel Library (TPL) was added to.NET to make parallel and asynchronous programming easier. The System.Threading.Tasks namespace is where it is mostly found.

Developers use Task objects, which represent asynchronous operations, rather than working directly with threads. Developers can concentrate on application logic since the runtime controls scheduling, execution, and resource usage. 

Why TPL Matters in ASP.NET Core?

ASP.NET Core uses a thread pool–based request handling model. Each incoming HTTP request requires a thread to execute. If that thread is blocked during I/O operations, it cannot serve other requests.

Using TPL correctly helps:

  • Improve application scalability

  • Avoid thread starvation

  • Increase throughput under high load

  • Use server resources efficiently

For these reasons, asynchronous programming using TPL is a best practice in ASP.NET Core.

Asynchronous Programming with async and await

The most common way to use TPL in ASP.NET Core is through the async and await keywords.

Example: Async Controller Action

public async Task<IActionResult> GetUsers()
{
    var users = await _userService.GetUsersAsync();
    return Ok(users);
}

This approach ensures that the thread is released while waiting for I/O operations, such as database or API calls.

Using TPL in Services and Repositories

Asynchronous programming should be implemented consistently across controllers, services, and repositories.

Example: Async Service Method

public async Task<List<User>> GetUsersAsync()
{
    return await _context.Users.ToListAsync();
}

Entity Framework Core provides native asynchronous methods that integrate directly with TPL and should always be preferred.

Executing Multiple Tasks in Parallel

When multiple independent operations need to be executed, TPL allows them to run concurrently using Task.WhenAll.

Example: Parallel Execution of Independent Tasks
public async Task<IActionResult> GetDashboardData()
{
    var usersTask = _userService.GetUsersAsync();
    var ordersTask = _orderService.GetOrdersAsync();

    await Task.WhenAll(usersTask, ordersTask);

    return Ok(new
    {
        Users = usersTask.Result,
        Orders = ordersTask.Result
    });
}

This pattern improves performance by reducing total execution time for independent I/O-bound operations.

Handling CPU-Bound Operations

ASP.NET Core applications are primarily I/O-bound. However, in rare cases, CPU-intensive operations may be required.

For such scenarios, it Task.Run can be used to offload work to a background thread.

await Task.Run(() => PerformComplexCalculation());
Important Notes
  • Use Task.Run sparingly

  • Avoid using it for database or HTTP operations

  • Prefer background services for long-running CPU-bound tasks

Background Processing Using TPL

Long-running tasks should not be executed within controller actions. ASP.NET Core provides BackgroundService for such use cases.

Example: Background Worker
public class EmailBackgroundService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await SendEmailsAsync();
            await Task.Delay(5000, stoppingToken);
        }
    }
}

This approach ensures proper lifecycle management and graceful shutdown.

Common Mistakes to Avoid

Blocking Asynchronous Code

Avoid blocking asynchronous calls using .Result or .Wait().

var data = GetDataAsync().Result; // Not recommended

Blocking can lead to deadlocks and thread pool exhaustion.

Fire-and-Forget Tasks

Starting tasks without awaiting them inside controllers can result in incomplete operations if the application restarts.

Instead, use background services or message queues.

Using Parallel.For in Web Requests

Parallel.For is intended for CPU-bound workloads and should not be used inside ASP.NET Core controllers.

Best Practices for Using TPL in ASP.NET Core
  • Use async it await consistently

  • Prefer asynchronous APIs provided by libraries

  • Use Task.WhenAll for independent operations

  • Avoid blocking calls

  • Keep controllers lightweight

  • Use background services for long-running tasks

TPL vs Traditional Threading
Traditional ThreadingTask Parallel Library
Manual thread managementManaged by runtime
Blocking executionNon-blocking
Hard to scaleHighly scalable
Error-proneException-safe

Conclusion

The Task Parallel Library plays a crucial role in the architecture of ASP.NET Core applications. By leveraging asynchronous programming and parallel execution correctly, developers can build applications that are scalable, responsive, and efficient.

Understanding and applying TPL best practices is essential for developing modern, high-performance ASP.NET Core applications.

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 10.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...

Thursday, 22 January 2026

Common Dependency Injection Errors in Scrutor and How to Prevent Them

Leave a Comment

Scrutor is an effective addition to the Dependency Injection (DI) container that comes with.NET. Without taking the place of the default DI system, it makes assembly scanning, convention-based registration, and decorator support possible. Scrutor greatly saves boilerplate, but if used carelessly, it also adds indirection that can conceal architectural issues. The majority of Dependency Injection problems encountered in practical applications are not caused by Scrutor per se, but rather by excessively expansive scanning rules, ambiguous lifetimes, or hard-to-understand implicit registrations.



Using real-world, production-focused examples, this article examines the most frequent Dependency Injection errors while using Scrutor and discusses how to avoid them.

1. Over-Scanning Assemblies

The problem

builder.Services.Scan(scan =>
    scan.FromApplicationDependencies()
        .AddClasses()
        .AsImplementedInterfaces()
);

Why does this cause issues

Scanning all application dependencies registers every discoverable class, including framework and third-party types. This makes the DI container unpredictable, slows application startup, and creates behavior that is difficult to debug and reason about.

The correct approach

Scan only assemblies you own and apply strict filters.

builder.Services.Scan(scan =>
    scan.FromAssemblyOf<IApplicationService>()
        .AddClasses(c => c.AssignableTo<IApplicationService>())
        .AsImplementedInterfaces()
        .WithScopedLifetime()
);

Assembly scanning should always be deliberate and narrowly scoped.

2. Accidental Multiple Interface Registrations

The problem

public class UserService :
    IUserService,
    IApplicationService,
    IDisposable
{
}
.AsImplementedInterfaces()

This registers every implemented interface, including interfaces that were never meant to be resolved from the container.

Why this is dangerous

Unintended registrations can override other services and introduce subtle bugs that appear only at runtime.

The correct approach

Be explicit when registering services that implement multiple interfaces.

.AddClasses(c => c.AssignableTo<IApplicationService>())
.As<IUserService>()
.WithScopedLifetime()

Automatic interface registration should be used only when the intent is clear and controlled.

3. Lifetime Mismatch (Captive Dependency)

The problem

services.AddSingleton<ReportService>();
services.AddScoped<AppDbContext>();
public class ReportService
{
    public ReportService(AppDbContext context) { }
}

Why this breaks applications

A singleton capturing a scoped dependency leads to memory leaks, invalid object lifetimes, and runtime exceptions under load.

The correct approach

Align lifetimes so that dependencies live at least as long as their consumers.

services.AddScoped<ReportService>();

Alternatively, use factory abstractions such as IDbContextFactory .

4. Hidden Decorators and Execution Order Confusion

The problem

services.Decorate<IUserService, LoggingUserService>();
services.Decorate<IUserService, CachingUserService>();

What actually happens

The runtime execution order becomes:

Caching -> Logging -> UserService

Why this is confusing

Decorators are invisible at injection sites, and the execution order is not obvious. Changing the registration order can silently alter application behavior.

The correct approach

Register decorators together, document their order, and add constructor logging in development to make resolution explicit.

public LoggingUserService(...)
{
    Console.WriteLine("Logging decorator created");
}

Decorator order should always be intentional.

5. Mixing Manual and Scrutor Registrations

The problem

services.AddScoped<IUserService, UserService>();

services.Scan(scan =>
    scan.FromAssemblyOf<IApplicationService>()
        .AddClasses(c => c.AssignableTo<IApplicationService>())
        .AsImplementedInterfaces()
);

Why this leads to bugs

The .NET DI container follows a last-registration-wins strategy. Mixing manual and scanned registrations makes behavior unpredictable and environment-dependent.

The correct approach

Use one registration strategy per layer. Scrutor is well-suited for application services and repositories, while manual registration should be reserved for infrastructure and framework services.

Consistency is more important than flexibility.

6. Not Validating Dependency Injection at Startup

The problem

Dependency Injection errors surface only at runtime, often under load.

The correct approach

Enable DI validation during application startup.

builder.Host.UseDefaultServiceProvider(options =>
{
    options.ValidateScopes = true;
    options.ValidateOnBuild = true;
});

This configuration catches missing registrations, circular dependencies, and lifetime mismatches early.

7. Business Logic Inside Decorators

The problem

public class AuthorizationDecorator : IUserService
{
    public async Task DeleteUser(int id)
    {
        if (!IsAdmin())
            throw new UnauthorizedAccessException();

        await _inner.DeleteUser(id);
    }
}

Why this is a design flaw

Decorators should not contain business rules. Hiding business logic inside decorators makes behavior harder to understand, test, and maintain.

The correct approach

Decorators should handle technical cross-cutting concerns such as logging, caching, validation, or metrics. Business rules should remain in the core service.

8. Overusing Singleton Services

The problem

services.AddSingleton<UserService>();

Why this causes problems

Singletons introduce shared mutable state, thread-safety issues, and test instability. Many services do not need to live for the entire lifetime of the application.

The correct approach

Default to scoped services unless there is a clear and justified reason to use a singleton.

services.AddScoped<IUserService, UserService>();

Singletons should be rare and stateless.

9. Service Locator Usage in Minimal APIs

The problem

app.MapGet("/users", (IServiceProvider provider) =>
{
    var service = provider.GetRequiredService<IUserService>();
});

Why this is harmful

Manually resolving services hides dependencies and reintroduces the service locator anti-pattern, making the code harder to test and reason about.

The correct approach

Let the framework inject dependencies directly.

app.MapGet("/users", (IUserService service) =>
{
    return service.GetAll();
});
Mental Model to Remember

Scrutor reduces boilerplate, but it also hides complexity. If your DI configuration is difficult to explain, difficult to debug, or behaves differently across environments, your scanning rules are too permissive.

Key Takeaway

Scrutor is not inherently dangerous. Uncontrolled conventions are.

When used with tight assembly scanning, clear lifetimes, explicit intent, and proper validation, Scrutor becomes a powerful tool for building clean, scalable Dependency Injection in .NET.

Happy Coding!

I write about modern C#, .NET, and real-world development practices. Follow me on C# Corner for regular insights, tips, and deep dives.

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...

Monday, 19 January 2026

How to Begin Using Minimal APIs in.NET Core?

Leave a Comment

Microsoft introduced Minimal APIs, a streamlined approach to creating HTTP APIs with fewer boilerplate and a greater emphasis on functionality, with the release of.NET 6. Microservices, lightweight APIs, and quick prototyping are all best served by minimal APIs.


Minimal APIs: What Are They?
Developers can design RESTful services without controllers, starting classes, or complicated configurations thanks to minimal APIs. The Program.cs file contains definitions for everything.

They are:

  • Lightweight

  • Fast to develop

  • Easy to read

  • Perfect for small to medium APIs

Why Minimal APIs?

Traditional Web APIs require:

  • Controllers

  • Attributes

  • Dependency injection setup

  • Multiple files

Minimal APIs reduce this complexity by enabling you to define endpoints directly using lambda expressions.

Benefits:

  • Less boilerplate code

  • Improved performance

  • Easy learning curve

  • Clean and concise syntax

Creating a Minimal API in .NET Core

Step 1: Create a New Project

Use the following command:

dotnet new web -n MinimalApiDemo
Plain text

Open the project in Visual Studio or VS Code.

Step 2: Program.cs Example

Here is a simple Minimal API example:

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

app.MapGet("/", () => "Welcome to Minimal API!");

app.MapGet("/hello/{name}", (string name) =>
{
    return $"Hello, {name}!";
});

app.Run();
Plain text

Step 3: Run the Application

Run the project and navigate to:

  • https://localhost:5001/

  • https://localhost:5001/hello/John

HTTP Methods in Minimal APIs

app.MapGet("/users", () => "Get all users");

app.MapPost("/users", (User user) => $"User {user.Name} created");

app.MapPut("/users/{id}", (int id, User user) =>
    $"User {id} updated");

app.MapDelete("/users/{id}", (int id) =>
    $"User {id} deleted");
Plain text

Model Binding Example

public record User(int Id, string Name, string Email);
Plain text

Minimal APIs automatically bind request data to models.

Dependency Injection in Minimal APIs

builder.Services.AddScoped<IUserService, UserService>();

app.MapGet("/service", (IUserService service) =>
{
    return service.GetMessage();
});
Plain text

When Should You Use Minimal APIs?

  • Microservices

  • Lightweight REST APIs

  • Prototypes & PoCs

  • Serverless applications

Not ideal for very large, complex enterprise applications

Minimal APIs vs Controller-Based APIs

FeatureMinimal APIsController APIs
BoilerplateVery LowHigh
Learning CurveEasyModerate
PerformanceHighGood
StructureFlatLayered

Conclusion

A contemporary, tidy, and effective method of creating APIs in.NET is using minimal APIs. They preserve power and flexibility while lowering complexity. Minimal APIs are a great option if your application doesn't need complex architecture.

Read More...

Tuesday, 13 January 2026

Learning FastEndpoints in ASP.NET

Leave a Comment

In the evolution of ASP.NET Core, we have moved from the rigid structure of MVC (Model-View-Controller) to the streamlined simplicity of Minimal APIs. However, as applications grow, Minimal APIs can become disorganized, and Controllers often suffer from “bloat”—violating the Single Responsibility Principle.

 

Enter FastEndpoints.

This library is not just another framework; it is a philosophy shift. It wraps the performance of Minimal APIs in a structured, maintainable pattern known as REPR (Request-Endpoint-Response). In this post, we will dive deep into what FastEndpoints is, why it matters, and how to implement it in your .NET projects.

The Problem with Controllers 

Traditionally, .NET developers group logic by resource. A UserController, for example, might handle Get, Create, Update, and Delete operations.

While this looks organized initially, it leads to several issues:

  • Constructor Bloat: A controller injecting dependencies for all its methods means every request instantiates services it might not need.

  • Hidden Coupling: Shared private methods inside a controller create tight coupling between unrelated endpoints (e.g., “Get User” logic bleeding into “Update User”).

  • Violation of SRP: A controller handles many responsibilities, rather than just one.

The Solution: The REPR Pattern 

FastEndpoints implements the REPR pattern, which stands for Request-Endpoint-Response.

Instead of grouping by resource (a Controller), you group by feature (an Endpoint). Each endpoint is a distinct class dedicated to a single purpose (e.g., CreateUserEndpoint, GetUserEndpoint).

The Components

  1. Request: A DTO (Data Transfer Object) defining what the client sends.

  2. Endpoint: A class containing the logic for only that specific request.

  3. Response: A DTO defining what the server returns.

Getting Started with FastEndpoints 

Let’s look at how to implement a simple “Create User” feature using FastEndpoints in .NET 8.

  1. Installation

First, install the necessary package via the NuGet Package Manager or CLI:

dotnet add package FastEndpoints
  1. Configuration

In your Program.cs, register and enable FastEndpoints. It sits on top of the existing ASP.NET Core pipeline, so it integrates seamlessly.

using FastEndpoints;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFastEndpoints(); // Register services
var app = builder.Build();
app.UseFastEndpoints(); // Register middleware
app.Run();
  1. Defining the Endpoint

Here is where the magic happens. Instead of a method inside a controller, we define a class.

The DTOs:

public class CreateUserRequest
{
    public string FirstName { get; set; } = default!;
    public string LastName { get; set; } = default!;
    public string Email { get; set; } = default!;
}
public class CreateUserResponse
{
    public string FullName { get; set; } = default!;
    public string Message { get; set; } = default!;
}

 The Endpoint:

using FastEndpoints;
public class CreateUserEndpoint : Endpoint<CreateUserRequest, CreateUserResponse>
{
    public override void Configure()
    {
        Post("/api/users/create");
        AllowAnonymous();
    }
    public override async Task HandleAsync(CreateUserRequest req, CancellationToken ct)
    {
        // Simulate database logic
        var fullName = $"{req.FirstName} {req.LastName}";
        // Send the response
        await SendAsync(new CreateUserResponse
        {
            FullName = fullName,
            Message = "User created successfully!"
        }, cancellation: ct);
    }
}

Breakdown of the Code

  • Inheritance: The class inherits from Endpoint<TRequest, TResponse>, providing strict typing immediately.

  • Configure(): This replaces route attributes ([HttpPost], [Route]). You define the configuration imperatively, which is often cleaner and more discoverable.

  • HandleAsync(): This contains your business logic. It takes the strongly-typed Request object and a CancellationToken automatically.

Key Features & Benefits

1. Built-in Validation

FastEndpoints integrates heavily with FluentValidation. You don’t need to manually check ModelState.IsValid. You simply define a validator, and the library handles the 400 Bad Request response automatically if validation fails.

public class CreateUserValidator : Validator<CreateUserRequest>
{
    public CreateUserValidator()
    {
        RuleFor(x => x.Email).NotEmpty().EmailAddress();
        RuleFor(x => x.FirstName).NotEmpty().MinimumLength(3);
    }
}
2. Vertical Slice Architecture

By forcing you to create a file per endpoint, FastEndpoints naturally pushes your project toward Vertical Slice Architecture. All files related to a feature (the Endpoint, the Request/Response DTOs, the Validator, and the Mapper) can live in a single folder. This makes navigating code significantly easier than jumping between Controllers/, Models/, and Services/ folders.

3. Performance

Because FastEndpoints is a wrapper around ASP.NET Core’s Minimal APIs, it is extremely performant—significantly faster than traditional MVC Controllers and effectively on par with raw Minimal APIs. It builds the expression trees for mapping and validation at startup, ensuring runtime speed is maximized.

Conclusion

FastEndpoints offers the “sweet spot” for modern .NET API development. It provides the structure that Minimal APIs lack and removes the bloat that Controllers impose. If you are looking to build maintainable, high-performance, and type-safe APIs, adopting the REPR pattern with FastEndpoints is a powerful choice.

Happy Coding !!!

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 10.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.eu 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, 8 January 2026

Right now, Which Version of .NET Should I use?

Leave a Comment

If you are starting a new application today, use .NET 10. It is the current Long Term Support release and will be supported until November 2028. That alone makes it the safest default.

If you are maintaining an existing production system, your safest choices right now are .NET 10 or .NET 8. Both are supported, but .NET 8 has a much shorter runway left. If you are considering .NET 9, do it intentionally. It is a Short Term Support release and only makes sense if your team is comfortable upgrading again soon.

Supported .NET versions right now
.NET VersionSupport TypeEnd of Support
.NET 10LTSNovember 2028
.NET 9STSNovember 2026
.NET 8LTSNovember 2026

This table explains almost every decision you need to make.

LTS vs STS explained like a human

LTS is what you choose when stability matters more than chasing the newest features. LTS releases are supported for about three years and are ideal for business applications, enterprise systems, and long-lived products.

STS is for teams that like moving fast and upgrading often. STS releases are supported for a shorter time and assume you will move to the next version on schedule.

Most problems happen when teams choose STS but behave like they chose LTS.

Which .NET version should you use based on what you’re building

New applications

Use .NET 10. It gives you the longest support window, modern performance improvements, and a clean baseline for future upgrades. Starting new projects on older versions usually creates unnecessary work later.

Existing applications on .NET 8

If your app is already on .NET 8, you are not in trouble, but the clock is ticking. Support ends in November 2026. If your dependencies allow it and your test coverage is reasonable, upgrading to .NET 10 sooner rather than later usually reduces future pain.

Existing applications on .NET 9

Treat .NET 9 as a temporary stop, not a destination. It is fine if you knowingly chose STS, but you should already have a plan to move to .NET 10 well before 2026 ends.

Applications on .NET 6 or .NET 7

These versions are already out of support. Running out-of-support frameworks is not just a security concern. Over time, tooling, hosting providers, and third-party libraries quietly move on, and your upgrade becomes harder and more expensive. If possible, jump straight to .NET 10. If not, use .NET 8 as a short-term bridge.

.NET Framework applications

If you are on .NET Framework, your decision is different. For Windows-only legacy systems with heavy dependencies, staying put for now may be realistic. For anything that needs cloud deployment, containers, cross-platform support, or modern performance, you should plan a gradual migration to modern .NET.

A decision framework that actually works
  • Is this a new app meant to live for years? Use .NET 10.

  • Do you want the least maintenance and longest support? Use .NET 10 LTS.

  • Are you blocked by dependencies or vendors? Use .NET 8 temporarily and plan your move to .NET 10.

  • Are you intentionally upgrading every one to two years? Then .NET 9 can make sense.

Locking your team to the right version

Check installed SDKs.

dotnet --list-sdks
dotnet --list-runtimes

Pin the SDK using global.json to avoid environment drift.

{"sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"}}

Target the correct framework.

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

For shared libraries.

<PropertyGroup>
  <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
</PropertyGroup>
A low-risk upgrade strategy
  • Upgrade the SDK and build pipeline first without changing the target framework. This separates tooling issues from runtime issues.

  • Update NuGet packages early because most upgrade problems come from outdated dependencies.

  • Avoid mixing framework upgrades with large refactors so rollbacks stay cheap.

  • Measure before and after. Startup time, memory usage, throughput, and latency should guide decisions, not assumptions.

Common mistakes teams keep making
  • Choosing STS for long-lived systems and forgetting to upgrade.

  • Staying on unsupported versions because nothing is broken yet.

  • Upgrading without pinning the SDK and wasting time debugging environment differences.

Final recommendation

If you want one boring, correct answer, use .NET 10.

It gives you the longest support window, aligns with Microsoft’s direction, and minimizes forced upgrades.

Use .NET 8 only as a short-term step if you are blocked.
Use .NET 9 only if you are disciplined about upgrading again.

Top 5 FAQs: Choosing the Right .NET Version

1. Should I still start a new project on .NET 8?

Only if you have a hard dependency that does not yet support .NET 10. Otherwise, .NET 10 is the better long-term choice.

2. Is .NET 9 safe for production?

Yes, but only if you treat it as short-term. The risk is not stability, it is forgetting to upgrade later.

3. How often should I upgrade .NET in production apps?

Most teams should upgrade every LTS cycle, roughly every three years. STS requires a faster and more disciplined cadence.

4. Can I skip versions when upgrading .NET?

In most cases, yes. Many teams move directly from .NET 6 or 7 to .NET 10 as long as dependencies are compatible.

5. Does upgrading .NET automatically improve performance?

Often, but not magically. Real gains come when you upgrade, test, measure, and remove old workarounds that newer runtimes no longer need.

ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 10.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.eu 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, 5 January 2026

ASP.NET Core's Observability Becomes a First-Class Feature

Leave a Comment
Contemporary applications require visibility into production in addition to functional coding. With built-in metrics, diagnostics, logging, and tracing, ASP.NET Core now views observability as a first-class feature.

This article discusses:
  • ASP.NET Core's built-in observability features
  • Detailed setup
  • Examples of real-world coding for tracking, logging, and metrics
  • Alerts, dashboards, and production preparedness
What Is Observability and Why Does It Matter?

Observability is the ability to understand the internal state of your application from external outputs like metrics, logs, and traces.

Core Observability Components

  1. your application's internal state from external outputs such asMetrics – Quantitative data (request rate, latency, errors).

  2. Logs – Detailed events or errors with structured information.as Metrics

  3. Traces – End-to-end visibility of requests across services.

Proper observability ensures you can detect and diagnose issues without guessing .

Built-In Observability Features in ASP.NET Core

ASP.NET Core now ships with rich diagnostics:

  • HTTP request counts and durations

  • Active requests

  • Failed requests (4xx / 5xx)

  • Connection, TLS, and runtime metrics (CPU, memory, GC)

These features reduce the need for heavy custom instrumentation and integrate easily with Prometheus, Grafana, and Azure Monitor.

Step-by-Step Setup of Observability

1. Create or Open Your ASP.NET Core App

dotnet new webapi -n ObservabilityDemo
cd ObservabilityDemo

2. Add Required NuGet Packages

dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore

3. Configure OpenTelemetry in Program.cs

using OpenTelemetry.Metrics;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddRuntimeInstrumentation()
            .AddPrometheusExporter();
    });

var app = builder.Build();

app.MapPrometheusScrapingEndpoint();
app.MapControllers();

app.Run();

Your app now exposes production-ready metrics at /metrics.

Real-World Coding Examples

These examples illustrate practical observability in production scenarios.

Example 1: Track API Usage (Business Metric)

using System.Diagnostics.Metrics;

public static class AppMetrics
{
    public static readonly Meter Meter = new("ObservabilityDemo");

    public static readonly Counter<int> OrdersCreated =
        Meter.CreateCounter<int>("orders_created");
}

[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    [HttpPost]
    public IActionResult CreateOrder()
    {
        AppMetrics.OrdersCreated.Add(1);
        return Ok("Order created successfully");
    }
}

Scenario: Track how many orders your API receives per minute for business insight.

Example 2: Measure Request Duration for a Specific Endpoint

using System.Diagnostics;

[HttpGet("inventory")]
public async Task<IActionResult> GetInventory()
{
    var stopwatch = Stopwatch.StartNew();

    var inventory = await _inventoryService.GetItemsAsync();

    stopwatch.Stop();
    Console.WriteLine($"Inventory request took {stopwatch.ElapsedMilliseconds} ms");

    return Ok(inventory);
}

Scenario: Measure performance of heavy endpoints like inventory queries or report generation.

Example 3: Track Failures with Custom Counter

public static readonly Counter<int> PaymentFailures =
    AppMetrics.Meter.CreateCounter<int>("payment_failures");

[HttpPost("payments")]
public IActionResult ProcessPayment(PaymentRequest request)
{
    try
    {
        _paymentService.Process(request);
        return Ok();
    }
    catch(Exception)
    {
        PaymentFailures.Add(1);
        return StatusCode(500, "Payment failed");
    }
}

Scenario: Monitor payment failures in real-time for alerting or retries.

Example 4: Add Distributed Tracing for Downstream Calls

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddAspNetCoreInstrumentation();
        tracing.AddHttpClientInstrumentation();
    });

[HttpGet("external-data")]
public async Task<IActionResult> GetExternalData()
{
    var client = _httpClientFactory.CreateClient();
    var response = await client.GetAsync("https://api.example.com/data");
    return Ok(await response.Content.ReadAsStringAsync());
}

Scenario: Trace latency across external API calls to identify bottlenecks.

Dashboards and Alerts
Prometheus & Grafana

For dashboards and alerts we can use open sourced, self-hosted, cloud-friendly observability tools Prometheus and Grafana. They work on-prem, cloud, hybrid, and air-gapped systems. Cloud providers offer managed versions.

Dashboard Panels:

  • Request rate ( http_server_request_duration_seconds_count )

  • Latency P95 ( histogram_quantile(0.95, rate(...)) )

  • Error rate ( rate(http_server_request_duration_seconds_count{status_code=~"5.."}[1m]) )

Alerts:

  • High error rate

  • High latency

  • CPU/memory spikes

Include service name, severity, and dashboard links in every alert.

Production Observability Checklist

Before going live, ensure:

  • Metrics endpoint exposes system and business metrics

  • Structured logging with correlation IDs is enabled

  • Distributed tracing is enabled for all incoming/outgoing requests

  • Health checks and readiness probes are configured

  • Dashboards visualize key metrics and errors

  • Alerts are actionable and tested

  • Deployment markers and versions are visible

Production-ready apps answer: "What broke, why, and how badly — using metrics, logs, and traces alone."

Key Takeaways

ASP.NET Core's first-class observability enables developers to monitor apps confidently in production. By combining metrics, logs, tracing, dashboards, and alerts, you can proactively detect and resolve issues, optimize performance, and ensure your applications are reliable.

Observability is no longer optional — it's essential. Start implementing it from day one and make your app production-ready.

Happy Coding!

I write about modern C#, .NET, and real-world development practices. Follow me on C# Corner for regular insights, tips, and deep dives.

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...

Monday, 22 December 2025

Session Count-Based Concurrent Login Management in ASP.NET WebForms

Leave a Comment

Limiting the amount of concurrent logins per user is a typical practice in many real-world applications, including banking portals, trading platforms, admin panels, and business systems.
Data inconsistencies, license abuse, and security problems might result from permitting infinite logins with the same credentials.


This article describes a traditional concurrent login/session-count control requirement, explores several solutions, and offers a simple ASP.NET WebForms + SQL Server solution with functioning logic and output explanation.

Problem Statement

  • A single user account should be allowed to log in from only N sessions (example: 2, 3, or 4).

  • If the user logs in again after reaching the limit:

    • Either deny login OR

    • Automatically terminate the oldest inactive session and allow new login.

  • Sessions may remain active if:

    • Browser is closed suddenly

    • User forgets to log out

  • The system must handle these cases automatically.

Requirements Summary

  • Allow configurable session count per user

  • Track:

    • Login time

    • Last activity

    • Active / inactive status

  • Automatically expire unused sessions

  • Ensure latest active users are not disturbed

Possible Approaches

Option 1: ASP.NET Session Only (Not Recommended )

  • Uses Session[] variables

  • Fails when:

    • Browser crashes

    • Multiple devices used

  • No central control

Not suitable for concurrent login control

Option 2: Database-Based Session Tracking (Recommended )

  • Store sessions in a database table

  • Track activity per login

  • Kill old sessions safely

This is the correct and professional approach

Database Design

adminmaster Table

Stores user details and allowed session count.

CREATE TABLE adminmaster
(
    Srno INT PRIMARY KEY,
    userid VARCHAR(50),
    password VARCHAR(50),
    SessionCount INT DEFAULT 3
);

UserSessions Table

Tracks each login session.

CREATE TABLE UserSessions
(
    SessionId UNIQUEIDENTIFIER PRIMARY KEY,
    UserSrno INT NOT NULL,
    LoginTime DATETIME,
    LastActivity DATETIME,
    IsActive BIT,
    CONSTRAINT FK_UserSession_Admin
        FOREIGN KEY (UserSrno) REFERENCES adminmaster(Srno)
);

Concept Used

1. GUID-Based Session Identification

  • Each login creates a unique SessionId

  • Avoids conflicts

  • Safe across multiple users and devices

2. Least Recently Used (LRU) Strategy

  • Sessions ordered by LastActivity

  • Oldest inactive session is terminated first

3. Configurable Session Limit

  • Each user has a different SessionCount

  • Can be changed without code modification

Login Flow Logic

  1. Validate username & password

  2. Read allowed session count

  3. Fetch active sessions

  4. If session limit reached:

    • Kill the oldest active session

  5. Create a new session

  6. Allow login

ASP.NET WebForms Login Code

// Validate user
string sqlqry = @"
SELECT Srno, userid, SessionCount
FROM adminmaster (NOLOCK)
WHERE userid=@UserId AND password=@passwrd";

DataSet ds = SqlHelper.ExecuteDataset(con, CommandType.Text, sqlqry, param);

if (ds == null || ds.Tables[0].Rows.Count == 0)
{
    ErrLbl.Text = "Invalid UserID or Password";
    return;
}

// User is valid
DataRow row = ds.Tables[0].Rows[0];
int userSrno = Convert.ToInt32(row["Srno"]);
int maxSession = row["SessionCount"] == DBNull.Value ? 0 : Convert.ToInt32(row["SessionCount"]);

// Get active sessions
string activeQry = @"
SELECT SessionId
FROM UserSessions
WHERE UserSrno=@uid AND IsActive=1
ORDER BY LastActivity";

SqlParameter[] pActive =
{
    new SqlParameter("@uid", userSrno)
};

DataTable dtActive =
    SqlHelper.ExecuteDataset(con, CommandType.Text, activeQry, pActive)
    .Tables[0];

// Kill oldest session if limit reached
if (dtActive.Rows.Count >= maxSession)
{
    Guid oldSessionId =
        Guid.Parse(dtActive.Rows[0]["SessionId"].ToString());

    SqlParameter[] pKill =
    {
        new SqlParameter("@sid", oldSessionId)
    };

    SqlHelper.ExecuteNonQuery(
        con,
        CommandType.Text,
        "UPDATE UserSessions SET IsActive=0 WHERE SessionId=@sid",
        pKill);
}

// Create new session
Guid newSessionId = Guid.NewGuid();

SqlParameter[] pInsert =
{
    new SqlParameter("@sid", newSessionId),
    new SqlParameter("@uid", userSrno)
};

SqlHelper.ExecuteNonQuery(
    con,
    CommandType.Text,
    @"INSERT INTO UserSessions
      VALUES (@sid,@uid,GETDATE(),GETDATE(),1)",
    pInsert);

// Store session
Session["UserSrno"] = userSrno;
Session["SessionId"] = newSessionId;

Response.Redirect("Dashboard.aspx");

Session Activity Update (BasePage Concept)

protected void Page_Load(object sender, EventArgs e)
{
    if (Session["SessionId"] != null)
    {
        SqlParameter[] p =
        {
            new SqlParameter("@sid", Session["SessionId"])
        };

        SqlHelper.ExecuteNonQuery(
            con,
            CommandType.Text,
            @"UPDATE UserSessions
              SET LastActivity=GETDATE()
              WHERE SessionId=@sid AND IsActive=1",
            p);
    }
}

Auto-Expire Inactive Sessions (SQL Job)

UPDATE UserSessions
SET IsActive = 0
WHERE IsActive = 1
AND DATEDIFF(MINUTE, LastActivity, GETDATE()) > 30;

Purpose

  • Handles browser crash

  • Cleans unused sessions

  • Keeps session count accurate

Output Explanation

Scenario 1

User allowed 3 sessions, logs in 3 times
All logins allowed

Scenario 2

User logs in 4th time

  • Oldest session is terminated automatically

  • New session is created

  • Login succeeds

Scenario 3

User closes browser without logout

  • Session becomes inactive after timeout

  • Slot is freed for new login

Advantages of This Approach
  • Secure

  • Scalable

  • Works across devices

  • Handles unexpected logouts

  • Professional enterprise-ready solution

Conclusion

Concurrent login control is a critical security feature in modern applications.
By using a database-driven session tracking mechanism with GUID-based session IDs and LRU session termination, we can efficiently control active logins without impacting user experience.

This solution is simple, clean, and production-ready for ASP.NET WebForms applications.

 

Best ASP.NET Core 10.0 Hosting Recommendation

One of the most important things when choosing a good ASP.NET Core 8.0.11 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...