European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core 10.0 Hosting - HostForLIFE :: Opening the Next Era of Cross-Platform Development with.NET 10

clock November 13, 2025 08:00 by author Peter

Performance, developer productivity, and platform consistency all significantly improve with the advent of.NET 10. For enterprise-grade applications, startups, and large-scale systems that prioritize dependability, this Long-Term Support (LTS) edition offers three years of guaranteed stability and support. In order to maintain its position as the foundation of contemporary cloud-native and AI-driven development, Microsoft 10 places a strong emphasis on the developer experience, cross-platform enhancements, and contemporary performance optimizations.

What's New for Long-Term Support (LTS) in.NET 10?
.NET 10 offers LTS with longer updates and patches, continuing Microsoft's regular release cycle. Because of its stability, it is the ideal option for enterprises planning multi-year installations or cloud-based services that require predictable upgrade pathways.

Improvements to the Runtime and Compiler
The JIT compiler and Common Language Runtime (CLR) have undergone significant performance and efficiency improvements.
Better method call inlining and de-virtualization, which lowers overhead.
More intelligent waste collection with fewer interruptions during demanding tasks.
Better code generation for value types and structs.
Memory optimizations for async methods and array interfaces.
Improved use of CPU-specific instructions for ARM64 and x64 platforms.

Developers will see measurable gains in throughput and latency-sensitive applications, especially APIs, microservices, and financial systems.

SDK and Tooling Upgrades
The .NET 10 SDK introduces smoother workflows and productivity improvements:

  • File-based app support lets developers write and run C# scripts without full project scaffolding.
  • Enhanced command-line tooling with more intelligent tab completion.
  • Simplified global tools management using dotnet tool exec.
  • Faster build and publish times through optimized MSBuild.
  • Extended hot reload capabilities that apply even to project file edits.

These changes allow developers to focus on writing code rather than managing configurations.

File-Based Apps and Scripting

.NET 10 makes lightweight, single-file applications a reality. Developers can run .cs files directly with dotnet run using #sdk or #package directives at the top of the file. This streamlines scripting, prototyping, and building small utilities without creating full projects.

Example
#r "nuget: Newtonsoft.Json"using Newtonsoft.Json;
Console.WriteLine(JsonConvert.SerializeObject(new { Hello = "World" }));

Perfect for DevOps scripts, educational demos, or automation utilities, this feature makes .NET feel as lightweight as Python for small tasks.

ASP.NET Core 10 Innovations
ASP.NET Core receives key upgrades aimed at speed, security, and simplicity:

  • Built-in support for OpenAPI 3.1 with full JSON Schema 2020-12 compatibility.
  • Smarter middleware pipeline for minimal APIs with reduced startup time.
  • Blazor enhancements including offline preloading, better reconnection UI, and unified resource handling.
  • Identity now supports passkeys via FIDO2 and WebAuthn for passwordless authentication.
  • Streamlined integration with Azure Container Apps and Kubernetes for scalable deployment.

Base Class Library Improvements
The Base Class Library (BCL) has been modernized across multiple areas:

  • Span and Memory APIs have broader support across collections.
  • JSON serialization performance is faster and more memory-efficient.
  • File I/O now benefits from OS-level async optimizations.
  • Networking libraries include QUIC enhancements and better HTTP/3 support.
  • System.Security adds new cryptographic primitives and improved token handling.

These upgrades reinforce .NET’s position as one of the most efficient runtimes for high-performance and cloud-native workloads.

C# 14 Language Integration
.NET 10 ships with C# 14, introducing:

  • The field keyword for backing field access in auto-properties.
  • Extension blocks that group extension methods and properties logically.
  • Null-conditional assignment operators (?.=).
  • Implicit conversions for spans.
  • Partial constructors and events for source generator support.

This tight integration between the runtime and language gives developers more expressive, concise, and performant code.

Cross-Platform and Cloud Improvements

  • Improved support for macOS (Apple Silicon), Linux distributions, and containers.
  • Reduced image sizes for container deployments.
  • Better integration with GitHub Actions, Azure DevOps, and CI/CD pipelines.
  • Performance optimization in WebAssembly (WASM) for Blazor apps.

.NET 10 continues to lead in portability, making it ideal for multi-environment applications.

Feature Comparison Table

Area.NET 9.NET 10Impact

Runtime

Fast, general-purpose

Faster, optimized JIT and GC

Up to 15% faster APIs

SDK

Full project needed

File-based scripting and tools

Simplified prototyping

Web

OpenAPI 3.0

OpenAPI 3.1, passkeys, Blazor improvements

More secure, modern web

BCL

Stable APIs

Span/Memory updates, faster JSON

Memory-efficient applications

Language

C# 13

C# 14

Cleaner syntax, fewer bugs

Cloud/DevOps

Docker support

Smaller images, better CI/CD

Cloud-native ready

Migration and Adoption Tips

  • Update your global.json or project file to use <TargetFramework>net10.0</TargetFramework>.
  • Run full regression tests, especially if you rely on unsafe or reflection-heavy code.
  • Test ASP.NET Core projects for middleware or startup differences.
  • For libraries, enable multi-targeting to support .NET 8 and .NET 10 simultaneously.
  • Take advantage of the new hot reload and file-based tools to simplify CI/CD scripts.

Summary and Best Use Cases
.NET 10 marks a balanced evolution—focused on speed, simplicity, and modernization. It strengthens .NET’s cross-platform consistency, reinforces its position as a top-tier enterprise runtime, and makes day-to-day development faster and more enjoyable.

Best suited for:

  • Enterprises needing long-term support and predictable upgrades
  • Cloud-native applications and microservices
  • Developers building modern APIs, AI, or high-performance systems
  • Teams using DevOps automation and single-file tools
  • Framework and library authors adopting C# 14 innovations

If you’re planning your next upgrade or building a new solution, .NET 10 is the best foundation for the next decade of development, fast, stable, and future-ready.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: Implementation of Digital Signatures (DocuSign +.NET)

clock November 10, 2025 08:18 by author Peter

1. Overview — what this article covers
You will learn, step-by-step, how to implement DocuSign eSignature in a .NET (ASP.NET Core) app:

  • key concepts and authentication options (JWT vs Authorization Code)
  • installing and configuring the official DocuSign C# SDK and quickstart links.
  • code sample to create and send an envelope (server-side)
  • embedded signing (recipient view) sample for in-app signing
  • implementing and securing DocuSign Connect (webhook) listener in ASP.NET Core, including validation.
  • testing, error handling, logging, and deployment considerations
  • best practices for security, compliance, and monitoring

I’ll include working C# snippets and notes you can drop into an ASP.NET Core project.

2. Quick background: DocuSign building blocks

  • Account / Integration Key — your DocuSign developer account (Demo) and the App (Integration Key) you create in the DocuSign Admin.
  • Envelope — a container for documents, recipients, and signing fields.
  • Recipient & Tabs — signer (email/name) and signing tab positions (fields).
  • Authentication — DocuSign supports OAuth: Authorization Code and JWT Grant; for server-to-server automated flows, JWT Grant is recommended.
  • Connect (Webhooks) — DocuSign’s webhook system to notify your app of envelope events (completed, declined, etc.).

