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 9.0 Hosting - HostForLIFE :: Using Switch-Case to Dynamically Load Several ASCX Pages into an ASPX Page

clock October 14, 2025 07:21 by author Peter

Pages in ASP.NET Web Forms frequently have to dynamically display various user controls (.ascx) in response to a condition (such as a query string parameter or menu selection). Using LoadControl and a switch-case structure, you may load each control dynamically at runtime rather than generating distinct pages for each control.

Scenario
Suppose you have multiple user controls:

  • Dashboard.ascx
  • NewClient.ascx
  • AddNewClient.ascx
  • masterList.ascx
  • …and many others

You want to load these controls into a placeholder ( ControlHolder ) in your Default.aspx page based on a numeric or string parameter.

Implementation Steps

1. Add a Placeholder in ASPX Page
In your ASPX page ( Default.aspx ), add a PlaceHolder control where dynamic controls will be loaded:
<asp:PlaceHolder ID="ControlHolder" runat="server"></asp:PlaceHolder>

This placeholder acts as a container for your user controls.

2. Load Controls Dynamically in Code-Behind
In your code-behind ( Default.aspx.cs ), you can use a switch-case statement to determine which .ascx control to load:
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        string pageID = Request.QueryString["PageID"] != null ? Request.QueryString["PageID"].ToString() : ""; // Example: get value from URL ?PageID=1
        LoadUserControl(pageID);
    }
}


private void LoadUserControl(string pageID)
{
    switch (pageID)
    {
        case "1":
            Dashboard dashboared_ctrl = (Dashboard)LoadControl("Dashboard.ascx");
            ControlHolder.Controls.Add(dashboared_ctrl);
            break;

        case "2":
            NewClientmaster newclient_ctrl = (NewClientmaster)LoadControl("NewClient.ascx");
            ControlHolder.Controls.Add(newclient_ctrl);
            break;

        case "3":
            AddNewClientList addnewclient = (AddNewClientList)LoadControl("AddNewClient.ascx");
            ControlHolder.Controls.Add(addnewclient);
            break;

        case "4":
            UCCmasterList uccmaster_ctrl = (UCCmasterList)LoadControl("masterList.ascx");
            ControlHolder.Controls.Add(uccmaster_ctrl);
            break;

        case "5":
            AddClientList addclient_ctrl = (AddClientList)LoadControl("AddClientList.ascx");
            ControlHolder.Controls.Add(addclient_ctrl);
            break;

        case "6":
            ViewIPOMaster viewipo_master = (ViewIPOMaster)LoadControl("ViewMaster.ascx");
            ControlHolder.Controls.Add(viewipo_master);
            break;

        case "7":
            AddIPOMaster addipomastr = (AddIPOMaster)LoadControl("AddMaster.ascx");
            ControlHolder.Controls.Add(addipomastr);
            break;

        case "8":
            AddIPO addipo = (AddIPO)LoadControl("AddIPO.ascx");
            ControlHolder.Controls.Add(addipo);
            break;

        case "9":
            UpdateExchangeIPO updateEx = (UpdateExchangeIPO)LoadControl("Update.ascx");
            ControlHolder.Controls.Add(updateEx);
            break;

        case "10":
            OrderList orderlist_ctrl = (OrderList)LoadControl("OrderList.ascx");
            ControlHolder.Controls.Add(orderlist_ctrl);
            break;

        case "11":
            UPIReport upireport_ctrl = (UPIReport)LoadControl("Report.ascx");
            ControlHolder.Controls.Add(upireport_ctrl);
            break;

        case "12":
            IPOSettingsOffline offlinesetting_ctrl = (IPOSettingsOffline)LoadControl("Settings.ascx");
            ControlHolder.Controls.Add(offlinesetting_ctrl);
            break;

        case "13":
            OfflineOrders offlinelist_ctrl = (OfflineOrders)LoadControl("Orders.ascx");
            ControlHolder.Controls.Add(offlinelist_ctrl);
            break;

        case "14":
            IPOAllotment ipoallotment_ctrl = (IPOAllotment)LoadControl("Allotment.ascx");
            ControlHolder.Controls.Add(ipoallotment_ctrl);
            break;

        case "15":
            ExchangePage ExchangePage_ctrl = (ExchangePage)LoadControl("ExchangePage.ascx");
            ControlHolder.Controls.Add(ExchangePage_ctrl);
            break;

        case "16":
            ViewIssue ViewIssue_ctrl = (ViewIssue)LoadControl("ViewIssue.ascx");
            ControlHolder.Controls.Add(ViewIssue_ctrl);
            break;

        case "17":
            ViewIPO ViewIPO_ctrl = (ViewIPO)LoadControl("ViewIPO.ascx");
            ControlHolder.Controls.Add(ViewIPO_ctrl);
            break;

        default:
            // Optionally show a default control or message
            ControlHolder.Controls.Add(new LiteralControl("<p>Select a valid option</p>"));
            break;
    }
}