3. Prerequisites

  1. DocuSign developer account ( https://developers.docusign.com ) — use Demo (sandbox).
  2. Integration Key (app) created in DocuSign Admin with the redirect URI (if using Auth Code) or configured for JWT.
  3. RSA keypair (private key) generated and uploaded to your DocuSign app (for JWT flow).
  4. .NET 6 / .NET 7+ SDK and an ASP.NET Core Web API or MVC project.
  5. NuGet packages: DocuSign.eSign (official C# SDK).

Install the SDK
dotnet add package DocuSign.eSign

4. Authentication: choose the right flow
JWT Grant (service integrations): no user interaction; server exchanges signed JWT for access token. Best for backend services that act on behalf of users or a system account. Requires consent once (admin or user).

Authorization Code Grant (user interactive): redirect user to DocuSign login page, user consents and returns code. Best for user-driven flows where the signer must authenticate with DocuSign.

Recommendation: Use JWT for server flows (creating envelopes programmatically, embedded signing server prepares envelope); use Authorization Code when the user must sign in to DocuSign itself.

5. Getting an access token (JWT) — minimal example

  • Upload your RSA private key to the DocuSign app (Admin → Apps and Keys).
  • Grant consent to your Integration Key for the impersonated user (admin consent or via browser link).

Below is a minimal JWT token flow using the DocuSign SDK helper. The SDK exposes helper classes; you can also form JWT by hand.
using DocuSign.eSign.Client;
using System.Security.Cryptography;
using System.IO;

public class DocuSignAuthService
{
    private readonly string _integrationKey = "<INTEGRATION_KEY>";
    private readonly string _userId = "<IMPERSONATED_USER_ID_GUID>";
    private readonly string _authBasePath = "account-d.docusign.com"; // demo
    private readonly string _privateKeyPath = "docusign_private_key.pem"; // RSA private key (PKCS8)

    public ApiClient CreateApiClient()
    {
        var apiClient = new ApiClient($"https://{_authBasePath}");
        return apiClient;
    }

    public string GetAccessToken()
    {
        var apiClient = CreateApiClient();
        // Read private key
        var privateKey = File.ReadAllText(_privateKeyPath);
        // Requests JWT token. SDK provides a helper method.
        var oauthToken = apiClient.RequestJWTUserToken(
            _integrationKey,
            _userId,
            new List<string> { "signature", "impersonation" },
            System.Text.Encoding.UTF8.GetBytes(privateKey),
            3600);
        return oauthToken.access_token;
    }
}


Notes

  • Use the SDK method RequestJWTUserToken carefully (it expects private key bytes in a certain format). Refer SDK docs for exact signature.
  • In production, store private keys in a secure store (KeyVault, AWS KMS) — never commit to repo.

6. Create and send an envelope (server side)
This example creates an envelope with a single PDF and a single signer.
using DocuSign.eSign.Api;
using DocuSign.eSign.Model;
using DocuSign.eSign.Client;
using System.Collections.Generic;

public class DocuSignService
{
    private readonly string _accountId;
    private readonly ApiClient _apiClient;

    public DocuSignService(string accessToken, string accountId, string basePath = "https://demo.docusign.net/restapi")
    {
        _apiClient = new ApiClient(basePath);
        _apiClient.Configuration.DefaultHeader.Add("Authorization", "Bearer " + accessToken);
        _accountId = accountId;
    }

    public EnvelopeSummary SendEnvelope(byte[] pdfBytes, string signerEmail, string signerName, string signerClientUserId = null)
    {
        var envelopesApi = new EnvelopesApi(_apiClient.Configuration);

        // Document
        var doc = new Document
        {
            DocumentBase64 = Convert.ToBase64String(pdfBytes),
            Name = "Sample Document",
            FileExtension = "pdf",
            DocumentId = "1"
        };

        // Signer
        var signer = new Signer
        {
            Email = signerEmail,
            Name = signerName,
            RecipientId = "1",
            RoutingOrder = "1"
        };

        // Example: add a signHere tab at absolute position
        signer.Tabs = new Tabs
        {
            SignHereTabs = new List<SignHere> {
                new SignHere { DocumentId = "1", PageNumber = "1", XPosition = "100", YPosition = "150" }
            }
        };

        var envDef = new EnvelopeDefinition
        {
            EmailSubject = "Please sign this document",
            Documents = new List<Document> { doc },
            Recipients = new Recipients { Signers = new List<Signer> { signer } },
            Status = "sent" // "sent" to send immediately, "created" to save as draft
        };

        var result = envelopesApi.CreateEnvelope(_accountId, envDef);
        return result;
    }
}

This SendEnvelope returns an EnvelopeSummary with the envelopeId you can store and track.

7. Embedded Signing (In-app signing / Recipient View)
For in-app signing (so the signer stays in your app), create a recipient view (signing URL). This requires the signer to be a embedded recipient — set clientUserId on the signer object.
public string CreateRecipientView(string envelopeId, string signerEmail, string signerName, string returnUrl)
{
    var viewRequest = new RecipientViewRequest
    {
        ReturnUrl = returnUrl, // user returns here after signing
        ClientUserId = "123",  // must match Signer.clientUserId
        AuthenticationMethod = "none",
        UserName = signerName,
        Email = signerEmail
    };

    var envelopesApi = new EnvelopesApi(_apiClient.Configuration);
    var result = envelopesApi.CreateRecipientView(_accountId, envelopeId, viewRequest);
    return result.Url; // redirect user to this URL (or open in iframe if allowed)
}


Notes and security
Use clientUserId to mark recipient as embedded.

For security, generate and validate any tokens you use to let only authenticated users open the signing session.

DocuSign recommends opening the signing URL in a browser window (or iframe with proper CSP and headers) — test cross-origin policies.

8. DocuSign Connect (Webhook) — implement listener in ASP.NET Core
DocuSign Connect delivers envelope events to your public webhook endpoint. Implement a listener to receive JSON/XML notifications and verify them.

Simple controller endpoint
[ApiController]
[Route("api/docusign")]
public class DocuSignController : ControllerBase
{
    [HttpPost("connect")]
    public async Task<IActionResult> Connect()
    {
        // DocuSign may send XML or JSON based on configuration
        string body;
        using (var reader = new StreamReader(Request.Body))
        {
            body = await reader.ReadToEndAsync();
        }

        // Optionally log the raw payload (ensure PII rules)
        // Validate using HMAC or OAuth method (recommended)
        // Process payload: parse envelope status, recipient events etc.

        // Respond with 200 OK quickly
        return Ok();
    }
}

Validation options (recommended):
HMAC-SHA256 validation: DocuSign can be configured to sign the payload and you validate using your shared secret.
OAuth (Connect validation) : DocuSign provides an OAuth method where they send a JWT/assertion you can validate. See DocuSign docs for recommended validation.

Example: validating HMAC
public bool ValidateHmac(string requestBody, string hmacHeader, string secret)
{
    var keyBytes = Encoding.UTF8.GetBytes(secret);
    using var hmac = new HMACSHA256(keyBytes);
    var computed = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
    var computedBase64 = Convert.ToBase64String(computed);
    return string.Equals(computedBase64, hmacHeader, StringComparison.InvariantCulture);
}


Configure DocuSign Admin for Connect to include your HMAC key, and check the header (DocuSign sends it in X-DocuSign-Signature or similar; check current header name in the docs).

9. Processing webhook events — idempotency & state transitions

  • Idempotency : Webhooks may be retried — make processing idempotent by tracking eventId or envelopeId+eventTimestamp.
  • Event types : sent , delivered , completed , declined , voided — handle only the ones you care about.
  • Security : Validate sender before processing (HMAC/OAuth).
  • Queue background work : Return 200 OK quickly; enqueue heavy work (DB updates, notifications) to background processors (Hangfire, Azure Functions, background queue).

10. Local testing with DocuSign Demo & ngrok

  • Use the DocuSign Demo account ( account-d.docusign.com ) for development.
  • For webhook testing, use ngrok to expose your local endpoint and register it in DocuSign Connect settings or in your app’s Connect config.

Example with ngrok
ngrok http 5000
# Use the generated https URL in DocuSign Connect settings


Set the Connect listener to POST to https://<your-ngrok-id>.ngrok.io/api/docusign/connect .

11. Error handling, retries, and monitoring

  • Retry logic : DocuSign retries webhooks on non-2xx responses. Make sure your endpoint responds 200 quickly for accepted payloads.
  • Logging : Log envelopeId, event, timestamp, signer info (avoid logging sensitive fields). Use structured logging (Serilog) and centralize logs (ELK, Seq).
  • Metrics : Monitor envelope creation rates, webhook failures, token expiry errors.
  • Alerts : Alert on repeated webhook delivery failures or auth token rejection rates.

12. Example: End-to-end flow (step diagram)
Developer / System
   └─> Create Envelope (server) --> DocuSign eSignature API (Create Envelope)
         └─> DocuSign sends email to recipient (if remote) OR
         └─> Server requests Recipient View URL (embedded signing) and returns URL to client

User (if embedded) visits signing URL -> signs -> DocuSign updates envelope status

DocuSign Connect (webhook) --> Your webhook endpoint (/api/docusign/connect)
   └─> You validate (HMAC / OAuth) --> enqueue processing (update DB, send notifications)

13. Deployment & CI/CD tips

  • Keep your DocuSign environment variables (Integration Key, AccountId, UserId, Private Key reference, base URL) in secure pipeline secrets (GitHub Actions secrets, Azure Key Vault).
  • If you use JWT, ensure the private key is stored in Key Vault and injected into the build/runtime securely.
  • Automate tests using DocuSign Demo environment (create test envelopes and assert status transitions).
  • Post-deploy step: validate integration by creating a test envelope and checking the Connect webhook roundtrip.

14. Security & compliance recommendations

  • Use HTTPS for all endpoints.
  • Store private keys securely (Azure KeyVault, AWS KMS).
  • Use JWT with short lifetimes and refresh tokens only as needed.
  • Mask or avoid storing PII in logs; use tokenization for sensitive values.
  • Audit trails : record which app/user created/modified envelopes (DocuSign records audit data; also replicate key events to your logs for compliance).
  • Consent and legal : ensure proper consent for e-signature use in your jurisdiction; DocuSign provides guidance for legal enforceability.

15. Testing & QA checklist

  • Create envelopes with different document types (PDF, DOCX) and verify field placement.
  • Test embedded signing flows across browsers and mobile.
  • Test webhook delivery and validation (use invalid signature to confirm rejection).
  • Simulate retries by forcing 5xx from your endpoint to ensure idempotent handling.
  • Validate token refresh and error handling when access token expires.

16. Useful DocuSign resources (official)

  • DocuSign eSignature C# SDK docs and setup.
  • How to get an access token with JWT Grant (official guide).
  • DocuSign Connect (webhooks) implementation details and security.
  • Official GitHub C# client repo & code examples.
  • (These five references are the most load-bearing sources for setup, auth, SDK, and webhook guidance.)

17. Common pitfalls and how to avoid them

  • Private key format mismatch : SDK expects PKCS8/PEM in certain formats — use the SDK examples or convert keys with openssl .
  • Missing user consent for JWT : you must obtain consent for the impersonation user once. Use the consent URL provided by DocuSign or have an admin grant consent.
  • Webhook not reachable (ngrok vs production) : use stable endpoints in production and avoid relying on tunneling for long-term operation.
  • Incorrect envelope status handling : treat "completed" as verified final state; be careful with "delivered" vs "completed".
  • Testing in production : don’t mix demo and production credentials.

18. Advanced topics (next steps)

  • Templates : Create reusable templates in DocuSign and create envelopes from templates to simplify repeated documents.
  • Bulk sending : Use bulk send for high-volume signature workflows.
  • PowerForms : Use PowerForms for self-service signature forms.
  • Hybrid approach : Use Authorization Code for user-initiated flows and JWT for system automation.
  • Connect & storage : Replace heavy webhook processing with serverless functions if you want autoscaling and pay-per-use.

19. Example repository & samples
DocuSign maintains sample repos and quickstarts (C# examples) to help you start quickly — the official GitHub repo and Developer Center have ready examples you can clone and run.

20. Summary — practical checklist before go-live

  • Developer account with Integration Key and correct redirect/consent configured.
  • Private key stored securely and not in source control.
  • Access tokens and refresh strategy implemented (JWT for server).
  • Envelope creation + recipient view flows working end-to-end.
  • Webhook listener implemented, validated (HMAC/OAuth), and idempotent.
  • Logging, monitoring, and alerts in place for failures and retries.
  • QA: cross-browser signing, mobile signing, retry behavior tested.
  • Legal/compliance: retention, consent, and audit trail verified.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: ASP.NET Core Blazor Evolution: Interactive Web User Interfaces in 2025

clock November 5, 2025 07:10 by author Peter

Microsoft Blazor has emerged as one of the most fascinating technologies in the field of contemporary web development. Blazor makes it simpler for.NET developers to use the same language for front-end and back-end development by enabling them to create interactive web user interfaces (UIs) using C# rather than JavaScript.

Blazor has developed into a unified web user interface framework that supports server-side, client-side, and static rendering under one roof with ASP.NET Core 9 (2025).

Blazor: What is it?
Blazor is an ASP.NET Core-based web user interface framework.
It renders HTML using Razor components (.razor files), and you can handle logic directly in C#.

Earlier, Blazor came in two major forms:

Blazor TypeRuns OnDescription
Blazor Server Server UI updates handled over SignalR connection
Blazor WebAssembly (WASM) Browser Runs C# directly inside browser using WebAssembly

Now, in 2025, Microsoft has merged these concepts into a unified Blazor model.

Evolution of Blazor (Timeline)

Blazor 2018 → Blazor Server 2019 → Blazor WebAssembly 2020
      ↓
Blazor Hybrid (MAUI) 2022 → Blazor United 2023 → Blazor in ASP.NET Core 9 (2025)

Each version improved performance, interactivity, and tooling.
Blazor 2025 brings them all together into a single powerful framework.

Unified Rendering Model in ASP.NET Core 9

In ASP.NET Core 9, Blazor offers three rendering modes within the same app:

ModeDescriptionUse Case
Static Rendering Pre-renders HTML on the server Best for SEO and fast initial load
Interactive Server UI interactions handled on server (SignalR) Ideal for intranet or internal tools
Interactive WebAssembly Runs C# in browser via WebAssembly Best for offline or client-heavy apps

You can mix and match these modes per component.

Flowchart: Unified Blazor Rendering

┌────────────────────┐
          │    Blazor Page     │
          └──────┬─────────────┘
                 │
       ┌─────────┴─────────┐
       │                   │
┌─────────────┐     ┌──────────────┐
│ Server Mode │     │ WebAssembly  │
└─────────────┘     └──────────────┘
       │                   │
       ▼                   ▼
 SignalR Updates       Browser Execution
       │                   │
       └─────────► Unified Blazor UI ◄─────────┘

This makes Blazor extremely flexible for any type of web app — from dashboards to public portals.

Visual Representation (UI Diagram)

+-------------------------------------------------------+
| ASP.NET Core 9 App                                   |
|-------------------------------------------------------|
|  Controllers | APIs | Razor Components | Middleware   |
|-------------------------------------------------------|
|  Shared C# Models and Services                       |
|-------------------------------------------------------|
|  Blazor Rendering Modes                              |
|   → Static | Interactive Server | WASM               |
+-------------------------------------------------------+

This structure lets developers use one shared C# codebase for everything.

Example: Hybrid Rendering in a Blazor Component

@page "/dashboard"
@rendermode InteractiveServer
<h3>Dashboard</h3>

<p>Welcome @userName!</p>

<button @onclick="LoadData">Load Data</button>

@if (data != null)
{
    <ul>
        @foreach (var item in data)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    private string userName = "Rajesh";
    private List<string>? data;

    private void LoadData()
    {
        data = new List<string> { "Order 1", "Order 2", "Order 3" };
    }
}

This example uses Interactive Server Mode — meaning data binding and events are managed from the server in real time.

Performance Improvements in 2025

  • Streaming Rendering: HTML starts rendering even before backend finishes.
  • Auto Prerendering: Faster initial load with deferred interactivity.
  • Smaller WASM Payload: Reduced download size for Blazor WebAssembly apps.
  • Enhanced Hot Reload: Changes apply instantly during development.
  • Server + Client Sync: Same Razor components can render on both ends.

Integration with .NET MAUI and Hybrid Apps
Blazor 2025 fully integrates with .NET MAUI — so developers can build:

  • Web apps
  • Desktop apps (Windows, Mac)
  • Mobile apps (Android, iOS)

all using the same Blazor components.

Blazor + Security Enhancements
ASP.NET Core 9 improves security integration:

  • Built-in authentication/authorization with OpenID Connect
  • Better CSRF protection
  • Improved Identity scaffolding
  • SignalR security for real-time Blazor Server apps

 

Why Blazor is the Future of .NET Web Development

BenefitsDescription
Single Language C# everywhere (client + server)
No JavaScript dependency Still supports interop when needed
High performance Optimized in .NET 9 runtime
Cross-platform Web, Desktop, Mobile
Modern development experience Hot reload, unified project templates

Flow Summary Diagram

Frontend (UI)  →  Blazor Components (.razor)
       ↓
Backend (Logic) → ASP.NET Core Controllers / APIs
       ↓
Database (Data) → SQL Server / EF Core
       ↓
Output          → Responsive Interactive Web App

Conclusion

Blazor in ASP.NET Core 9 (2025) represents the next stage of .NET Web UI evolution. It unifies server, client, and hybrid rendering, offers high performance, and reduces complexity for developers. If you’re a .NET or C# developer, learning Blazor in 2025 is one of the smartest moves to stay future-ready. Pro Tip: Combine Blazor with SignalR, EF Core, and Azure to build modern, full-stack, real-time applications all powered by .NET 9.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: Fibonacci Series Generator (Example for ASP.NET WebForms)

clock October 31, 2025 08:09 by author Peter

Step 1: Design (Default.aspx)
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FibonacciWebApp.Default" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Fibonacci Series Generator</title>
    <style>
        body {
            font-family: Arial;
            background-color: #f5f6fa;
            text-align: center;
            padding: 40px;
        }
        .container {
            background-color: #fff;
            border-radius: 10px;
            padding: 30px;
            width: 450px;
            margin: auto;
            box-shadow: 0px 0px 10px #ccc;
        }
        h2 { color: #1A2A80; }
        input, button {
            margin: 10px;
            padding: 8px;
            font-size: 16px;
        }
        .result {
            color: green;
            font-weight: bold;
            word-wrap: break-word;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <div class="container">
            <h2>Fibonacci Series Generator</h2>
            <asp:Label ID="Label1" runat="server" Text="Enter Number of Terms: "></asp:Label>
            <asp:TextBox ID="txtTerms" runat="server"></asp:TextBox>
            <br />
            <asp:Button ID="btnGenerate" runat="server" Text="Generate Fibonacci Series" OnClick="btnGenerate_Click" />
            <br /><br />
            <asp:Label ID="lblResult" runat="server" CssClass="result"></asp:Label>
        </div>
    </form>
</body>
</html>

Step 2: Backend Logic (Default.aspx.cs)
using System;
using System.Text;

namespace FibonacciWebApp
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected void btnGenerate_Click(object sender, EventArgs e)
        {
            try
            {
                int n = int.Parse(txtTerms.Text);
                if (n <= 0)
                {
                    lblResult.Text = " Please enter a positive integer greater than zero.";
                    return;
                }

                lblResult.Text = $"Fibonacci Series up to {n} terms:<br/>" + GenerateFibonacci(n);
            }
            catch (FormatException)
            {
                lblResult.Text = " Please enter a valid number.";
            }
            catch (Exception ex)
            {
                lblResult.Text = $"Error: {ex.Message}";
            }
        }

        private string GenerateFibonacci(int n)
        {
            int first = 0, second = 1, next;
            StringBuilder series = new StringBuilder();
            series.Append(first + " ");

            for (int i = 1; i < n; i++)
            {
                series.Append(second + " ");
                next = first + second;
                first = second;
                second = next;
            }

            return series.ToString();
        }
    }
}

Real-Time Flow Explanation

  • The user enters how many Fibonacci terms they want (for example, 10).
  • On clicking Generate, the backend runs GenerateFibonacci() method.
  • The method uses a loop and a StringBuilder to efficiently build the Fibonacci series string.
  • The output is displayed on the webpage dynamically.

Output Example
If user enters 8,

Fibonacci Series up to 8 terms:
0 1 1 2 3 5 8 13

Real-Time Use Case

This logic can be used in:

  • Math learning web portals for students.
  • Online coding practice systems (like HackerRank/LeetCode practice pages).
  • Company coding tests in WebForms environments.
  • Dashboard Widgets that show numerical sequences for demonstrations.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: The REPR Pattern in Contemporary .NET with Fast Endpoints

clock October 27, 2025 07:52 by author Peter

The HTTP layer becomes a thin adaptor, intentions are explicit, and invariants reside in the domain in mature.NET systems that converge on a feature-first structure. Request > Entity > Processor > Response is one name for that form. It is what those concepts typically result from when scaled across teams and time; it does not take the place of Clean Architecture or CQRS. In order to keep the surface modest and the business logic at the center, this article describes REPR as a fundamental design and shows how to express it precisely in modern.NET using FastEndpoints.

Clarity is the aim, with each use-case being represented by a slice made up of a consumer-shaped contract (Response), a truthful domain core (Entity), a use case script (Processor), and an intent (Request). The slice itself conveys the meaning, so incidental abstractions, controllers, and service layers disappear. As a result, the codebase reads by verbs instead of layers, which is the logical unit of reasoning for both engineering and products.

Why REPR Emerges from Clean Architecture and CQRS?
Clean Architecture pushes dependencies inward and centres decisions in the domain. CQRS separates reads and writes to sharpen intent. Yet both leave open the practical question: What is the smallest useful unit of a feature? REPR answers with a compact pipeline. The Request captures intent without leaking storage concerns. The Entity enforces business rules and invariants. The Processor orchestrates precisely what the use-case demands and nothing more. The Response returns a business outcome suitable for its consumer, not an accidental snapshot of internal state.

This alignment reduces cognitive load. A feature has a single entry and a single exit, and the domain is the only place allowed to change truth. Cross-cutting concerns become pipeline steps around the Processor instead of boilerplate inside it. Teams scale by owning sets of verbs rather than slices of a layered cake.

The Feature Slice: Directory Shape and Mental Model

A coherent slice contains only what it needs, co-located for discoverability. An example for approving a Submission (the same structure fits quotes, orders, pricing, or claims):
/Features/Submissions/Approve

ApproveSubmissionRequest.cs

ApproveSubmissionResponse.cs

ApproveSubmissionValidator.cs

ApproveSubmissionEndpoint.cs

ApproveSubmissionProcessor.cs

Submission.cs

ISubmissionRepository.cs

EfSubmissionRepository.cs


Each file is small and purposeful. The Endpoint adapts transport to intent. The Processor applies policy and coordinates domain work. The Entity owns invariants and protects truth. The repository is a narrow port that returns the shapes the domain needs.

The REPR Flow
The Request expresses what the caller wants. The Processor asks the Entity to perform domain operations under invariant checks. The Repository persists or projects as needed. The Response communicates the outcome in consumer terms. The HTTP or messaging edge is merely a conduit.

Request and Response: Intent and Outcome
Requests model intent; they are not transport bags or EF rows. Responses model what consumers need; they are not entities.
// Request > intent, not storage schema.

public sealed record ApproveSubmissionRequest(long SubmissionId, string ApprovedBy);

// Response > consumer-shaped business outcome.

public sealed record ApproveSubmissionResponse(

long SubmissionId,

DateTime ApprovedAtUtc,

string ApprovedBy,

string StatusMessage);


Names should read like language in the domain. Versioning the Response is an explicit contract decision, not an accident.

Entity: The Only Place Allowed to Change Truth
Entities own invariants. Processors never toggle fields directly; they call domain methods that enforce rules atomically.

public sealed class Submission(long id, DateTime createdAtUtc)

{

public long Id { get; } = id;

public DateTime CreatedAtUtc { get; } = createdAtUtc;

public bool IsApproved { get; private set; }

public DateTime? ApprovedAtUtc { get; private set; }

public string? ApprovedBy { get; private set; }

public Result Approve(string approver, DateTime nowUtc)

{

if (IsApproved)

return Result.Fail("Submission is already approved.");

if (string.IsNullOrWhiteSpace(approver))

return Result.Fail("Approver is required.");

IsApproved  = true;

ApprovedAtUtc = nowUtc;

ApprovedBy  = approver;

return Result.Ok();

}

}

A small, explicit domain method prevents entire categories of drift, such as direct flag mutation scattered across handlers.

Repository Port: Narrow and Purposeful

Commands require Entities to enforce invariants; queries typically require projections. A port keeps the Processor independent of storage concerns.

public interface ISubmissionRepository

{

Task<Submission?> GetAsync(long id, CancellationToken ct);

Task SaveAsync(Submission Submission, CancellationToken ct);

}


Implementation choices (EF Core, Dapper, external service) do not change the Processor surface. N+1 issues are solved inside the repository contract, not leaked into callers.

Processor: The Use-Case Script
The Processor composes the use-case: load, ask, change, persist, reply. It does not perform transport work or data access gymnastics; it orchestrates policy with the Entity.

public interface IClock { DateTime UtcNow { get; } }

public sealed class SystemClock : IClock { public DateTime UtcNow => DateTime.UtcNow; }

public sealed class ApproveSubmissionProcessor(ISubmissionRepository repo, IClock clock)

{

public async Task<Result<ApproveSubmissionResponse>> Handle(ApproveSubmissionRequest req, CancellationToken ct)

{

var Submission = await repo.GetAsync(req.SubmissionId, ct);

if (Submission is null)

return Result.Fail<ApproveSubmissionResponse>("Submission not found.");

var approved = Submission.Approve(req.ApprovedBy, clock.UtcNow);

if (approved.IsFailure)

return approved.Cast<ApproveSubmissionResponse>();

await repo.SaveAsync(Submission, ct);

return Result.Ok(new ApproveSubmissionResponse(

Submission.Id,

Submission.ApprovedAtUtc!.Value,

Submission.ApprovedBy!,

"Approved"));

}

}


This class should remain short. If it grows, the Entity likely needs a new domain operation to absorb rules.

FastEndpoints: The Precise HTTP Adapter
FastEndpoints models the HTTP edge with minimal ceremony. The Endpoint accepts the Request and delegates to the Processor, returning a Response or an error mapping.

using FastEndpoints;

public sealed class ApproveSubmissionEndpoint(ApproveSubmissionProcessor processor)

: Endpoint<ApproveSubmissionRequest, ApproveSubmissionResponse>

{

public override void Configure()

{

Post("/Submission/Submissions/{SubmissionId:long}/approve");

Version(1);

Summary(s =>

{

s.Summary = "Approve a Submission";

s.Description = "Approves a Submission if business rules allow.";

s.ExampleRequest = new ApproveSubmissionRequest(42, "Approver01");

});

}

public override async Task HandleAsync(ApproveSubmissionRequest req, CancellationToken ct)

{

var result = await processor.Handle(req, ct);

if (result.IsSuccess)

await SendOkAsync(result.Value, ct);

else

await SendErrorsAsync(400, result.Message);

}

}


The Endpoint remains transport-centric. It does not read data stores, compute business rules, or create hidden dependencies.

Validation: Polite at the Edge, Strict in the Core

Transport-level validation protects the Processor from malformed input, and invariants in the Entity protect the core from impossible states.
using FastEndpoints;

using FluentValidation;

public sealed class ApproveSubmissionValidator : Validator<ApproveSubmissionRequest>

{

public ApproveSubmissionValidator()

{

RuleLevelCascadeMode = CascadeMode.Stop;

RuleFor(r => r.SubmissionId).GreaterThan(0);

RuleFor(r => r.ApprovedBy).NotEmpty().MaximumLength(200);

}

}

This split is essential. It prevents duplication and avoids shifting business rules into validators where they cannot be enforced consistently.

Program Setup: Small Surface, Clear Wiring

Minimal registration with DI and FastEndpoints keeps the composition explicit and discoverable.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MyDbContext>(...);

builder.Services.AddScoped<ISubmissionRepository, EfSubmissionRepository>();

builder.Services.AddScoped<ApproveSubmissionProcessor>();

builder.Services.AddSingleton<IClock, SystemClock>();

builder.Services.AddFastEndpoints();

builder.Services.AddFluentValidationAutoValidation();

builder.Services.AddSwaggerDoc();

var app = builder.Build();

app.UseAuthorization();

app.UseFastEndpoints();

app.UseOpenApi();

app.UseSwaggerUi3();

app.Run();


New slices add themselves by file convention; the application is composed of features rather than layers.

Query Slices: Reads Benefit Even More
Read models become explicit, projection-first, and immune to accidental IQueryable leakage. The Request captures query intent, the Processor (or Endpoint for simple reads) projects to a consumer shape.
public sealed record GetSubmissionsRequest(int Page = 1, int PageSize = 20);

public sealed record SubmissionSummary(long Id, bool IsApproved, DateTime CreatedAtUtc);

public sealed class GetSubmissionsEndpoint(MyDbContext db)

: Endpoint<GetSubmissionsRequest, IReadOnlyList<SubmissionSummary>>

{

public override void Configure()

{

Get("/uwSubmission/Submissions");

Version(1);

}

public override async Task HandleAsync(GetSubmissionsRequest req, CancellationToken ct)

{

if (req.Page <= 0 || req.PageSize is <= 0 or > 200)

{

await SendErrorsAsync(400, "Invalid paging");

return;

}

var data = await db.Submissions

.AsNoTracking()

.OrderByDescending(h => h.CreatedAtUtc)

.Skip((req.Page - 1) * req.PageSize)

.Take(req.PageSize)

.Select(h => new SubmissionSummary(h.Id, h.IsApproved, h.CreatedAtUtc))

.ToListAsync(ct);

await SendOkAsync(data, ct);

}

}


Read and write slices share the same mental model and directory shape, simplifying navigation and ownership.

Cross-Cutting Concerns: Pipeline, Not Boilerplate

In REPR, cross-cutting concerns belong around the Processor as pipeline steps or behaviours, not inside it. FastEndpoints supports pre/post processors at the HTTP edge; a mediator or composition pattern can provide behaviours around the Processor for logging, correlation, authorisation, idempotency, and resilience.

A simple correlation pre-processor:
public sealed class CorrelationPreProcessor<TReq, TRes>(

ILogger<CorrelationPreProcessor<TReq,TRes>> log)

: IPreProcessor<Endpoint<TReq,TRes>>

where TReq : notnull

{

public Task PreProcessAsync(Endpoint<TReq,TRes> ep, CancellationToken ct)

{

var cid = ep.HttpContext.TraceIdentifier;

ep.HttpContext.Items["cid"] = cid;

log.LogInformation("Handling {Request} cid={Cid}", typeof(TReq).Name, cid);

return Task.CompletedTask;

}

}


Applied globally or per endpoint, this keeps transport concerns at the edge and business concerns in the core.

Events and Outbox: Facts Out, Reliably

Clarity is the aim, with each use-case being represented by a slice made up of a consumer-shaped contract (Response), a truthful domain core (Entity), a use case script (Processor), and an intent (Request). The slice itself conveys the meaning, so incidental abstractions, controllers, and service layers disappear. As a result, the codebase reads by verbs instead of layers, which is the logical unit of reasoning for both engineering and products.

This pattern aligns neatly with REPR’s flow: intent in, invariant-checked state change, facts recorded, outcome returned.

Testing: Small, Fast, and Honest

REPR improves testability by shrinking surface areas. Entities are tested directly via domain methods. Processors are tested with fake repositories and clocks, asserting on Result<T> and domain state. Endpoints can be verified with FastEndpoints’ in-memory host where HTTP-level assurance is required, but business logic never depends on a web server to be testable.

A Processor test is concise and meaningful:
public async Task Approve_Sets_State_And_Returns_Response()

{

var now = new DateTime(2025, 10, 24, 12, 0, 0, DateTimeKind.Utc);

var clock = Substitute.For<IClock>(); clock.UtcNow.Returns(now);

var Submission = new Submission(42, now.AddDays(-1));

var repo = Substitute.For<ISubmissionRepository>();

repo.GetAsync(42, Arg.Any<CancellationToken>()).Returns(Submission);

var processor = new ApproveSubmissionProcessor(repo, clock);

var result = await processor.Handle(new ApproveSubmissionRequest(42, "PK"), CancellationToken.None);

result.IsSuccess.ShouldBeTrue();

Submission.IsApproved.ShouldBeTrue();

Submission.ApprovedAtUtc.ShouldBe(now);

Submission.ApprovedBy.ShouldBe("PK");

await repo.Received(1).SaveAsync(Submission, Arg.Any<CancellationToken>());

}


The test demonstrates intent, rule enforcement, and persistence orchestration without scaffolding overhead.

Anti-Patterns to Avoid

A few pitfalls undermine the benefits:

  • Requests that mirror database rows rather than intent encourage accidental coupling and bloat.
  • Processors that toggle fields directly instead of calling domain methods bypass invariants.
  • Repositories that return IQueryable leak persistence concerns and invite N+1 issues outside the boundary.
  • Responses that expose Entities create fragile contracts and accidental internal coupling.
  • Generic “BaseRepository<T>” or “Service” classes that centralise convenience instead of domain language lead to anaemic designs.

REPR’s strength is clarity; resist abstractions that obscure intent.

Impact and Ownership

Eams use verbs and results to reason. When a slice of a story—Request, Processor, Response—with invariants visible in an entity, PRs examine it more quickly. Because logs and analytics match Request types and semantic processes rather than raw URLs, observability improves. Instead of mapping to controllers or horizontal layers, ownership maps to features, allowing for autonomous delivery free from unintentional contention.

REPR is not a framework and does not require a mediator, controller, or a particular storage engine. It is the minimal shape a serious codebase adopts when Clean Architecture and CQRS are applied with discipline and scaled across a product’s lifetime. Expressed with FastEndpoints, the HTTP edge becomes quiet and intention-based, and the domain regains custody of truth.

The practical move is simple: for the next non-trivial feature, create a slice named for the verb. Write the Request so the intent is explicit. Put rules in an Entity method. Keep the Processor short and orchestral. Return a Response that a consumer would actually want. Repeat until the architecture fades into the furniture and only the product remains visible.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: Mastery of ASP.NET Core Middleware: Custom Auth, Pipeline Control, and Logging

clock October 23, 2025 08:35 by author Peter

Introduction to Middleware
What is Middleware?

Middleware in ASP.NET Core is software components that are assembled into an application pipeline to handle requests and responses. Each component:

  • Chooses whether to pass the request to the next component
  • Can perform work before and after the next component

// Simple middleware concept
app.Use(async (context, next) =>
{
    // Do work before the next middleware
    await next.Invoke();
    // Do work after the next middleware
});


Real-Life Analogy: Airport Security Check
Think of middleware as airport security checks:

  • Ticket Verification - Check if you have a valid ticket
  • Security Screening - Scan luggage and personal items
  • Passport Control - Verify travel documents
  • Boarding Gate - Final check before boarding

Each step can either pass you to the next or reject you entirely.

2. Understanding the Request Pipeline
Basic Pipeline Structure

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Pipeline Flow Visualization

Request → Middleware 1 → Middleware 2 → ... → Middleware N → Endpoint → Response

Lifecycle Example: E-Commerce Request
// Program.cs - Complete pipeline setup
var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();

var app = builder.Build();

// Configure pipeline
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRequestLogging(); // Custom middleware
app.UsePerformanceMonitoring(); // Custom middleware

app.MapControllers();

app.Run();

3. Built-in Middleware Components
Essential Built-in Middleware

3.1 Static Files Middleware
// Serve static files like HTML, CSS, JavaScript, images
app.UseStaticFiles();

// Serve files from custom directory
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
    RequestPath = "/StaticFiles"
});


3.2 Routing Middleware
app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapGet("/hello", async context =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
});


3.3 Authentication & Authorization
app.UseAuthentication();
app.UseAuthorization();

3.4 CORS Middleware
// Add CORS service
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll",
        builder =>
        {
            builder.AllowAnyOrigin()
                   .AllowAnyMethod()
                   .AllowAnyHeader();
        });
});

// Use CORS middleware
app.UseCors("AllowAll");


4. Creating Custom Middleware
4.1 Inline Middleware

app.Use(async (context, next) =>
{
    var startTime = DateTime.UtcNow;

    // Call the next middleware
    await next();

    var endTime = DateTime.UtcNow;
    var duration = endTime - startTime;

    Console.WriteLine($"Request took: {duration.TotalMilliseconds}ms");
});


4.2 Class-Based Middleware
Basic Custom Middleware Class
// Custom logging middleware
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var startTime = DateTime.UtcNow;

        _logger.LogInformation($"Starting request: {context.Request.Method} {context.Request.Path}");

        try
        {
            await _next(context);
        }
        finally
        {
            var endTime = DateTime.UtcNow;
            var duration = endTime - startTime;

            _logger.LogInformation(
                $"Completed request: {context.Request.Method} {context.Request.Path} " +
                $"with status: {context.Response.StatusCode} in {duration.TotalMilliseconds}ms");
        }
    }
}