3. How It Works

  • Get the Page Identifier: You can use QueryString , a DropDownList , or any parameter to determine which control to load.
  • Switch-Case Logic: Each case corresponds to a specific .ascx control. The control is loaded dynamically and added to the placeholder.
  • Type Casting: (Dashboard)LoadControl("Dashboard.ascx") casts the loaded control to the correct class so you can access its public properties or methods.
  • Adding to Placeholder: ControlHolder.Controls.Add(dashboared_ctrl) adds the control to the page dynamically at runtime.

4. Advantages of this Approach

  • Single ASPX page for multiple views: Reduces code duplication.
  • Dynamic content loading: Only loads the control needed based on user interaction.
  • Maintainability: Adding new controls is simple: just add a new case in the switch.
  • Encapsulation: Each user control handles its own UI and logic.

5. Optional Enhancements

  • Use QueryString or Session for persistence.
  • Default control: Show a welcome dashboard if no PageID is provided.
  • Error handling: Wrap LoadControl in try-catch to prevent runtime errors.
  • Refactor using Dictionary : For large numbers of controls, you can use a dictionary mapping instead of long switch-case.

Example URL to Load Controls
Default.aspx?PageID=1   -> Loads Dashboard.ascx
Default.aspx?PageID=2   -> Loads NewClientmaster.ascx
Default.aspx?PageID=10  -> Loads OrderList.ascx


Conclusion
An effective technique to dynamically load several user controls into a single ASPX page is to utilize switch-case with LoadControl in ASP.NET Web Forms. This makes your program modular and maintainable.

HostForLIFE ASP.NET Core 9.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 9.0 Hosting - HostForLIFE :: In.NET 8, Dapper and Entity Framework Core (EF Core) work together to combat PostgreSQL

clock October 7, 2025 08:38 by author Peter

I’ll show two approaches you can choose from:

  • Approach A (recommended for most reads/writes): Use EF Core for schema, migrations, and most CRUD; use Dapper (Npgsql) for fast raw/complex queries. (Dapper uses its own IDbConnection.)
  • Approach B (when you need a single transaction across EF + Dapper): Reuse the DbConnection /transaction inside DbContext so Dapper executes on the same connection/transaction as EF Core.

Key libraries used: Dapper , Npgsql (Postgres ADO.NET driver), and Npgsql.EntityFrameworkCore.PostgreSQL (EF Core provider).

1. Create project & add packages

dotnet new webapi -n DapperEfPgDemo
cd DapperEfPgDemo

dotnet add package Dapper
dotnet add package Npgsql
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Design

(Install dotnet-ef tool if you need CLI migrations: dotnet tool install --global dotnet-ef .)

2. Connection string (appsettings.json)

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Database=dapperefpg;Username=postgres;Password=your_password"
  }
}

3. POCO models
Models/Product.cs
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
}

Models/Category.cs
public class Category
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
}

4. EF Core DbContext
Data/AppDbContext.cs
using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}

    public DbSet<Product> Products { get; set; } = null!;
    public DbSet<Category> Categories { get; set; } = null!;
}

Register DbContext in Program.cs (below).

5. Program.cs (DI & EF + optional Dapper factory)
Program.cs (minimal)
using Npgsql;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var conn = builder.Configuration.GetConnectionString("DefaultConnection");

// EF Core
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(conn)
);

// OPTIONAL: register an IDbConnection factory for Dapper (Approach A)
builder.Services.AddScoped<System.Data.IDbConnection>(_ => new NpgsqlConnection(conn));

// register repositories, controllers, swagger...
builder.Services.AddScoped<IProductRepository, ProductRepository>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

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

You can either inject IDbConnection (a new NpgsqlConnection per scope) for Dapper, or use the DbContext ’s connection (shown below) when you need transaction sharing. npgsql.org

6. Create the DB (migrations)
dotnet ef migrations add InitialCreate
dotnet ef database update