// Extension method for easy use
public static class RequestLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggingMiddleware>();
    }
}


4.3 Factory-Based Middleware
// Factory-activated middleware
public class TimingMiddleware : IMiddleware
{
    private readonly ILogger<TimingMiddleware> _logger;

    public TimingMiddleware(ILogger<TimingMiddleware> logger)
    {
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var stopwatch = Stopwatch.StartNew();

        _logger.LogInformation($"Starting request: {context.Request.Path}");

        await next(context);

        stopwatch.Stop();
        _logger.LogInformation($"Request {context.Request.Path} completed in {stopwatch.ElapsedMilliseconds}ms");
    }
}

// Register in DI container
builder.Services.AddTransient<TimingMiddleware>();


5. Real-World Middleware Examples
5.1 API Key Authentication Middleware

// API Key Authentication Middleware
public class ApiKeyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IConfiguration _configuration;
    private const string API_KEY_HEADER = "X-API-Key";

    public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
    {
        _next = next;
        _configuration = configuration;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Skip API key check for certain paths
        if (context.Request.Path.StartsWithSegments("/public"))
        {
            await _next(context);
            return;
        }

        if (!context.Request.Headers.TryGetValue(API_KEY_HEADER, out var extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("API Key is missing");
            return;
        }

        var validApiKeys = _configuration.GetSection("ValidApiKeys").Get<List<string>>();

        if (!validApiKeys.Contains(extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Invalid API Key");
            return;
        }

        await _next(context);
    }
}

// Extension method
public static class ApiKeyMiddlewareExtensions
{
    public static IApplicationBuilder UseApiKeyAuthentication(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ApiKeyMiddleware>();
    }
}

5.2 Request/Response Logging Middleware
// Detailed request/response logging middleware
public class DetailedLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<DetailedLoggingMiddleware> _logger;

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

    public async Task InvokeAsync(HttpContext context)
    {
        // Log request
        await LogRequest(context);

        // Copy original response body stream
        var originalBodyStream = context.Response.Body;

        using var responseBody = new MemoryStream();
        context.Response.Body = responseBody;

        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred");
            throw;
        }
        finally
        {
            // Log response
            await LogResponse(context, responseBody, originalBodyStream);
        }
    }

    private async Task LogRequest(HttpContext context)
    {
        context.Request.EnableBuffering();

        var request = $"{context.Request.Method} {context.Request.Path}{context.Request.QueryString}";
        var headers = string.Join(", ", context.Request.Headers.Select(h => $"{h.Key}: {h.Value}"));

        _logger.LogInformation($"Request: {request}");
        _logger.LogDebug($"Headers: {headers}");

        if (context.Request.ContentLength > 0)
        {
            using var reader = new StreamReader(context.Request.Body, Encoding.UTF8,
                leaveOpen: true);
            var body = await reader.ReadToEndAsync();
            context.Request.Body.Position = 0;

            _logger.LogDebug($"Request Body: {body}");
        }
    }

    private async Task LogResponse(HttpContext context, MemoryStream responseBody, Stream originalBodyStream)
    {
        responseBody.Position = 0;
        var responseText = await new StreamReader(responseBody).ReadToEndAsync();
        responseBody.Position = 0;

        _logger.LogInformation($"Response: {context.Response.StatusCode}");
        _logger.LogDebug($"Response Body: {responseText}");

        await responseBody.CopyToAsync(originalBodyStream);
        context.Response.Body = originalBodyStream;
    }
}