(Use EF migrations for schema management — easier than hand-written SQL for most workflows.) npgsql.org

7. Repositories — two patterns
Pattern A — Dapper using its own IDbConnection (no shared transaction)

Good when you only need quick reads/writes and don't need EF & Dapper to share a single transaction.

Repositories/ProductDapperRepository.cs
using Dapper;
using System.Data;

public class ProductDapperRepository : IProductRepository
{
    private readonly IDbConnection _db; // injected NpgsqlConnection (scoped)

    public ProductDapperRepository(IDbConnection db) => _db = db;

    public async Task<IEnumerable<Product>> GetAllAsync()
    {
        var sql = "SELECT id AS Id, name AS Name, price AS Price, categoryid AS CategoryId FROM products";
        if (_db.State != ConnectionState.Open) await _db.OpenAsync();
        return await _db.QueryAsync<Product>(sql);
    }

    public async Task<Product?> GetByIdAsync(int id)
    {
        var sql = "SELECT id, name, price, categoryid FROM products WHERE id = @Id";
        if (_db.State != ConnectionState.Open) await _db.OpenAsync();
        return await _db.QueryFirstOrDefaultAsync<Product>(sql, new { Id = id });
    }

    // Create/Update/Delete can use ExecuteAsync / ExecuteScalarAsync etc.
}


This is the simplest pattern and performs well for read-heavy, SQL-optimized queries.

  • Pattern B — Use EF DbContext's connection so EF & Dapper share the same connection/transaction
  • Use this when you need both EF and Dapper operations in the same transaction (commit/rollback together).

Repositories/ProductCombinedRepository.cs
using Dapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Data;

public class ProductCombinedRepository
{
    private readonly AppDbContext _db;

    public ProductCombinedRepository(AppDbContext db) => _db = db;

    // Dapper read using DbContext's connection (don't dispose the connection EF owns)
    public async Task<IEnumerable<Product>> GetAllWithDapperAsync()
    {
        var conn = _db.Database.GetDbConnection(); // returns DbConnection
        if (conn.State != ConnectionState.Open) await conn.OpenAsync();

        var sql = "SELECT id AS Id, name AS Name, price AS Price, categoryid AS CategoryId FROM products";
        // Dapper's QueryAsync accepts DbConnection since it implements IDbConnection
        return await conn.QueryAsync<Product>(sql);
    }

    // Example: an operation that uses Dapper + EF in one transaction
    public async Task<int> CreateProductAndLogAsync(Product product, string auditMessage)
    {
        // Start EF transaction
        await using var efTx = await _db.Database.BeginTransactionAsync();
        try
        {
            // 1) run a Dapper insert on the same connection/transaction
            var conn = _db.Database.GetDbConnection();
            if (conn.State != ConnectionState.Open) await conn.OpenAsync();

            var insertSql = @"
                INSERT INTO products (name, price, categoryid)
                VALUES (@Name, @Price, @CategoryId)
                RETURNING id;
            ";

            // Get underlying ADO.NET transaction to pass to Dapper
            var dbTransaction = efTx.GetDbTransaction(); // IDbTransaction
            var newId = await conn.ExecuteScalarAsync<int>(insertSql, product, transaction: dbTransaction);

            // 2) use EF Core to write to some tracked audit entity (or any other EF operation)
            var audit = new AuditLog { Message = auditMessage, ProductId = newId, CreatedAt = DateTime.UtcNow };
            _db.Add(audit);
            await _db.SaveChangesAsync();

            await efTx.CommitAsync();
            return newId;
        }
        catch
        {
            await efTx.RollbackAsync();
            throw;
        }
    }
}


Notes
Database.GetDbConnection() returns the DbConnection currently used by EF — do not dispose it if EF created it. Open it if closed.

BeginTransactionAsync() returns an IDbContextTransaction ; you can call .GetDbTransaction() to retrieve the underlying ADO.NET DbTransaction and pass it to Dapper. This is how you make Dapper run in the same DB transaction as EF.

8. Example controller (using combined repo)
Controllers/ProductsController.cs
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly ProductCombinedRepository _repo;

    public ProductsController(ProductCombinedRepository repo) => _repo = repo;

    [HttpGet]
    public async Task<IActionResult> GetAll() => Ok(await _repo.GetAllWithDapperAsync());

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] Product p)
    {
        var id = await _repo.CreateProductAndLogAsync(p, $"Created via API at {DateTime.UtcNow:O}");
        return CreatedAtAction(nameof(GetAll), new { id }, new { Id = id });
    }
}


9. Important best-practices & gotchas

  • Use EF Core for schema/migrations and domain mapping; use Dapper for raw/complex queries or performance hotspots. This combination is common in production.
  • If you obtain the connection from DbContext.Database.GetDbConnection() do NOT dispose it (EF owns it). Just open it when needed.
  • To share a transaction: call BeginTransaction() / BeginTransactionAsync() on the DbContext , get the DbTransaction via GetDbTransaction() , and pass it to your Dapper calls (via the transaction: parameter). That keeps EF & Dapper in the same DB transaction.
  • Parameterize SQL — Dapper supports anonymous parameter objects (always avoid string concatenation to prevent SQL injection).
  • Unit testing: EF InMemory provider doesn't run raw SQL — tests that use Dapper need a real Postgres (or testcontainer).Connection pooling: Npgsql pools connections; opening/closing is cheap. Prefer short-lived commands or let EF manage the connection lifetime when sharing.

10. Quick checklist / commands

  • Add packages: Dapper , Npgsql , Npgsql.EntityFrameworkCore.PostgreSQL , Microsoft.EntityFrameworkCore.Design .
  • Add AppDbContext and model DbSet s.
  • builder.Services.AddDbContext<AppDbContext>(options => options.UseNpgsql(conn));
  • Optionally register IDbConnection factory ( new NpgsqlConnection(conn) ) if you prefer separate Dapper connections.
  • dotnet ef migrations add InitialCreate → dotnet ef database update .

References/reading

  • Dapper (NuGet / repo).
  • Npgsql EF Core provider & docs.
  • EF Core transactions / using external DbTransaction (how to share a transaction with ADO.NET ).
  • Practical tutorial: Using EF Core + Dapper together (example & transaction patterns).
  • Note on GetDbConnection() disposal rules.

HostForLIFE ASP.NET Core 9.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 9.0 Hosting - HostForLIFE :: Understanding Filters in Minimal API with .NET 9.0

clock October 2, 2025 08:14 by author Peter

Minimal APIs gained popularity right away when they were released in.NET 6 because they made web services speedy and light.  Nevertheless, previous iterations lacked a suitable method for managing routine tasks like as permission, logging, and validation without having to repeat the same code. This is where Endpoint Filters come in; they were first introduced in.NET 7, got better in.NET 8, and are now much better in.NET 9, offering more control over the request pipeline, grouping options, and ease of use.

This article will examine filters in Minimal APIs with.NET 9 and show you how to utilize them efficiently while adhering to current best practices.

What is New in .NET 9 for Filters?
The core API remains the same, but .NET 9 brings several improvements that make filters easier and more powerful to use:

  • Better dependency injection (DI) and setup for IEndpointFilterFactory
  • Support for filter composition and grouping
  • Smarter type inference for inline filters
  • More consistent behavior between MapGroup and individual endpoints

If you have used filters in .NET 7 or 8, they’ll still work — but in .NET 9, you’ll enjoy cleaner code, stronger typing, and simpler configuration.

What Are Filters?

Filters let you add custom logic into the request pipeline of a Minimal API endpoint — both before and after the handler runs.
You can think of them as lightweight middleware but applied only to specific endpoints or endpoint groups.

They’re great for handling common tasks like:

  • Validation
  • Authentication / Authorization
  • Logging / Metrics
  • Exception Handling
  • Response Wrapping
  • The source code can be downloaded from GitHub.


The tools that I have used here

1. .NET 9.0
2. Visual Studio 2026 Insider


Defining Filters in .NET 9
You can create filters in two main ways:

  • Inline Filters: Write quick, simple logic directly next to the endpoint.
  • Reusable Filters: Create separate classes that implement IEndpointFilter for cleaner, reusable code.

Let us look at both.

Inline Filters
The simplest way to add a filter is inline using AddEndpointFilter

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGet("/inlinefilters", (string name) => $"Hello, {name}!")
   .AddEndpointFilter(async (context, next) =>
   {
       Console.WriteLine("Before endpoint logic");

       var result = await next(context);

       Console.WriteLine("After endpoint logic");
       return result;
   });
app.Run();

How It Works

  • You receive a context containing arguments, HttpContext, and more.
  • You can run code before or after calling next(context).
  • You can modify the result or even stop the request early.