5.3 Rate Limiting Middleware
// Simple rate limiting middleware
public class RateLimitingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly Dictionary<string, List<DateTime>> _requestLog = new();
    private readonly object _lockObject = new object();
    private readonly int _maxRequests = 100;
    private readonly TimeSpan _timeWindow = TimeSpan.FromMinutes(1);

    public RateLimitingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var clientIp = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";

        if (IsRateLimited(clientIp))
        {
            context.Response.StatusCode = 429; // Too Many Requests
            await context.Response.WriteAsync("Rate limit exceeded. Please try again later.");
            return;
        }

        await _next(context);
    }

    private bool IsRateLimited(string clientIp)
    {
        lock (_lockObject)
        {
            var now = DateTime.UtcNow;

            if (!_requestLog.ContainsKey(clientIp))
            {
                _requestLog[clientIp] = new List<DateTime>();
            }

            // Remove old requests outside the time window
            _requestLog[clientIp].RemoveAll(time => now - time > _timeWindow);

            // Check if over limit
            if (_requestLog[clientIp].Count >= _maxRequests)
            {
                return true;
            }

            // Log this request
            _requestLog[clientIp].Add(now);
            return false;
        }
    }
}

5.4 Correlation ID Middleware
// Correlation ID middleware for request tracking
public class CorrelationIdMiddleware
{
    private readonly RequestDelegate _next;
    private const string CORRELATION_ID_HEADER = "X-Correlation-ID";

    public CorrelationIdMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, ILogger<CorrelationIdMiddleware> logger)
    {
        var correlationId = GetOrCreateCorrelationId(context);

        // Add correlation ID to response headers
        context.Response.OnStarting(() =>
        {
            context.Response.Headers[CORRELATION_ID_HEADER] = correlationId;
            return Task.CompletedTask;
        });

        // Add to logger scope
        using (logger.BeginScope("{CorrelationId}", correlationId))
        {
            await _next(context);
        }
    }

    private string GetOrCreateCorrelationId(HttpContext context)
    {
        if (context.Request.Headers.TryGetValue(CORRELATION_ID_HEADER, out var correlationId))
        {
            return correlationId!;
        }

        return Guid.NewGuid().ToString();
    }
}