Reusable Filters (IEndpointFilter)
When logic needs to be reused or tested, define a class-based filter:
public class LoggingFilter : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        Console.WriteLine($"Incoming: {context.HttpContext.Request.Path}");

        var result = await next(context);

        Console.WriteLine($"Completed: {context.HttpContext.Response.StatusCode}");

        return result;

    }
}


Attach to the endpoint as below:
// Using a reusable filter
app.MapGet("/reusefilter", (string name) => $"Hello, {name}!")
   .AddEndpointFilter<WebApplication1.Filters.LoggingFilter>();

Practical Use Cases
Here are some real-world examples of how filters can be used in .NET 9 projects:

Validation Filter
Perform model validation before hitting your endpoint logic.
public class ValidationFilter<UserDto> : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        var model = context.Arguments.OfType<UserDto>().FirstOrDefault();
        if (model == null)
        {
            return Results.BadRequest("Invalid request payload.");
        }

        // Use reflection to check for UserName, Name, and Age properties
        var type = typeof(UserDto);
        var nameProp = type.GetProperty("Name");
        var ageProp = type.GetProperty("Age");

        var name = nameProp?.GetValue(model) as string;
        var ageValue = ageProp?.GetValue(model);
        int age = 0;
        if (ageValue != null && int.TryParse(ageValue.ToString(), out var parsedAge))
        {
            age = parsedAge;
        }

        if (string.IsNullOrWhiteSpace(name) || age <= 0)
        {
            return Results.BadRequest("Invalid request payload. Name and Age are required and Age must be greater than 0.");
        }

        var validationResults = new List<ValidationResult>();
        bool isValid = Validator.TryValidateObject(
            model, new ValidationContext(model), validationResults, true);

        if (!isValid)
        {
            var errors = validationResults.ToDictionary(
                r => r.MemberNames.FirstOrDefault() ?? "Unknown",
                r => new[] { r.ErrorMessage ?? "Validation error" });

            return Results.ValidationProblem(errors);
        }

        // Await the next delegate in the pipeline
        return await next(context);
    }
}


Attach to endpoint as below
// Using a validation filter
app.MapPost("/validationfilter", (WebApplication1.Models.UserDto user) => Results.Ok($"User {user.Name} of age {user.Age} created."))
   .AddEndpointFilter<ValidationFilter<WebApplication1.Models.UserDto>>();


Authorization Filter
Implement role-based access without repeating logic.
public class RoleFilter : IEndpointFilter
 {
     private readonly string _role;
     public RoleFilter(string role) => _role = role;

     public async ValueTask<object?> InvokeAsync(
         EndpointFilterInvocationContext context,
         EndpointFilterDelegate next)
     {
         var user = context.HttpContext.User;

         if (!user.Identity?.IsAuthenticated ?? false)
             return Results.Unauthorized();

         if (!user.IsInRole(_role))
             return Results.Forbid();

         return await next(context);
     }
 }


Attach to the endpoint:
//using role based filter
app.MapGet("/rolefilter", () => "Welcome, Admin")
   .AddEndpointFilterFactory((factoryContext, next) =>
       (context) =>
       {
           var filter = new RoleFilter("Admin");
           return filter.InvokeAsync(context, next);
       });


Exception Handler Filter
Centralize error handling at the endpoint or group level.
public class ExceptionFilter : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        try
        {
            return await next(context);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex.Message}");
            return Results.Problem("Something went wrong, please try again.");
        }
    }
}


Apply at a group level in .NET 9:
public class ExceptionFilter : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        try
        {
            return await next(context);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex.Message}");
            return Results.Problem("Something went wrong, please try again.");
        }
    }
}


Attach to the endpoint:
// Using a global exception handling filter
var api = app.MapGroup("/api")
      .AddEndpointFilter<ExceptionFilter>();

api.MapGet("/exceptionfilter", () => { throw new Exception("Test exception"); });


Conclusion
.NET 9 filters Common activities like validation, logging, and error handling can be easily handled in one location with minimal APIs. They guarantee that shared logic remains constant throughout your application while keeping your endpoint code clear and concise. For greater control, you can apply filters to particular endpoints or groups without introducing the intricacy of global middleware. Filters aid in maintaining readable, well-structured, and reusable code as your application expands. To put it briefly, they're an excellent method for creating dependable, clear, and stable minimal APIs. Happy Coding!

HostForLIFE ASP.NET Core 9.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