6. Middleware Ordering and Execution
Critical Middleware Order

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 1. Exception/Error Handling
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    // 2. HTTPS Redirection
    app.UseHttpsRedirection();

    // 3. Static Files
    app.UseStaticFiles();

    // 4. Routing
    app.UseRouting();

    // 5. Custom Middleware (Authentication, Logging, etc.)
    app.UseCorrelationId();
    app.UseRequestLogging();
    app.UseApiKeyAuthentication();

    // 6. CORS
    app.UseCors("AllowAll");

    // 7. Authentication & Authorization
    app.UseAuthentication();
    app.UseAuthorization();

    // 8. Endpoints
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
    });
}


Ordering Impact Example
// Wrong order - Authentication won't work properly
app.UseEndpoints(endpoints => { /* ... */ });
app.UseAuthentication(); // This won't be called for endpoint requests

// Correct order
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { /* ... */ });

7. Advanced Middleware Patterns
7.1 Conditional Middleware

// Conditional middleware based on request path
public class ConditionalMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _pathStartsWith;

    public ConditionalMiddleware(RequestDelegate next, string pathStartsWith)
    {
        _next = next;
        _pathStartsWith = pathStartsWith;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Path.StartsWithSegments(_pathStartsWith))
        {
            // Custom logic for specific paths
            context.Items["CustomFeature"] = "Enabled";
        }

        await _next(context);
    }
}

// Usage with multiple conditions
app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"),
    apiApp =>
    {
        apiApp.UseMiddleware<ApiSpecificMiddleware>();
        apiApp.UseRouting();
        apiApp.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    });

7.2 Branching Middleware Pipeline
// Branching for different pipeline configurations
app.Map("/admin", adminApp =>
{
    adminApp.UseMiddleware<AdminAuthenticationMiddleware>();
    adminApp.UseRouting();
    adminApp.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "admin",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
});

app.Map("/api", apiApp =>
{
    apiApp.UseMiddleware<ApiKeyMiddleware>();
    apiApp.UseRouting();
    apiApp.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
});

7.3 Middleware with Configuration
// Configurable middleware
public class ConfigurableMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ConfigurableMiddlewareOptions _options;

    public ConfigurableMiddleware(RequestDelegate next, IOptions<ConfigurableMiddlewareOptions> options)
    {
        _next = next;
        _options = options.Value;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (_options.EnableFeature && context.Request.Path.StartsWithSegments(_options.ApplyToPath))
        {
            // Apply middleware logic
            await ApplyCustomLogic(context);
        }

        await _next(context);
    }

    private async Task ApplyCustomLogic(HttpContext context)
    {
        // Custom implementation
        context.Items["CustomFeatureApplied"] = true;
    }
}

public class ConfigurableMiddlewareOptions
{
    public bool EnableFeature { get; set; }
    public string ApplyToPath { get; set; } = "/api";
}

// Registration
builder.Services.Configure<ConfigurableMiddlewareOptions>(options =>
{
    options.EnableFeature = true;
    options.ApplyToPath = "/secure";
});


8. Error Handling Middleware
Global Exception Handling Middleware

// Comprehensive error handling middleware
public class GlobalExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionHandlerMiddleware> _logger;
    private readonly IWebHostEnvironment _env;

    public GlobalExceptionHandlerMiddleware(
        RequestDelegate next,
        ILogger<GlobalExceptionHandlerMiddleware> logger,
        IWebHostEnvironment env)
    {
        _next = next;
        _logger = logger;
        _env = env;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred");
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";

        var errorResponse = new ErrorResponse
        {
            Success = false,
            Error = "An unexpected error occurred"
        };

        switch (exception)
        {
            case UnauthorizedAccessException:
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                errorResponse.Error = "Unauthorized access";
                break;

            case KeyNotFoundException:
                context.Response.StatusCode = StatusCodes.Status404NotFound;
                errorResponse.Error = "Resource not found";
                break;

            case ValidationException vex:
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                errorResponse.Error = "Validation failed";
                errorResponse.Details = vex.Errors;
                break;

            default:
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                if (_env.IsDevelopment())
                {
                    errorResponse.Details = exception.ToString();
                }
                break;
        }

        var json = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });

        await context.Response.WriteAsync(json);
    }
}

public class ErrorResponse
{
    public bool Success { get; set; }
    public string Error { get; set; } = string.Empty;
    public object? Details { get; set; }
}


Custom Exception Classes
// Custom exception for business logic
public class BusinessException : Exception
{
    public string ErrorCode { get; }

    public BusinessException(string errorCode, string message) : base(message)
    {
        ErrorCode = errorCode;
    }
}

public class ValidationException : Exception
{
    public Dictionary<string, string[]> Errors { get; }

    public ValidationException(Dictionary<string, string[]> errors)
        : base("Validation failed")
    {
        Errors = errors;
    }
}


9. Performance Optimization
Response Caching Middleware

// Custom response caching middleware
public class ResponseCachingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IMemoryCache _cache;
    private readonly ILogger<ResponseCachingMiddleware> _logger;

    public ResponseCachingMiddleware(
        RequestDelegate next,
        IMemoryCache cache,
        ILogger<ResponseCachingMiddleware> logger)
    {
        _next = next;
        _cache = cache;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cacheKey = GenerateCacheKey(context.Request);

        if (context.Request.Method == "GET" && _cache.TryGetValue(cacheKey, out byte[]? cachedResponse))
        {
            _logger.LogInformation($"Serving cached response for {context.Request.Path}");
            context.Response.ContentType = "application/json";
            context.Response.ContentLength = cachedResponse?.Length ?? 0;
            await context.Response.Body.WriteAsync(cachedResponse!);
            return;
        }

        var originalBodyStream = context.Response.Body;
        using var responseBody = new MemoryStream();
        context.Response.Body = responseBody;

        await _next(context);

        if (context.Response.StatusCode == 200)
        {
            var responseData = responseBody.ToArray();

            // Cache the response
            var cacheOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(5))
                .SetSlidingExpiration(TimeSpan.FromMinutes(1));

            _cache.Set(cacheKey, responseData, cacheOptions);

            _logger.LogInformation($"Cached response for {context.Request.Path}");
        }

        await responseBody.CopyToAsync(originalBodyStream);
    }

    private string GenerateCacheKey(HttpRequest request)
    {
        return $"{request.Path}{request.QueryString}";
    }
}


Performance Monitoring Middleware
// Performance monitoring middleware
public class PerformanceMonitoringMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<PerformanceMonitoringMiddleware> _logger;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        var startMemory = GC.GetTotalMemory(false);

        try
        {
            await _next(context);
        }
        finally
        {
            stopwatch.Stop();
            var endMemory = GC.GetTotalMemory(false);
            var memoryUsed = endMemory - startMemory;

            _logger.LogInformation(
                $"Performance - Path: {context.Request.Path}, " +
                $"Duration: {stopwatch.ElapsedMilliseconds}ms, " +
                $"Memory: {memoryUsed} bytes, " +
                $"Status: {context.Response.StatusCode}");

            // Log warning for slow requests
            if (stopwatch.ElapsedMilliseconds > 1000)
            {
                _logger.LogWarning(
                    $"Slow request detected: {context.Request.Path} " +
                    $"took {stopwatch.ElapsedMilliseconds}ms");
            }
        }
    }
}


10. Testing Middleware
Unit Testing Middleware

// Unit tests for custom middleware
public class RequestLoggingMiddlewareTests
{
    [Fact]
    public async Task InvokeAsync_LogsRequestInformation()
    {
        // Arrange
        var loggerMock = new Mock<ILogger<RequestLoggingMiddleware>>();
        var nextMock = new Mock<RequestDelegate>();

        var middleware = new RequestLoggingMiddleware(nextMock.Object, loggerMock.Object);

        var context = new DefaultHttpContext();
        context.Request.Method = "GET";
        context.Request.Path = "/api/test";

        // Act
        await middleware.InvokeAsync(context);

        // Assert
        nextMock.Verify(next => next(context), Times.Once);
        // Verify logging calls
    }

    [Fact]
    public async Task InvokeAsync_WhenException_LogsError()
    {
        // Arrange
        var loggerMock = new Mock<ILogger<RequestLoggingMiddleware>>();
        var nextMock = new Mock<RequestDelegate>();
        nextMock.Setup(next => next(It.IsAny<HttpContext>()))
                .ThrowsAsync(new Exception("Test exception"));

        var middleware = new RequestLoggingMiddleware(nextMock.Object, loggerMock.Object);
        var context = new DefaultHttpContext();

        // Act & Assert
        await Assert.ThrowsAsync<Exception>(() => middleware.InvokeAsync(context));

        // Verify error was logged
    }
}

// Integration testing
public class MiddlewareIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public MiddlewareIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task CustomMiddleware_AddsCorrelationHeader()
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/test");

        // Assert
        response.Headers.Contains("X-Correlation-ID").Should().BeTrue();
    }
}

11. Best Practices and Alternatives
Middleware Best Practices


11.1 Do's and Don'ts
Do:

  • Keep middleware focused and single-purpose
  • Use dependency injection properly
  • Handle exceptions appropriately
  • Consider performance implications
  • Use appropriate logging levels

Don't:

  • Put business logic in middleware
  • Block the pipeline unnecessarily
  • Ignore async/await patterns
  • Forget to call next() when needed

11.2 Performance Considerations
// Good - Async all the way
public async Task InvokeAsync(HttpContext context)
{
    await SomeAsyncOperation();
    await _next(context);
}

// Bad - Mixing sync and async
public async Task InvokeAsync(HttpContext context)
{
    SomeSyncOperation(); // Blocks thread
    await _next(context);
}

Alternatives to Custom Middleware
11.3 Action Filters
// Use action filters for controller-specific logic
public class LogActionFilter : IActionFilter
{
    private readonly ILogger<LogActionFilter> _logger;

    public LogActionFilter(ILogger<LogActionFilter> logger)
    {
        _logger = logger;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation($"Executing action: {context.ActionDescriptor.DisplayName}");
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation($"Executed action: {context.ActionDescriptor.DisplayName}");
    }
}


11.4 Endpoint Filters
// Endpoint filters for minimal APIs
app.MapGet("/api/products", () =>
{
    return Results.Ok(products);
})
.AddEndpointFilter<LoggingEndpointFilter>();

public class LoggingEndpointFilter : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        Console.WriteLine($"Before: {context.HttpContext.Request.Path}");
        var result = await next(context);
        Console.WriteLine($"After: {context.HttpContext.Request.Path}");

        return result;
    }
}


Complete Real-World Example: E-Commerce Pipeline
// Complete e-commerce application pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Exception handling
    app.UseGlobalExceptionHandler();

    // Security
    app.UseHsts();
    app.UseHttpsRedirection();

    // Performance
    app.UseResponseCompression();
    app.UseResponseCaching();

    // Static files
    app.UseStaticFiles();

    // Routing
    app.UseRouting();

    // Custom middleware
    app.UseCorrelationId();
    app.UseRequestLogging();
    app.UsePerformanceMonitoring();

    // Authentication & Authorization
    app.UseAuthentication();
    app.UseAuthorization();

    // Rate limiting for API
    app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"),
        apiApp =>
        {
            apiApp.UseRateLimiting();
            apiApp.UseApiKeyAuthentication();
        });

    // Endpoints
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();

        // Health check
        endpoints.MapHealthChecks("/health");
    });
}

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET Core 10.0 hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.

 



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Runtime Intelligence Unlocked: A Formal Investigation of Reflection in.NET

clock October 20, 2025 07:25 by author Peter

Programs can examine and work with their own metadata at runtime thanks to the potent.NET reflection feature. It serves as the foundation for numerous complex programming scenarios, such as runtime code generation, late binding, and dynamic type discovery. This article offers a thorough rundown of Reflection in.NET, including its essential elements, real-world uses, and recommended usage guidelines for security and effectiveness.

What Is Reflection?
Reflection is the ability of a program to examine and manipulate its own structure and behavior. In the .NET ecosystem, this is facilitated by the System.Reflection namespace, which provides classes and methods to access metadata about assemblies, modules, types, members, and parameters.

Reflection allows developers to:

  • Discover type information at runtime
  • Instantiate objects dynamically
  • Invoke methods and access properties or fields
  • Explore custom attributes and metadata
  • Key Components of Reflection

1. Assembly Inspection
The Assembly class enables loading and inspecting assemblies. Developers can retrieve types, modules, and referenced assemblies using methods like Assembly.GetTypes() and Assembly.GetReferencedAssemblies().

2. Type Discovery
The Type class is central to Reflection. It provides metadata about classes, interfaces, enums, and other types. Common methods include:
GetMethods(), GetProperties(), GetFields()
IsClass, IsInterface, IsGenericType

3. Member Invocation

Reflection allows invoking members dynamically using:

  • MethodInfo.Invoke() for methods
  • PropertyInfo.GetValue() and SetValue() for properties
  • FieldInfo.GetValue() and SetValue() for fields

4. Custom Attributes
Reflection can retrieve custom attributes applied to types or members using Attribute.GetCustomAttributes() or MemberInfo.GetCustomAttributes().
Practical Applications

Reflection is widely used in scenarios such as:

  • Dependency Injection Frameworks: Discovering and injecting services at runtime.
  • ORMs (Object-Relational Mappers): Mapping database tables to classes dynamically.
  • Unit Testing Tools: Discovering and executing test methods.
  • Serialization Libraries: Inspecting object graphs for custom serialization.

Performance Considerations
While Reflection is powerful, it comes with performance overhead due to runtime type resolution and security checks. Best practices include:

  • Caching Type, MethodInfo, and PropertyInfo objects
  • Avoiding frequent reflection calls in performance-critical paths
  • Using compiled expressions (Expression<T>) or delegates for repeated invocations

Security Implications
Reflection can bypass access modifiers and invoke private members, which may pose security risks. Developers should:

  • Avoid exposing sensitive internals via Reflection
  • Use BindingFlags judiciously to limit scope
  • Validate inputs when using Reflection in public APIs

Alternatives and Enhancements
With the advent of .NET Core and .NET 5+, alternatives like System.Text.Json and source generators offer compile-time metadata access, reducing reliance on Reflection. Additionally, the TypeInfo class in .NET Core provides a more granular API for type inspection.

Reflection in .NET empowers developers with runtime introspection and dynamic behavior, making it indispensable for frameworks, libraries, and tooling. However, its use should be balanced with performance and security considerations. By understanding its architecture and applying best practices, developers can harness Reflection to build flexible and intelligent applications.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.

 



European ASP.NET Core 10.0 Hosting - HostForLIFE :: SOLID Principles: A Comprehensive Guide to Practical.NET Projects

clock July 8, 2025 08:24 by author Peter

Single Responsibility Principle (SRP)
// One class = One job

Every class should have only one reason to change. This means no mixing database logic with business logic or API response formatting with validation rules.

Real-World .NET Example
// Bad: Doing too much
public class UserManager
{
    public void Register(User user) { /* logic */ }
    public void SaveToDb(User user) { /* DB logic */ }
    public void SendWelcomeEmail(User user) { /* Email logic */ }
}
// Good: SRP Applied
public class UserService
{
    public void Register(UserDto dto) { /* business rules */ }
}
public class UserRepository
{
    public void Save(User user) { /* DB only */ }
}
public class EmailSender
{
    public void SendWelcomeEmail(User user) { /* Email only */ }
}

Open/Closed Principle (OCP)
Open for extension, closed for modification

Your code should allow new behavior without changing existing code. This avoids regressions and makes it easier to add future features.

Real-World Example
public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal price);
}
public class HolidayDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price * 0.9m;
}
public class NoDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price;
}

By injecting IDiscountStrategy, you can add new discounts without changing your OrderService.

Liskov Substitution Principle (LSP)
Subtypes must be substitutable for their base types

Any derived class should be replaceable for its parent without breaking functionality.

Anti-Pattern
public class Rectangle
{
    public virtual void SetWidth(int width) { }
}
public class Square : Rectangle
{
    public override void SetWidth(int width)
    {
        throw new NotImplementedException(); // LSP Violation
    }
}

Instead, design with interfaces or composition when inheritance breaks behavior.

Interface Segregation Principle (ISP)
Keep interfaces small and focused

Clients shouldn't be forced to implement methods they don’t use. This avoids bloated and confusing code.

Better Design
public interface ILoginService
{
    void Login(string user, string password);
}
public interface IRegistrationService
{
    void Register(UserDto dto);
}

Instead of forcing everything into a single IUserService, split interfaces by responsibility.

Dependency Inversion Principle (DIP)
Depend on abstractions, not concrete classes

High-level modules (like OrderService) should depend on interfaces, not low-level classes like SqlOrderRepository.

Real-World Example
public interface IEmailService
{
    void SendEmail(string to, string message);
}
public class EmailService : IEmailService
{
    public void SendEmail(string to, string message)
    {
        // SMTP logic
    }
}
public class NotificationManager
{
    private readonly IEmailService _emailService;

    public NotificationManager(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public void Notify(string msg)
    {
        _emailService.SendEmail("[email protected]", msg);
    }
}


In Program.cs
services.AddScoped<IEmailService, EmailService>();

How to Apply SOLID Principles Efficiently While Working?

  • Start with interfaces, then build implementations.
  • Don’t mix responsibilities: Separate service, repository, controller.
  • Write unit tests: If a class is hard to test, it’s probably violating SOLID.
  • Utilize design patterns:such as Strategy, Factory, and Mediator, to simplify SOLID principles.
  • Think like Lego: Compose functionality from small, testable bricks.


Final Thoughts
Mastering SOLID is like upgrading your dev brain. You’ll not only write clean and testable code but also scale faster, debug smarter, and survive enterprise-level chaos like a pro.
Want feedback on how SOLID your current codebase is? Drop a sample, and let's refactor it together!

Happy coding, and stay SOLID.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: Wolverine's Modern CQRS and Messaging in.NET

clock July 1, 2025 08:44 by author Peter

You have most likely used libraries like MediatR, MassTransit, or CAP to handle commands and messages at some time during your.NET journey. Although each has advantages, wiring and configuration can occasionally impede the development of features alone. I recently learned about the JasperFx team's command and message bus library, Wolverine. It combines scheduled jobs, communications, CQRS, and a robust inbox/outbox into a single, streamlined solution. What's the best part? It's enjoyable to work with, quick, and clean.

This post will show you how to use Wolverine to manage commands, publish events, and schedule background messages without the need for a database or web API by guiding you through a straightforward console application. 

Why Wolverine?
Wolverine aims to simplify distributed application architecture by combining.

  • Command handling (like MediatR).
  • Asynchronous and scheduled messaging.
  • Built-in middleware and behaviors.
  • Optional durable inbox/outbox with Marten (PostgreSQL).

Instead of bolting together multiple libraries, Wolverine gives you a unified model with batteries included.
The Example: Console-Based User Registration

We’ll build a console app that,

  • Registers a user via a command.
  • Sends a welcome email (simulated).
  • Schedules a follow-up reminder message.

Everything runs in-process using Wolverine's local message bus.

Step 1. Create the Project

Project Folder Structure
WolverineConsoleDemo/
├── Program.cs
├── WolverineConsoleDemo.csproj
├── Messages/
│   ├── RegisterUser.cs
│   ├── UserRegistered.cs
│   └── RemindAdmin.cs
├── Handlers/
│   ├── RegisterUserHandler.cs
│   ├── UserRegisteredHandler.cs
│   └── RemindAdminHandler.cs


dotnet new console -n WolverineConsoleDemo
cd WolverineConsoleDemo

dotnet add package WolverineFx


Step 2. Define Messages and Handlers
RegisterUser.cs
public record RegisterUser(string Email, string FullName);

UserRegistered.cs
public record UserRegistered(string Email);

RemindAdmin.cs
public record RemindAdmin(string Email);

Step 3. Implement Handlers
RegisterUserHandler.cs

using Wolverine;
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
    public class RegisterUserHandler
    {
        /// <summary>
        /// Handles the specified command.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <param name="context">The context.</param>
        public static async Task Handle(RegisterUser command, IMessageContext context)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"Registered: {command.FullName}");
            Console.ResetColor();
            await context.PublishAsync(new UserRegistered(command.Email));
            await context.ScheduleAsync(new RemindAdmin(command.Email), TimeSpan.FromSeconds(10));
        }
    }
}


UserRegisteredHandler.cs
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
    public class UserRegisteredHandler
    {
        /// <summary>
        /// Handles the specified event.
        /// </summary>
        /// <param name="event">The event.</param>
        /// <returns></returns>
        public static Task Handle(UserRegistered @event)
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine($"Welcome email sent to {@event.Email}");
            Console.ResetColor();
            return Task.CompletedTask;
        }
    }
}


RemindAdminHandler.cs
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
    public class RemindAdminHandler
    {
        /// <summary>
        /// Handles the specified MSG.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <returns></returns>
        public static Task Handle(RemindAdmin msg)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Admin reminded for: {msg.Email}");
            Console.ResetColor();
            return Task.CompletedTask;
        }
    }
}

Step 4. Configure Wolverine in the Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Wolverine;
using WolverineConsoleDemo.Messages;
var builder = Host.CreateApplicationBuilder(args);
// suppress hosting lifetime logs
builder.Logging.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.None);
builder.Services.AddWolverine(opts =>
{
    opts.PublishMessage<RegisterUser>().ToLocalQueue("users");
    opts.PublishMessage<UserRegistered>().ToLocalQueue("emails");
    opts.PublishMessage<RemindAdmin>().ToLocalQueue("admin");
});
using var host = builder.Build();
// Start the host before using Wolverine
await host.StartAsync();
var bus = host.Services.GetRequiredService<IMessageBus>();
await bus.InvokeAsync(new RegisterUser("[email protected]", "Test User"));
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
// Optional: gracefully shut down
await host.StopAsync();


Sample Console Output
This output demonstrates how Wolverine utilizes color-coded messages to distinguish each part of the registration process clearly.


Conclusion
Wolverine brings a fresh, powerful approach to building message-driven systems in .NET. With minimal setup and zero boilerplate, we’ve seen how easy it is to.

  • Create and handle commands.
  • Publish and respond to events.
  • Schedule background tasks.
  • Run everything in-process without external queues or services.

This lightweight console app demonstrates that you don’t need heavy infrastructure to adopt clean, decoupled architectures. Whether you’re prototyping a feature, building core business logic, or moving toward microservices, Wolverine gives you the right tools with a developer-friendly experience. If you're tired of wiring up multiple libraries to do what Wolverine does out of the box, give it a try in your next project. You might be surprised how much simpler your code becomes.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 10.0 Hosting - HostForLIFE :: ASP.NET Core Service Lifetimes: Transient, Scoped, and Singleton

clock June 26, 2025 07:56 by author Peter

Dependency Injection (DI) is a potent feature in ASP.NET Core that aids programmers in creating loosely linked, tested, and maintainable applications. Understanding how services are handled over time is a crucial component of this system, and this is where Service Lifetimes enter the picture.

A Service Lifetime determines how long an instance of a service will live once it's created by the built-in IoC (Inversion of Control) container.

When a service is registered in the Program.cs (or Startup.cs in older versions), we must specify its lifetime. This decision directly affects.

  • When the service instance is created
  • How long will it be reused
  • Whether it's shared across requests, users, or components

"How long will the instance of the service live?" and "When will a new instance be created?"

There are 3 built-in lifetimes.

Lifetime Description
Transient New instance every time it is requested.
Scoped One instance per HTTP request (or scope).
Singleton One instance for the entire lifetime of the application..

Before diving into service lifetimes, it's very important to understand the core concepts.

Before diving into service lifetimes, it's very important to understand the core concepts.

  • Dependency Injection (DI)
  • Inversion of Control (IoC)

What is Dependency Injection (DI)?
Dependency Injection is a design pattern used to provide an object’s dependencies from the outside, instead of creating them inside the object itself.
Dependency = Another class or object that your class needs to function.
Injection = Supplying those dependencies externally.

Imagine you're a chef who needs ingredients to cook.

  • Without DI, the chef goes out to the farm, harvests the vegetables, and then cooks.
  • With DI, someone delivers the fresh ingredients to the chef so they can focus on cooking.

DI = Someone gives you what you need. You don’t fetch it yourself.
Without DI (tight coupling)
public class Car
{
    private Engine _engine = new Engine();  // Car creates its own engine

    public void Drive()
    {
        _engine.Start();
    }
}


Problem

  • Hard to test
  • Can’t swap the Engine with the electric engine easily
  • Tight coupling

With DI (loose coupling)
public class Car
{
    private readonly IEngine _engine;

    public Car(IEngine engine)  // Engine is "injected" from outside
    {
        _engine = engine;
    }

    public void Drive()
    {
        _engine.Start();
    }
}


Now we can,

  • Inject any IEngine implementation
  • Test easily (mock the engine)
  • Change logic without touching the Car class

What is Inversion of Control (IoC)?
Inversion of Control is a broader principle. It means that the control of creating and managing objects is inverted — from the class to a container or framework. In simpler terms.
Instead of our code controlling how things are created, the framework (like ASP.NET Core) does it for us.

Example of IoC
We register services in a container.
services.AddScoped<ICarService, CarService>();

Then our controller.
public class HomeController : Controller
{
    private readonly ICarService _carService;

    public HomeController(ICarService carService) // Injected by framework
    {
        _carService = carService;
    }
}


We didn’t create CarService the IoC container did and gave it to us!

Relationship Between DI and IoC

Concept Meaning Who Manages Creation
IoC Inverting object creation/control Framework/Container
DI A way to implement IoC by injecting dependencies The framework injects what you need

So, Dependency Injection is a technique used to achieve Inversion of Control.

  • IoC: Framework creates and manages the object
  • DI: The Controller gets the service it needs without creating it

Why Are Service Lifetimes Important?
Choosing the correct service lifetime is essential for,

  • Memory management: Avoid unnecessary object creation
  • Performance optimization: Reuse objects when appropriate
  • Thread safety prevents state corruption in multi-user scenarios
  • Correct behavior, especially when services maintain data like a database context
  • Prevents data leaks by isolating instances.
  • Promotes cleaner architecture via proper dependency reuse.

Types of Service Lifetimes in .NET Core
.NET provides three main service lifetimes.

  • Transient: A new instance is created every time the service is requested.
  • Scoped: A single instance is created per HTTP request and shared within that request.
  • Singleton: A single instance is created once and shared across the entire application.

Choosing the correct lifetime ensures better performance, resource management, and application behavior.

Where Are Lifetimes Defined?
Lifetimes are defined when registering services in the Program.cs file.
builder.Services.AddTransient<IMyService, MyService>();   // Transient
builder.Services.AddScoped<IMyService, MyService>();      // Scoped
builder.Services.AddSingleton<IMyService, MyService>();   // Singleton

Service Lifetime Differences in ASP.NET Core

Feature / Criteria Transient Scoped Singleton
Object Creation Every time it is requested Once per HTTP request Once for the entire application
Shared Between Never shared Shared within one request Shared across all requests and components
Reuse Behavior New instance for every injection Same instance reused during one request Same instance reused everywhere
Lifetime Ends Immediately after use End of the HTTP request When the app shuts down
Thread-Safety Required No (short-lived) No (request-specific) Yes (used by multiple threads)
Testing Use Case Best for isolated tests Good for per-request logic Good for app-level state
Best For Lightweight, stateless services Entity Framework DbContext, per-user logic Logging, Caching, Configuration
Common Pitfall High object creation cost Cannot be injected into a Singleton Memory leaks or shared state issues

Best Practices for Choosing Service Lifetimes in .NET

Scenario Recommended Lifetime Real Example Explanation
Stateless utility like a formatter/calculator Transient PriceFormatterService Creates a new instance each time — ideal for small, stateless services like currency formatting or calculations.
Database access, user session services Scoped ApplicationDbContext, UserService Ensures consistency within a single HTTP request — needed for services handling DB transactions or per-user data.
Global logger, configuration, and caching Singleton LoggerService, ConfigProvider Created once and reused across the app — perfect for shared logic like logging or reading global settings.

What Happens If We Choose the Wrong Lifetime?

1. Scoped Service Injected into Singleton

  • Problem: Scoped services (like DbContext) are created per request. A Singleton lives for the entire app.
  • What Happens: .NET throws a runtime error InvalidOperationException.
  • Why: A Singleton can't depend on something that changes per request.

2. Storing Large Data in a Singleton

  • Problem: Singleton services stay alive for the app's entire lifetime.
  • What Happens: If you store large data (e.g., thousands of records), it causes memory leaks or slow performance.
  • Why: Data is never released from memory.

3. Using Transient for Expensive or Stateful Services

  • Problem: Transient creates a new instance every time.
  • What Happens: Extra memory usage, duplicate work (like database connections or API calls), and inconsistent behavior.
  • Why: Data/state is not shared between calls.

4. Expecting Transient or Scoped to Act Like Singleton

  • Problem: Assuming the service "remembers" data across requests or injections.ASP.NET Core Service Lifetimes: Transient, Scoped, and Singleton
  • What Happens: Data is lost or reset unexpectedly.
  • Why: Scoped lasts only one request, and Transient lasts only one use.

Conclusion
Understanding Service Lifetimes in .NET is essential for building efficient, scalable, and maintainable applications. By mastering Dependency Injection and properly choosing between Transient, Scoped, and Singleton lifetimes, you can control how services behave, improve app performance, and avoid bugs related to memory leaks or shared state. Always start with Transient unless your scenario calls for Scoped or Singleton. Use Scoped for request-based operations like database access, and use Singleton cautiously for shared logic like configuration or logging, while ensuring thread safety. With the right service lifetime choices and a solid understanding of DI and IoC, your ASP.NET Core applications will be more testable, modular, and cleanly architected.

HostForLIFE ASP.NET Core 10.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Month List

Tag cloud

Sign in