<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~files/atom-premium.xsl"?>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedpress="https://feed.press/xmlns" xmlns:media="http://search.yahoo.com/mrss/" xmlns:podcast="https://podcastindex.org/namespace/1.0">
  <feedpress:locale>en</feedpress:locale>
  <link rel="hub" href="https://feedpress.superfeedr.com/"/>
  <logo>https://static.feedpress.com/logo/telerik-blogs-web-aspnet-core-618508f75ecad.jpg</logo>
  <title type="text">Telerik Blogs | Web | ASP.NET Core</title>
  <subtitle type="text">The official blog of Progress Telerik - expert articles and tutorials for developers.</subtitle>
  <id>uuid:2ab50c85-cbb7-4129-a662-85ed0053989f;id=10842</id>
  <updated>2026-05-28T11:59:39Z</updated>
  <link rel="alternate" href="https://www.telerik.com/"/>
  <link rel="self" type="application/atom+xml" href="https://feeds.telerik.com/blogs/web-aspnet-core"/>
  <entry>
    <id>urn:uuid:3c1b27d0-6ddf-4b4f-8063-d9d6d3024ec6</id>
    <title type="text">From DevUI to Observability: Evolving the Agent Dev Loop in Microsoft Agent Framework</title>
    <summary type="text">This article shows .NET developers how to get started building and debugging agent-based applications with Microsoft Agent Framework and DevUI. Using the aiagent-webapi template, readers create a multi-agent workflow that includes Writer, Editor, and Publisher agents while learning how Microsoft Agent Framework integrates with familiar ASP.NET Core patterns such as dependency injection and hosted services.

The article explores how DevUI shortens the agent development loop by providing a visual interface for running workflows, inspecting agent interactions, and debugging execution locally. As workflows become more complex, the article highlights the growing observability gap between local debugging and production AI systems, where teams need visibility into behavior across sessions, latency, cost, failures, and evaluations.

To bridge that gap, the article introduces Progress AI Observability Platform and demonstrates how to instrument applications using OpenTelemetry and the .NET Activity pipeline. Readers learn how to capture traces, monitor workflows across sessions, analyze costs, and drill into telemetry data for deeper debugging and operational insight.

By combining DevUI with observability tooling, the article demonstrates a more complete agent development loop that evolves from local experimentation into production-ready diagnostics, tracing, evaluation, and monitoring for enterprise AI applications.</summary>
    <published>2026-05-26T19:41:09Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Ed Charbeneau </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17348909/from-devui-to-observability-evolving-the-agent-dev-loop-in-microsoft-agent-framework"/>
    <content type="text"><![CDATA[<p>DevUI is a lightweight standalone application included with Microsoft Agent Framework that helps developers run, inspect, and debug agents and workflows during development. The tool provides a web-based interface for interactive testing alongside an OpenAI-compatible API backend, making it easier to iterate on workflows before integrating them into a larger application.</p><p>The key to DevUI&rsquo;s usefulness is its ability to shorten the agent development loop by giving developers immediate visibility into workflow execution and agent interactions. However, as agentic applications move from prototype to production, a new observability gap begins to appear. Developers need to understand not only what happened during a local debugging session, but how agents behave across users, sessions, models, tools, latency, cost, quality, and failures.</p><p>In this article, you'll use DevUI to develop, visualize, and debug workflows locally before expanding into observability to support multi-session debugging and production-ready diagnostics. By extending the agent development loop beyond local testing, observability helps bridge the gap between development-time insight and the operational visibility required for production systems.</p><h2 id="installing-the-microsoft-agent-framework-templates">Installing the Microsoft Agent Framework Templates</h2><p>To get started with DevUI and Microsoft Agent Framework, install the project templates from NuGet. The templates provide a starter project for building and debugging agent-based applications in .NET.</p><p>Install the templates using the .NET CLI:</p><pre><code class="language-bash">dotnet new install Microsoft.Agents.AI.ProjectTemplates::1.3.0-preview.1.26251.3
</code></pre><p>Once installed, the templates become available through Visual Studio under <strong>File &gt; New Project</strong>. Search for <code>Agent</code> to locate the available Microsoft Agent Framework project templates, shown in Figure 1.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/observability/ai-agent-template.png?sfvrsn=27c6cc2b_2" height="457" style="max-width:100%;height:auto;" width="800" alt="" /></p><p><strong>Figure 1: </strong>The project template shown in Visual Studio.</p><p>You can also create a new project directly from the command line:</p><pre><code class="language-bash">dotnet new aiagent-webapi
</code></pre><p>The generated project includes a starter implementation configured for Microsoft Agent Framework and DevUI, making it easy to begin experimenting with agents, orchestration, and workflow debugging locally.</p><h3 id="exploring-the-template">Exploring the Template</h3><p>The <code>aiagent-webapi</code> template includes a complete sample application that demonstrates how agents and workflows operate within Microsoft Agent Framework.</p><p>The sample application contains two hosted agents and a workflow:</p><ol><li><p><strong>Writer Agent</strong><br />Generates short stories based on a provided topic while keeping responses under 300 words.</p></li><li><p><strong>Editor Agent</strong><br />Reviews and refines the generated story by improving grammar, readability, and style while maintaining the word limit.</p></li><li><p><strong>Publisher Workflow Agent</strong><br />Coordinates the workflow between the writer and editor agents using a sequential process that passes content through each stage of execution.</p></li></ol><p>The agents are exposed through OpenAI-compatible API endpoints, making the sample easy to test with DevUI and simple to integrate with external tools and applications. By keeping the architecture approachable, the template creates a practical environment for understanding how agent orchestration works in a real .NET application.</p><h3 id="understanding-addagent-and-ihostedagentbuilder">Understanding <code>AddAgent</code> and <code>IHostedAgentBuilder</code></h3><p>Microsoft Agent Framework registers agents using the <code>AddAgent</code> extension method during application startup. This approach integrates agents directly into the standard ASP.NET Core dependency injection system, making the programming model feel familiar to .NET developers.</p><pre><code class="language-csharp">builder.AddAIAgent("writer", 
    "You write short stories (300 words or less) about the specified topic.");
</code></pre><p>The <code>AddAgent</code> method returns an <code>IHostedAgentBuilder</code>, which provides additional configuration options for the agent and its hosting behavior. The template uses this pattern to register the Writer, Editor, and Publisher workflow agents so they can be discovered and executed through DevUI and the framework&rsquo;s OpenAI-compatible endpoints.</p><h2 id="running-the-application">Running the Application</h2><p>With the project created, the next step is running the application and launching DevUI. Start the application using the .NET CLI:</p><pre><code class="language-bash">dotnet run
</code></pre><p>The application exposes OpenAI-compatible API endpoints that can be accessed from compatible clients and tools. During development, the application also maps a <code>/devui/</code> route that launches the Agent Framework development UI.</p><p>When running the project from Visual Studio or another IDE, the browser automatically opens to the DevUI endpoint. DevUI provides a web-based interface for interacting with agents and workflows while using the framework&rsquo;s Responses and Conversations endpoints behind the scenes. This creates a fast feedback loop for testing prompts, tracing interactions, and validating workflow execution during development.</p><h2 id="understanding-the-agent-development-loop">Understanding the Agent Development Loop</h2><p>DevUI improves the agent development loop by making it easier to build, run, inspect, and refine workflows during development. Instead of treating agents as black-box processes, developers can interact with workflows in real time and observe how messages move between agents during execution.</p><p>Both agents and workflows can be independently selected from the interface, shown in Figure 2.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/observability/agent-workflow-selection-steps.png?sfvrsn=a4dd7e97_2" height="479" style="max-width:100%;height:auto;" width="600" alt="" /></p><p><strong>Figure 2: </strong>The browser running DevUI with the agent selection showing the available agents and workflows. 1) The editor and writer agents can be selected and executed independently. 2) The publisher workflow can be executed.</p><p>This visibility becomes increasingly important as workflows grow more complex. Multi-agent systems introduce challenges that traditional request-response applications typically avoid, including non-deterministic behavior, chained execution steps, and state shared across conversations.</p><p>To run a selected workflow in DevUI, open a prompt by clicking Configure and Run. Then enter a prompt in the dialog box, shown in Figure 3. The prompt text is passed into the workflow and the entire workflow will execute when Run Workflow is clicked.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/observability/run-workflow.png?sfvrsn=7dce9b17_2" height="355" style="max-width:100%;height:auto;" width="500" alt="" /></p><p><strong>Figure 3: </strong>The Configure Workflow Input dialog box.</p><p>DevUI creates an approachable debugging experience, its current model focuses on short-lived, single-session workflows. The final output is clearly displayed on screen, shown in Figure 4.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/observability/workflow-complete.png?sfvrsn=906d9d62_2" height="584" style="max-width:100%;height:auto;" width="500" alt="" /></p><p><strong>Figure 4: </strong>The output shown in the workflow execution timeline.</p><p>This works well for experimentation and local testing. However, debugging becomes more difficult once multiple sessions are running concurrently or workflows require historical visibility for troubleshooting.</p><p>For example, the execution information is shown for this run in the Events panel in Figure 5. While the status and token information is visible from DevUI, it does not persist once the application stops.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/events.png?sfvrsn=a0b2a54e_2" height="431" style="max-width:100%;height:auto;" width="395" alt="" /></p><p><strong>Figure 5:</strong> The Events panel displays a completed status along with usage statistics for token consumption.</p><p>As developers move beyond isolated testing scenarios, the agent development loop begins to require more than interactive debugging alone. Understanding how workflows behave across sessions, tracing execution over time, and diagnosing failures in larger systems naturally leads to the next step: observability.</p><blockquote><p>Note: <a href="https://github.com/microsoft/agent-framework/issues/5806">Currently Telemetry is not supported within DevUI when using .NET.</a></p></blockquote><h2 id="introducing-ai-observability-across-tracing-debugging-cost-and-evaluation">Introducing AI Observability Across Tracing, Debugging, Cost, and Evaluation</h2><p>As workflows grow beyond simple development scenarios, observability becomes a critical part of the agent development loop. This is where the observability gap begins to appear. DevUI helps developers understand what happened during a local debugging session, but production systems require persistent visibility into how agents behave across users, sessions, models, tools, latency, cost, quality, and failures. While DevUI provides interactive debugging for local workflows, observability extends visibility across sessions, tool calls, evaluations, and distributed execution paths using OpenTelemetry and .NET&rsquo;s built-in <code>Activity</code> pipeline.</p><p>The .NET SDK instruments agents built with either <code>IChatClient</code> from <code>Microsoft.Extensions.AI</code> or <code>IAgent</code> from Microsoft Agent Framework. This allows developers to trace LLM requests, streaming responses, workflow execution, and tool calls while integrating naturally with existing .NET telemetry infrastructure.</p><p>To enable observability, we'll use <a href="https://www.telerik.com/ai-observability-platform">Progress AI Observability Platform</a>. The Progress AI Observability Platform is a cloud-based platform for tracing, debugging, cost analysis, and evaluation of AI applications. It provides visibility into how AI agents behave across models, tools, and sessions, helping teams identify issues as they happen, understand their impact, and continuously improve workflow quality over time.</p><p>Start by installing the .NET SDK for Progress AI Observability Platform.</p><pre><code class="language-bash">dotnet add package Progress.Observability.Instrumentation
</code></pre><p>Next, configure the desired options. Tags can be included for easier filtering within the observability reporting screen. In this example, the Environment name will capture: Development, Staging, and Production tags. Once the options are declared, the tracer is initialized.</p><pre><code class="language-csharp">// Configure the observiablity options, tags, and keys
var observabilityOptions = new ObservabilityOptions()
{
    AppName = builder.Environment.ApplicationName,
    ApiKey = builder.Configuration["Progress:ObservabilityKey"]!,
    AdditionalTags = new List&lt;string&gt; { builder.Environment.EnvironmentName }
};

// Initialize the observability tracer
ObservabilityTracer.Initialize(observabilityOptions);
</code></pre><p>Start capturing traces at the top level of applications using <code>IChatClient</code>, observability is added directly to the client:</p><pre><code class="language-csharp">var chatClient = new ChatClient(
        "gpt-4o-mini",
        new ApiKeyCredential(builder.Configuration["AzureOpenAI:Key"] ?? throw new InvalidOperationException("Missing configuration: AzureOpenAI:Key")),
        new OpenAIClientOptions { Endpoint = azureOpenAIEndpoint })
    .AsIChatClient()
    .AddObservability(o =&gt; o = observabilityOptions);

</code></pre><p>Applications using Microsoft Agent Framework can initialize observability through <code>AddObservability()</code> during agent construction:</p><pre><code class="language-csharp">var chatClient = new ChatClient(
        "gpt-4o-mini",
        new ApiKeyCredential(builder.Configuration["AzureOpenAI:Key"] ?? throw new InvalidOperationException("Missing configuration: AzureOpenAI:Key")),
        new OpenAIClientOptions { Endpoint = azureOpenAIEndpoint })
    .AsIChatClient()
    .AddObservability(o =&gt; o = observabilityOptions);
</code></pre><p>With observability enabled, you'll run the application using DevUI. Exercise the agents and workflows as before, but this time each session is collected for a deeper analysis through the AI Observability Platform. From the dashboard, you can perform cost analysis, run evaluations, and drill into traces across multiple sessions.</p><p>From the main Observations tab, all sessions are shown for a given period. Through the tagging feature the data is easily categorized, seen in Figure 6.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/observability/observability-all.png?sfvrsn=c8dd45c6_2" height="366" style="max-width:100%;height:auto;" width="800" alt="" /></p><p><strong>Figure 6: </strong>The observability platform with the Observations tab selected. 1) The date range and filter selection is chosen. 2) All sessions within the filter criteria.</p><p>Clicking on an individual item displays a complete breakdown of the trace log, see Figure 7. Including status, latency, cost and more. A tree interface allows deeper debugging, showing individual agent calls within the workflow, along with their corresponding inputs and outputs.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/observability-drill-down.png?sfvrsn=92ef854f_2" height="458" style="max-width:100%;height:auto;" width="800" alt="" /></p><p><strong>Figure 7: </strong>A trace is selected. 1) The status section shows top level telemetry data including status, latency and costs. 2) A tree interface expands into nested trace activity. 3) The inputs and outputs of the selected agent are displayed.</p><p>This approach creates a more complete agent development loop, allowing local experimentation in DevUI to evolve into production-ready diagnostics and monitoring. In this article, tracing serves as the primary example, however production AI observability extends much further. Teams also need debugging, cost analysis, evaluation, governance, and operational insight to successfully operate AI systems at scale.</p><p>Progress AI Observability Platform supports that broader production view, helping teams connect trace-level detail with the trust, scale, and operational control required for enterprise AI applications.</p><h2 id="next-steps">Next Steps</h2><p>DevUI provides a strong starting point for building and debugging agents locally, but production AI systems require deeper visibility across tracing, debugging, evaluation, and operational monitoring.</p><p>Explore the <a href="https://www.telerik.com/ai-observability-platform">Progress AI Observability Platform</a> to see how observability extends the agent development loop from local experimentation to production-ready diagnostics.</p><p>Have questions or want to share what you're building? Join the conversation with the team on <a href="https://discord.gg/tK6RuSWKf">Discord</a>.</p><img src="https://feeds.telerik.com/link/23052/17348909.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:c37f48f7-cf9d-4e8d-95b3-010546859051</id>
    <title type="text">Protect Your Entities with Domain Validation</title>
    <summary type="text">Learn about error-prone domain validations in ASP.NET Core and how to correctly model them.</summary>
    <published>2026-05-19T13:32:48Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17344158/protect-entities-domain-validation"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn about error-prone domain validations in ASP.NET Core and how to correctly model them.</span></p><p>In ASP.NET Core applications, developers often underestimate the importance of validating objects and classes. An <code>if</code> statement in the controller, a <code>FluentValidation</code> in the request or some <code>DataAnnotations</code> in the model, and that&rsquo;s it. The problem is that, if care isn&rsquo;t taken when implementing domain validations, responsibility ends up leaking, which can compromise the entire system&rsquo;s evolution.</p><p>In this article, we will analyze common examples of domain validation that are highly prone to errors and how these validations should be correctly modeled according to the principles of Domain-Driven Design (DDD).</p><h2 id="poorly-structured-validations">Poorly Structured Validations</h2><h3 id="validation-in-the-controller">1. Validation in the Controller</h3><p>Although it speeds up development, implementing validations in API controllers is discouraged, as it makes the controller highly coupled to business rules. Furthermore, the rules created there are impossible to reuse. Finally, validations in controllers allow other parts of the code to execute the same actions, bypassing the rules.</p><p>Consider the following example:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span><span class="token function">Route</span><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token punctuation">[</span>ApiController<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderController</span> <span class="token punctuation">:</span> ControllerBase
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> OrderRepository _orderRepository<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">OrderController</span><span class="token punctuation">(</span>OrderRepository orderRepository<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _orderRepository <span class="token operator">=</span> orderRepository<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>HttpPost<span class="token punctuation">]</span>
    <span class="token keyword">public</span> IActionResult <span class="token function">Create</span><span class="token punctuation">(</span>CreateOrderDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Total <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Total must be greater than zero"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Items <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token operator">!</span>dto<span class="token punctuation">.</span>Items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Order must have at least one item"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            Total <span class="token operator">=</span> dto<span class="token punctuation">.</span>Total<span class="token punctuation">,</span>
            Items <span class="token operator">=</span> dto<span class="token punctuation">.</span>Items
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        _orderRepository<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">Ok</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The code above is a common example of validations in the Controller, and it&rsquo;s a bad example because it has all the flaws mentioned earlier. Note that the <code>_orderRepository.Add(order);</code> method is called at the end, meaning there are loopholes here.</p><p>Imagine that the parameter <code>CreateOrderDto dto</code> has a total greater than zero, and the Items list has one item, but the rest of the properties are null. Even so, the <code>Add</code> method will be called and will create problematic entities or generate a bug.</p><h3 id="validation-in-the-service-class">2. Validation in the Service Class</h3><p>Although common, the use of validations in the Service class should also be avoided because they place business rules in the wrong place. Considering the previous example, they only changed location but still remain a problem.</p><p>When a business rule is coupled to a Service, the domain model becomes passive, and entities can be created or modified in invalid states because there is nothing to prevent them from doing so. The result is a fragile system, as object validation ceases to be a priority and becomes solely dependent on the execution flow.</p><p>Another problem is rule duplication. In large systems, the same rule is often needed in more than one use case. When it is in the Service, it ends up being copied to other services, handlers or jobs, which increases the risk of inconsistency and hinders business evolution.</p><p>Finally, the Service Layer is responsible for orchestrating use cases, coordinating repositories, transactions and external calls. When it validates domain rules, it mixes responsibilities and becomes a central point of complexity, bloated with if statements and exceptions that don&rsquo;t belong to it.</p><p>The example below shows what a Service class looks like with business rules defined in it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderService</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> OrderRepository _orderRepository<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">OrderService</span><span class="token punctuation">(</span>OrderRepository orderRepository<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _orderRepository <span class="token operator">=</span> orderRepository<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Create</span><span class="token punctuation">(</span>CreateOrderDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Total <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Exception</span><span class="token punctuation">(</span><span class="token string">"Total must be greater than zero"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>dto<span class="token punctuation">.</span>Items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Exception</span><span class="token punctuation">(</span><span class="token string">"Order must have at least one item"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Total<span class="token punctuation">,</span> dto<span class="token punctuation">.</span>Items<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _orderRepository<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that the logic of <code>if</code> and <code>else</code> statements remains the same, it has only changed location, allowing objects to change state and create corrupted states.</p><h3 id="using-data-annotations">3. Using Data Annotations</h3><p>Another common form of validation is through the Data Annotations Model Binder, which is implemented using attributes placed above the properties of an entity class.</p><p>The problem is that, as they make the domain dependent on a framework, the validation only works in binding (MVC), and they may not work in non-web scenarios such as workers, messaging and tests.</p><pre class=" language-csharp"><code class="prism  language-csharp"> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Order</span>
  <span class="token punctuation">{</span>
      <span class="token punctuation">[</span>Required<span class="token punctuation">]</span>
      <span class="token punctuation">[</span><span class="token function">Range</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">double</span><span class="token punctuation">.</span>MaxValue<span class="token punctuation">)</span><span class="token punctuation">]</span>
      <span class="token keyword">public</span> <span class="token keyword">decimal</span> Total <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
      <span class="token keyword">public</span> List<span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span> Items <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
</code></pre><p>Note that we are requiring the Total property to have a minimum value of 1. But even so, it is not yet a secure validation because it is still possible to create an entity with an invalid state.</p><h2 id="domain-validation-with-ddd">Domain Validation with DDD</h2><p>Domain-Driven Design emphasizes the importance of keeping validations within the domain. The main reason is that this way, the domain becomes self-protecting.</p><p>Entities and aggregates cease to be passive structures (as seen in previous examples) and ensure that their rules are always respected, regardless of where or how they are used.</p><p>Another positive aspect of this approach is the model&rsquo;s coherence. When business rules are in the domain, they are closer to the concept they represent, making the code more expressive and easier to understand. Reading an entity or a behavioral method reveals the rules governing that concept. In other words, you understand the intention behind that behavior.</p><p>Finally, implementing validations in the domain makes system evolution safer. New use cases can be added without fear of breaking existing rules because the domain itself acts as a safety barrier. This reduces maintenance costs and makes the system more resilient to business growth and changes.</p><p>Next, we&rsquo;ll look at an example of a domain guided by DDD principles and analyze each point. You can access the source code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/domain-validations">Domain Validations code</a>.</p><p>Order class with Domain Validations:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Order</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> List<span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span> _items <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> Guid Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateTime CreatedAt <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> IReadOnlyCollection<span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span> Items <span class="token operator">=</span><span class="token operator">&gt;</span> _items<span class="token punctuation">.</span><span class="token function">AsReadOnly</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Total <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token function">Order</span><span class="token punctuation">(</span>IEnumerable<span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span> items<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>items <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token operator">!</span>items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order must have at least one item."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Id <span class="token operator">=</span> Guid<span class="token punctuation">.</span><span class="token function">NewGuid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">;</span>

        <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> item <span class="token keyword">in</span> items<span class="token punctuation">)</span>
            <span class="token function">AddItem</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">ValidateItemsQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">AddItem</span><span class="token punctuation">(</span>OrderItem item<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>item <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order item cannot be null."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        _items<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">RecalculateTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">RecalculateTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Total <span class="token operator">=</span> _items<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span>i <span class="token operator">=</span><span class="token operator">&gt;</span> i<span class="token punctuation">.</span>Subtotal<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>Total <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order total must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">ValidateItemsQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>_items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order cannot exist without items."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Item class with Domain Validations:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderItem</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Guid ProductId <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Price <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Quantity <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Subtotal <span class="token operator">=</span><span class="token operator">&gt;</span> Price <span class="token operator">*</span> Quantity<span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token function">OrderItem</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// EF</span>

    <span class="token keyword">public</span> <span class="token function">OrderItem</span><span class="token punctuation">(</span>Guid productId<span class="token punctuation">,</span> <span class="token keyword">decimal</span> price<span class="token punctuation">,</span> <span class="token keyword">int</span> quantity<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>productId <span class="token operator">==</span> Guid<span class="token punctuation">.</span>Empty<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"ProductId is required."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>price <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Price must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>quantity <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Quantity must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        ProductId <span class="token operator">=</span> productId<span class="token punctuation">;</span>
        Price <span class="token operator">=</span> price<span class="token punctuation">;</span>
        Quantity <span class="token operator">=</span> quantity<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="private-setters-and-readonly-properties">Private Setters and ReadOnly Properties</h3><p>The first aspect we can notice in this new version of the Order class is that the Items property is private, which means it is inaccessible outside the class: <code>private readonly List&lt;OrderItem&gt; _items = new();</code>.</p><p>We also have a public list of items: <code>public IReadOnlyCollection&lt;OrderItem&gt; Items =&gt; _items.AsReadOnly();</code>, but note that it is defined as read-only. This means that it can be accessed by external sources, but only for reading the data and never for modification.</p><p>Another factor protecting the class properties is that, despite being public, they have private setters: <code>public Guid Id { get; private set; }</code>, preventing external sources from modifying their states.</p><h3 id="protected-constructor">Protected Constructor</h3><p>Protecting the constructor method means allowing only valid states of an entity to be created. In our example, we are defining rules within the constructor:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token function">Order</span><span class="token punctuation">(</span>IEnumerable<span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span> items<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>items <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token operator">!</span>items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order must have at least one item."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Id <span class="token operator">=</span> Guid<span class="token punctuation">.</span><span class="token function">NewGuid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">;</span>

    <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> item <span class="token keyword">in</span> items<span class="token punctuation">)</span>
        <span class="token function">AddItem</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token function">ValidateItemsQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">AddItem</span><span class="token punctuation">(</span>OrderItem item<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>item <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order item cannot be null."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    _items<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">RecalculateTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">RecalculateTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    Total <span class="token operator">=</span> _items<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span>i <span class="token operator">=</span><span class="token operator">&gt;</span> i<span class="token punctuation">.</span>Subtotal<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>Total <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order total must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">ValidateItemsQuantity</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>_items<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DomainException</span><span class="token punctuation">(</span><span class="token string">"Order cannot exist without items."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that the item quantity validation is performed within the constructor. That is, when creating a new object state, values are also set for the <code>Id</code> and <code>CreatedAt</code> properties. Finally, the <code>ValidateItemsQuantity</code> method verifies if the private property <code>_items</code> has actually been loaded with items, adding an extra layer of validation. In this way, the database will only receive valid states, a corrupted or incomplete entity will never be inserted, enabling data consistency.</p><h3 id="domain-exceptions">Domain Exceptions</h3><p>Domain exceptions are errors thrown when a business rule (invariant) is violated. Unlike technical failures such as database outages or timeouts, domain exceptions represent violations of the system&rsquo;s business rules, such as an order without items, for example.</p><p>Domain exceptions are important because they help prevent an entity or aggregate from existing in an invalid state. If a rule is broken, the domain needs to react immediately, and the exception is a suitable mechanism to keep inconsistent data from reaching the database or being manipulated in any way.</p><p>In the previous example, we validated whether the value was less than zero and whether the list of items was empty. If either condition is met, a <code>DomainException</code> will be thrown, which is a custom exception. The code below shows how the exception is implemented:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> DomainValidation<span class="token punctuation">;</span>

<span class="token punctuation">[</span>Serializable<span class="token punctuation">]</span>
<span class="token keyword">internal</span> <span class="token keyword">class</span> <span class="token class-name">DomainException</span> <span class="token punctuation">:</span> Exception
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">DomainException</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token function">DomainException</span><span class="token punctuation">(</span><span class="token keyword">string</span><span class="token operator">?</span> message<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token function">DomainException</span><span class="token punctuation">(</span><span class="token keyword">string</span><span class="token operator">?</span> message<span class="token punctuation">,</span> Exception<span class="token operator">?</span> innerException<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> innerException<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="is-fluentvalidation-still-useful">Is FluentValidation Still Useful?</h3><p>FluentValidation is a widely used .NET library for validations, and even in scenarios where we use the domain validation approach, it certainly remains useful.</p><p>The main functionality of FluentValidation is to validate input data, not business rules. Therefore, by using it in the application, we can obtain an extra layer of validation, preventing corrupted data from reaching the domain.</p><p>The code below demonstrates how FluentValidation can be used to validate input data:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> FluentValidation<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateOrderRequest</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> List<span class="token operator">&lt;</span>CreateOrderItemRequest<span class="token operator">&gt;</span> Items <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateOrderRequestValidator</span> <span class="token punctuation">:</span> AbstractValidator<span class="token operator">&lt;</span>CreateOrderRequest<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">CreateOrderRequestValidator</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Items<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">NotNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithMessage</span><span class="token punctuation">(</span><span class="token string">"Items cannot be null."</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Must</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithMessage</span><span class="token punctuation">(</span><span class="token string">"Order must contain at least one item."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">RuleForEach</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Items<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">SetValidator</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">CreateOrderItemRequestValidator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateOrderItemRequest</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Guid ProductId <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Price <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Quantity <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateOrderItemRequestValidator</span> <span class="token punctuation">:</span> AbstractValidator<span class="token operator">&lt;</span>CreateOrderItemRequest<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">CreateOrderItemRequestValidator</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>ProductId<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">NotEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithMessage</span><span class="token punctuation">(</span><span class="token string">"ProductId is required."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Price<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GreaterThan</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithMessage</span><span class="token punctuation">(</span><span class="token string">"Price must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Quantity<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GreaterThan</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithMessage</span><span class="token punctuation">(</span><span class="token string">"Quantity must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="when-out-of-domain-validations-make-sense">When Out-of-Domain Validations Make Sense</h2><p>Validations in the Controller and Service classes make sense when they are not business rules, but rather validations such as security, flow or format.</p><p>It is common to perform authentication/authorization validations in the Controller, because this is the responsibility of the edge (API), and if a user or system is not properly authenticated/authorized, it should not have access to the domain or the rest of the system; it should be blocked at the entrance.</p><p>The Service class, on the other hand, can have validations used to manage operational flows, for example, checking if the customer exists before creating an order, checking if the product exists, checking if there is already an open order for the customer.</p><p>In this way, we do not validate the internal state of the entity. Instead, we validate the interactions between aggregates or external systems.</p><h2 id="conclusion">Conclusion</h2><p>Implementing domain validations in an application means explicitly stating the reason for that domain&rsquo;s existence and the rules that determine its behavior. Furthermore, domain validations keep an invalid object from reaching the database, preventing future bugs and the resulting damage.</p><p>Throughout this post, we&rsquo;ve seen examples of incorrectly implemented validations, scattered across controllers or services, resulting in fragile and difficult-to-maintain code. In contrast, we implemented validations directly on the entities, applying DDD principles.</p><p>I hope this post has helped you see the domain as the heart of the application and the importance of keeping it consistent, protected and expressive.</p><hr /><p><strong>More on DDD: </strong><a href="https://www.telerik.com/blogs/getting-started-domain-driven-design-aspnet-core" target="_blank">Getting Started with Domain-Driven Design in ASP.NET Core</a></p><img src="https://feeds.telerik.com/link/23052/17344158.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:1feea2d1-2fa0-48fa-a5fa-f1d611bdf2d5</id>
    <title type="text">Streaming Server Events with SSE and Blazor</title>
    <summary type="text">Understand the SSE standard, the .NET 10  changes to simplify SSE endpoints and how to add real-time events to your Blazor clients.</summary>
    <published>2026-05-12T13:18:03Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17339195/streaming-server-events-sse-blazor"/>
    <content type="text"><![CDATA[<p><span class="featured">Understand the SSE standard, the .NET 10 changes to simplify SSE endpoints and how to add real-time events to your Blazor clients.</span></p><p>In the world of web development, there are scenarios where you need to receive real-time information, such as notifications about an event, server metrics or live logs. One possible solution to achieve this is the use of Server-Sent Events (SSE). With the arrival of .NET 10, implementing this type of solution has become much easier, so let&rsquo;s see how to integrate this web standard into your projects.</p><h2 id="what-are-server-sent-events">What Are Server-Sent Events?</h2><p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events">Server-Sent Events (SSE)</a> are not a new technology. Their origins date back to around 2006, and they are a web standard that allows data to be sent to clients continuously using a persistent HTTP connection.</p><p>If you wonder the difference compared to other similar technologies, we can make a comparison:</p><ul><li><p><strong>SSE vs. Polling</strong>: In the case of polling, the direction goes from the client to the server, using the HTTP protocol. We can see it as a client asking the server if there are updates at certain intervals. It involves low implementation complexity.</p></li><li><p><strong>SSE vs. WebSockets</strong>: With WebSockets there is bidirectional communication. It involves high complexity and is ideal for scenarios like video games, chats, etc. It works over the WS protocol.</p></li><li><p><strong>SSE vs. SignalR</strong>: SignalR also establishes bidirectional communication, with medium implementation complexity. It allows the use of binary protocols, which makes it a very good option for scalable enterprise apps. It uses variable protocols.</p></li></ul><p>Analyzing the above, we can conclude that SSE is ideal when the data flow is unidirectional, you need it to work over the HTTP protocol and it must be easy to implement.</p><h2 id="how-does-the-sse-protocol-work">How Does the SSE Protocol Work?</h2><p>The workings behind the scenes of the SSE protocol, in broad terms, are as follows: a server SSE endpoint needs to send an HTTP response of the type <code>Content-Type: text/event-stream</code>. The response looks similar to the following:</p><pre class=" language-json"><code class="prism  language-json">event<span class="token punctuation">:</span> app<span class="token operator">-</span>event
data<span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token string">"id"</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">"time"</span><span class="token punctuation">:</span><span class="token string">"10:30:45"</span><span class="token punctuation">,</span><span class="token string">"level"</span><span class="token punctuation">:</span><span class="token string">"Info"</span><span class="token punctuation">,</span><span class="token string">"source"</span><span class="token punctuation">:</span><span class="token string">"OrderService"</span><span class="token punctuation">,</span><span class="token string">"message"</span><span class="token punctuation">:</span><span class="token string">"Order #1234 placed successfully"</span><span class="token punctuation">}</span>
</code></pre><p>In the code above, there are some fields we should pay attention to:</p><ul><li><code>event</code>: Specifies the name of the event</li><li><code>data</code>: Is the content of the message</li><li><code>id</code>: Identifier used to perform a reconnection if needed</li></ul><h2 id="whats-new-in-.net-10-for-sse">What&rsquo;s New in .NET 10 for SSE</h2><p>The most important update in .NET 10 for working with SSE is that the ability to return a <code>ServerSentEvents</code> using the API <code>TypedResults.ServerSentEvents</code> has been implemented. This means that the method <code>TypedResults.ServerSentEvents&lt;T&gt;()</code> allows converting any <code>IAsyncEnumerable&lt;T&gt;</code> into a formatted SSE stream without needing to do anything else. Tasks like JSON serialization, HTTP headers, connection closing, etc. are handled automatically.</p><p>To better understand how it works, let&rsquo;s create a project using the SSE standard with ASP.NET 10.</p><h2 id="building-an-sse-project-using-asp.net-10">Building an SSE Project Using ASP.NET 10</h2><p>To practice the theoretical concepts covered so far, we&rsquo;ll create a page that shows a simulation of receiving events from backend services in real time. Using a Progress Telerik UI for <a target="_blank" href="https://www.telerik.com/blazor-ui/grid">Blazor Grid</a>, we&rsquo;ll perform tasks like filtering, grouping and analysis quickly.</p><h3 id="creating-and-configuring-the-project">Creating and Configuring the Project</h3><p>The first thing we&rsquo;ll do is create a project using the <strong>Blazor Web App</strong> template, selecting an <strong>Interactive render mode</strong> of <strong>Server</strong> and <strong>Interactivity location</strong> of <strong>Global</strong>. Next, we&rsquo;ll follow the official installation guide for <a target="_blank" href="https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a> to configure the project and be able to use the Telerik components.</p><h3 id="creating-an-eventbroadcaster">Creating an EventBroadcaster</h3><p>For our project, we&rsquo;ll create an event broadcaster, which in simple terms will be a bus that SSE clients can connect to to consume events, while backend services will use it to publish events. First, we&rsquo;ll create a record that represents a system event:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SSEDemo<span class="token punctuation">.</span>Services
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> record <span class="token function">AppEvent</span><span class="token punctuation">(</span><span class="token keyword">int</span> Id<span class="token punctuation">,</span> <span class="token keyword">string</span> Time<span class="token punctuation">,</span> <span class="token keyword">string</span> Level<span class="token punctuation">,</span> <span class="token keyword">string</span> Source<span class="token punctuation">,</span> <span class="token keyword">string</span> Message<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Next, we will create the class <code>EventBroadcaster</code> which will have the following definition:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventBroadcaster</span>
<span class="token punctuation">{</span>
    <span class="token comment">//1.</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> Channel<span class="token operator">&lt;</span>AppEvent<span class="token operator">&gt;</span> _channel <span class="token operator">=</span> Channel<span class="token punctuation">.</span><span class="token generic-method function">CreateBounded<span class="token punctuation">&lt;</span>AppEvent<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>
    <span class="token keyword">new</span> <span class="token class-name">BoundedChannelOptions</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> FullMode <span class="token operator">=</span> BoundedChannelFullMode<span class="token punctuation">.</span>DropOldest <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> _nextId<span class="token punctuation">;</span>

    <span class="token comment">// 2.</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Publish</span><span class="token punctuation">(</span><span class="token keyword">string</span> level<span class="token punctuation">,</span> <span class="token keyword">string</span> source<span class="token punctuation">,</span> <span class="token keyword">string</span> message<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> id <span class="token operator">=</span> Interlocked<span class="token punctuation">.</span><span class="token function">Increment</span><span class="token punctuation">(</span><span class="token keyword">ref</span> _nextId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> evt <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AppEvent</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> DateTime<span class="token punctuation">.</span>Now<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"HH:mm:ss"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> level<span class="token punctuation">,</span> source<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _channel<span class="token punctuation">.</span>Writer<span class="token punctuation">.</span><span class="token function">TryWrite</span><span class="token punctuation">(</span>evt<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 3.</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> IAsyncEnumerable<span class="token operator">&lt;</span>AppEvent<span class="token operator">&gt;</span> <span class="token function">Subscribe</span><span class="token punctuation">(</span>
        <span class="token punctuation">[</span>System<span class="token punctuation">.</span>Runtime<span class="token punctuation">.</span>CompilerServices<span class="token punctuation">.</span>EnumeratorCancellation<span class="token punctuation">]</span> CancellationToken ct<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>ct<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">bool</span> hasData<span class="token punctuation">;</span>
            <span class="token keyword">try</span>
            <span class="token punctuation">{</span>
                hasData <span class="token operator">=</span> <span class="token keyword">await</span> _channel<span class="token punctuation">.</span>Reader<span class="token punctuation">.</span><span class="token function">WaitToReadAsync</span><span class="token punctuation">(</span>ct<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">OperationCanceledException</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>hasData<span class="token punctuation">)</span> <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>

            <span class="token keyword">while</span> <span class="token punctuation">(</span>_channel<span class="token punctuation">.</span>Reader<span class="token punctuation">.</span><span class="token function">TryRead</span><span class="token punctuation">(</span><span class="token keyword">out</span> <span class="token keyword">var</span> evt<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">yield</span> <span class="token keyword">return</span> evt<span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, we have the following sections:</p><ol><li><p>Sets up the channel where events will be passed, with a queue limit of 100 messages. <code>FullMode = BoundedChannelFullMode.DropOldest</code> allows that if the queue fills up and a new message arrives, the oldest one is removed to make room for the new message.</p></li><li><p>The method <code>Publish</code> is the one that will be used to emit events. <code>Interlocked.Increment</code> allows generating sequential IDs in a safe manner, while <code>AppEvent</code> packages the received data together with the obtained id. Finally the method <code>TryWrite()</code> attempts to write the event to the channel asynchronously.</p></li><li><p>On the other hand, the method <code>Subscribe</code> returns a continuous stream of asynchronous data through the use of <code>IAsyncEnumerable&lt;AppEvent&gt;</code>. Inside its implementation an infinite loop is created that waits for data to be available in the channel. Once an event enters the channel, it is emitted to the consumer. This will happen until the <code>CancellationToken</code> called <code>ct</code> is canceled.</p></li></ol><h3 id="generating-test-events">Generating Test Events</h3><p>To test the bus defined above, we&rsquo;ll create a service that simulates the activity of multiple services:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DemoEventGenerator</span><span class="token punctuation">(</span>EventBroadcaster broadcaster<span class="token punctuation">)</span> <span class="token punctuation">:</span> BackgroundService
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Levels <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Info"</span><span class="token punctuation">,</span> <span class="token string">"Warning"</span><span class="token punctuation">,</span> <span class="token string">"Error"</span><span class="token punctuation">,</span> <span class="token string">"Success"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Sources <span class="token operator">=</span>
        <span class="token punctuation">[</span><span class="token string">"OrderService"</span><span class="token punctuation">,</span> <span class="token string">"PaymentService"</span><span class="token punctuation">,</span> <span class="token string">"InventoryService"</span><span class="token punctuation">,</span> <span class="token string">"AuthService"</span><span class="token punctuation">,</span> <span class="token string">"ShippingService"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Messages <span class="token operator">=</span>
    <span class="token punctuation">[</span>
        <span class="token punctuation">[</span><span class="token string">"Order #{0} placed successfully"</span><span class="token punctuation">,</span> <span class="token string">"New customer registered"</span><span class="token punctuation">,</span> <span class="token string">"Product viewed: SKU-{0}"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"High latency detected: {0}ms"</span><span class="token punctuation">,</span> <span class="token string">"Retry attempt #{0}"</span><span class="token punctuation">,</span> <span class="token string">"Queue depth above threshold"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"Payment failed for order #{0}"</span><span class="token punctuation">,</span> <span class="token string">"Database timeout after {0}ms"</span><span class="token punctuation">,</span> <span class="token string">"Service unreachable"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"Deployment completed v2.{0}"</span><span class="token punctuation">,</span> <span class="token string">"Health check passed"</span><span class="token punctuation">,</span> <span class="token string">"Cache refreshed ({0} items)"</span><span class="token punctuation">]</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">ExecuteAsync</span><span class="token punctuation">(</span>CancellationToken stoppingToken<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> rng <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>stoppingToken<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>            
            <span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">1500</span><span class="token punctuation">,</span> <span class="token number">4000</span><span class="token punctuation">)</span><span class="token punctuation">,</span> stoppingToken<span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> levelIdx <span class="token operator">=</span> rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Levels<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> level <span class="token operator">=</span> Levels<span class="token punctuation">[</span>levelIdx<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> source <span class="token operator">=</span> Sources<span class="token punctuation">[</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Sources<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> templates <span class="token operator">=</span> Messages<span class="token punctuation">[</span>levelIdx<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> message <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">Format</span><span class="token punctuation">(</span>templates<span class="token punctuation">[</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>templates<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span> rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            broadcaster<span class="token punctuation">.</span><span class="token function">Publish</span><span class="token punctuation">(</span>level<span class="token punctuation">,</span> source<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The previous class is a random event generator, taking a random value from the arrays <code>Levels</code>, <code>Sources</code> and <code>Messages</code>. In addition, through the parameter <code>broadcaster</code>, the new event is published to the bus.</p><h3 id="creating-the-sse-endpoint">Creating the SSE Endpoint</h3><p>Now it&rsquo;s time to create the SSE endpoint, which will consume the shared <code>EventBroadcaster</code>, with the purpose of creating the Event Feed so clients can connect to receive events:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">SseEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">MapSseEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/sse/events"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>EventBroadcaster broadcaster<span class="token punctuation">,</span> CancellationToken ct<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            TypedResults<span class="token punctuation">.</span><span class="token function">ServerSentEvents</span><span class="token punctuation">(</span>broadcaster<span class="token punctuation">.</span><span class="token function">Subscribe</span><span class="token punctuation">(</span>ct<span class="token punctuation">)</span><span class="token punctuation">,</span> eventType<span class="token punctuation">:</span> <span class="token string">"app-event"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above you can notice how <code>EventBroadcaster</code> is injected as a parameter of <code>MapGet</code>. Likewise, <code>TypedResults.ServerSentEvent</code> is executed, which serializes the information and sends the correct SSE format to clients.</p><h3 id="registering-services-in-program.cs">Registering Services in Program.cs</h3><p>For everything to work as expected, we must register in <code>Program.cs</code> the different instances that will interact in the Blazor application. This means creating a singleton instance of <code>EventBroadcaster</code>, so that all systems have access to the same bus. Similarly, we will register <code>DemoEventGenerator</code> as a background service, through the method <code>AddHostedService</code>. This will allow simulated events to be generated in the background continuously:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>EventBroadcaster<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddHostedService<span class="token punctuation">&lt;</span>DemoEventGenerator<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

app<span class="token punctuation">.</span><span class="token function">MapSseEndpoints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the previous code, in addition to registering the services, <code>MapSseEndpoints</code> is invoked to enable SSE communication in the project.</p><h3 id="creating-the-blazor-application">Creating the Blazor Application</h3><p>Once we have the application infrastructure ready, it&rsquo;s time to move on to the UI part. At this point, let&rsquo;s start by creating a JavaScript client, because the standard requires using the browser&rsquo;s <code>EventSource</code> API. Since the project is configured as <code>InteractiveServer</code>, we cannot add inline <code>script</code> tags. To work around this, I will add a new file inside the <code>wwwroot/js</code> folder called <code>sse-demos.js</code>:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token comment">//1.</span>
<span class="token keyword">let</span> eventFeedSource <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

<span class="token number">2</span><span class="token punctuation">.</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">start</span><span class="token punctuation">(</span>dotNetRef<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 3.</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>eventFeedSource<span class="token punctuation">)</span> eventFeedSource<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//4.</span>
    <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EventSource</span><span class="token punctuation">(</span><span class="token string">'/sse/events'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 5.</span>
    src<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'app-event'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnEventReceived'</span><span class="token punctuation">,</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 6.</span>
    src<span class="token punctuation">.</span><span class="token function-variable function">onopen</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnConnectionChanged'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    src<span class="token punctuation">.</span><span class="token function-variable function">onerror</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnConnectionChanged'</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

    eventFeedSource <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//7.</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>eventFeedSource<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        eventFeedSource<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        eventFeedSource <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The previous code does the following:</p><ol><li>A variable <code>eventFeedSource</code> is created to keep information about whether there is an active connection with the server.</li><li>A function named <code>start</code> should be called from C# code when you want to start receiving information&hellip;</li><li>It checks whether there is an open connection, in which case it is closed.</li><li>It tells the browser to connect to <code>/sse/events</code> and to listen for all events that the server sends.</li><li>It filters events named <code>app-event</code>.</li><li>We subscribe to the events <code>onopen</code> and <code>onerror</code>. Each will notify the method <code>OnConnectionChanged</code> about a change in the connection, which will allow showing a different state in the Blazor UI.</li><li>The function <code>stop</code> should be invoked to clean up memory when we want to close the connection.</li></ol><p>With the JS functions ready, the next step is to create the Blazor component. In this new component we will use a <a target="_blank" href="https://www.telerik.com/blazor-ui/grid">Blazor Data Grid</a> type component, because it is a highly configurable component that has built-in options to filter, group, etc., ideal for quickly obtaining information about events in the different systems.</p><p>To do the above, we will create a component called <code>EventFeed.razor</code>, which looks as follows:</p><pre class=" language-xml"><code class="prism  language-xml">@page "/events"
@rendermode InteractiveServer
@inject IJSRuntime JS
@implements IAsyncDisposable

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Live Event Feed<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PageTitle</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Live Event Feed<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header d-flex align-items-center justify-content-between py-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>d-flex align-items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            @if (isStreaming)
            {
                &lt;span class="badge rounded-pill @(isConnected ? "bg-success" : "bg-secondary") fs-6"&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>me-1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>⬤<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>@(isConnected ? "Connected" : "Disconnected")
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
            }
            else
            {
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>badge rounded-pill bg-warning text-dark fs-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>me-1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>⏸<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>Paused
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
            }
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-muted<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                Events received: <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-dark<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@totalEventsReceived<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>d-flex gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ToggleStreaming<span class="token punctuation">"</span></span>
                           <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(isStreaming ? ThemeConstants.Button.ThemeColor.Primary : ThemeConstants.Button.ThemeColor.Success)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                @(isStreaming ? "⏸ Pause" : "▶ Resume")
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ClearGrid<span class="token punctuation">"</span></span> <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Light<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span> Clear<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body p-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikGrid</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@events<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Height</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>560px<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Sortable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Resizable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Reorderable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Groupable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>                     
                     <span class="token attr-name">ShowColumnMenu</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Time)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Time<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>110px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Level)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Level<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>120px<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                        @{
                            var item = (AppEvent)context;
                        }
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>badge rounded-pill @GetBadgeClass(item.Level) px-3 py-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@item.Level<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumn</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Source)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Source<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>140px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Message)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Message<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikGrid</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

@code {
    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span> events = new();
    private bool isConnected;
    private bool isStreaming = true;
    private int totalEventsReceived;
    private DotNetObjectReference<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>EventFeed</span><span class="token punctuation">&gt;</span></span>? dotNetRef;
    private IJSObjectReference? jsModule;
    private bool _jsInitialized;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetRef = DotNetObjectReference.Create(this);
                       
            jsModule = await JS.InvokeAsync<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IJSObjectReference</span><span class="token punctuation">&gt;</span></span>("import", "./js/sse-demos.js");
                        
            await jsModule.InvokeVoidAsync("start", dotNetRef);
            
            _jsInitialized = true;
        }
    }

    [JSInvokable]
    public void OnEventReceived(AppEvent appEvent)
    {
        totalEventsReceived++;
        var updated = new List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span>(events);
        updated.Insert(0, appEvent);
        if (updated.Count &gt; 50)
            updated.RemoveRange(50, updated.Count - 50);
        events = updated;
        InvokeAsync(StateHasChanged);
    }

    [JSInvokable]
    public void OnConnectionChanged(bool connected)
    {
        isConnected = connected;
        InvokeAsync(StateHasChanged);
    }

    private async Task ToggleStreaming()
    {
        isStreaming = !isStreaming;
        
        if (jsModule is not null)
        {
            if (isStreaming)
                await jsModule.InvokeVoidAsync("start", dotNetRef);
            else
                await jsModule.InvokeVoidAsync("stop");
        }
    }

    private void ClearGrid()
    {
        events = new List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span>();
        StateHasChanged();
    }

    private string GetBadgeClass(string level) =&gt; level switch
    {
        "Info" =&gt; "bg-info text-dark",
        "Warning" =&gt; "bg-warning text-dark",
        "Error" =&gt; "bg-danger",
        "Success" =&gt; "bg-success",
        _ =&gt; "bg-secondary"
    };

    public async ValueTask DisposeAsync()
    {
        if (_jsInitialized &amp;&amp; jsModule is not null)
        {
            try
            {                
                await jsModule.InvokeVoidAsync("stop");
                                
                await jsModule.DisposeAsync();
            }
            catch
            {                
            }
        }
        dotNetRef?.Dispose();
    }

    public class AppEvent
    {
        public int Id { get; set; }
        public string Time { get; set; } = string.Empty;
        public string Level { get; set; } = string.Empty;
        public string Source { get; set; } = string.Empty;
        public string Message { get; set; } = string.Empty;
    }
}
</code></pre><p>In the previous code, there are some points to highlight:</p><ul><li><code>jsModule</code> dynamically loads the JS module.</li><li>The variable <code>dotNetRef</code> wraps the instance of the Blazor component that we will use inside the JS code.</li><li>The method <code>InvokeVoidAsync</code> is used both to start and to stop the event streaming.</li><li>The method <code>OnEventReceived</code> is invoked from the JS code each time an event is received. This allows updating the list <code>events</code> to show the new information in <code>TelerikGrid</code>.</li><li>The <code>OnConnectionChanged</code> method receives a connection status from the JS code to update the UI according to any change in the connection.</li></ul><p>With the new component ready, we can test the application, which looks like the following:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/telerik-datagrid-sse-event-feed.gif?sfvrsn=58cc913a_2" alt="Telerik DataGrid receiving live SSE event feed" /></p><p>With this, we verify that everything works correctly. Also, thanks to the Blazor DataGrid capabilities, we can perform operations such as monitoring only those high-severity events:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/timeline-grouped-events-view.png?sfvrsn=5ae1f10_2" alt="Timeline view of events grouped by day" /></p><p>With this we have a nice event viewer that monitors the status of multiple fictitious systems.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article you have learned about the SSE standard. You have also seen how changes introduced in .NET 10 help simplify the creation of SSE endpoints, enabling the creation of applications that send real-time events to clients. Now it&rsquo;s your turn to explore when you might use this web standard in your own projects. See you in the next article!</p><hr /><p>Try all this yourself with a free 30-day trial of Telerik UI for Blazor.</p><p><a href="https://www.telerik.com/try/ui-for-blazor" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23052/17339195.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:bb2fa99e-0c29-407f-9eb9-4d2b43a9632a</id>
    <title type="text">Creating More Realistic Tests with In-Memory Databases in ASP.NET Core</title>
    <summary type="text">Testing ASP.NET Core APIs with in-memory SQLite and JustMock enables validation of real-world scenarios like pagination, keys and rules without a real database.</summary>
    <published>2026-05-06T17:20:57Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17335630/creating-more-realistic-tests-memory-databases-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Testing ASP.NET Core APIs with in-memory SQLite and JustMock enables validation of real-world scenarios like pagination, keys and rules without a real database.</span></p><p>Testing a web API involves much more than simply checking whether methods return or send data correctly. It requires validating real-world behaviors like pagination, unique keys, data persistence and other database rules. Anticipating and reproducing these scenarios can be challenging, especially when they depend on specific query behaviors or stored data.</p><p>To address this, the .NET ecosystem provides powerful tools for unit testing. In this post, we explore how in-memory SQLite serves as an effective solution for testing ASP.NET Core APIs, enabling developers to simulate common scenarios without deploying a real database. Additionally, we demonstrate how tools like Progress Telerik JustMock can streamline test implementation and help validate business rules efficiently.</p><h2 id="the-importance-of-testing-in-real-world-environments">The Importance of Testing in Real-World Environments</h2><p>A common example of functionality in web APIs is paginated search, which at first glance is quite simple. We receive parameters such as <code>page</code> and <code>pageSize</code>, apply a Skip and a Take, return the data and move on. In a controlled scenario, with few records, it&rsquo;s difficult for something to go wrong.</p><p>The problem begins when this code reaches production or even test environments, without having been validated with a real database. Consider a common scenario: in a paginated search, sorting is essential for it to function correctly. Without an explicit <code>OrderBy</code>, the database does not guarantee the order of the returned records.</p><p>In in-memory lists, the result usually appears stable, which completely masks the problem. Without tests running this query on a relational database, the error goes unnoticed, resulting in inconsistent pagination with duplicate records between pages, items &ldquo;disappearing&rdquo; when navigating between pages or different results for the same request. This type of bug is especially difficult to reproduce locally when not using a real database or even a simulation of one.</p><p>This is where the in-memory database plays a very important role. It allows the query to be executed exactly as it will be executed in production, revealing flaws that only appear when pagination, sorting and the database work together.</p><h2 id="understanding-in-memory-sqlite">Understanding In-Memory SQLite</h2><p>In-memory SQLite is a way to use the SQLite database entirely in memory. Instead of saving data to a <code>.db</code> file, SQLite creates the database only while the application or connection is active. When the connection is closed, all data is discarded.</p><p>In-memory SQLite is widely used in automated testing, especially in ASP.NET Core applications in conjunction with Entity Framework Core, as it has advanced features such as simulating a real database and executing real SQL (constraints, indexes, unique keys, FK and others).</p><h2 id="testing-with-sqlite-in-memory">Testing with SQLite in Memory</h2><p>Next, we&rsquo;ll look at some examples of unit tests in common web API scenarios where in-memory SQLite excels. But first, let&rsquo;s create the basic application and then add the tests.</p><p>You can access all the code discussed in this post in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/memo-order">Memo Order source code</a>.</p><p>To create the application you can use the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o MemoOrder
</code></pre><p>To add the NuGet packages you can use the following commands:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Microsoft.EntityFrameworkCore --version 10.0.1
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 10.0.1
</code></pre><p>Then, open the application and create the following classes:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> MemoOrder<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Order</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Number <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> TotalAmount <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateTime CreatedAt <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsDeleted <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> MemoOrder<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderSummaryDto</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Number <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Total <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> MemoOrder<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AppDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">AppDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">)</span>
        <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Order<span class="token operator">&gt;</span> Orders <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Order<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnModelCreating</span><span class="token punctuation">(</span>ModelBuilder modelBuilder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        modelBuilder<span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>Order<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>entity <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            entity<span class="token punctuation">.</span><span class="token function">HasKey</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>
            entity<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Number<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            entity<span class="token punctuation">.</span><span class="token function">HasIndex</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Number<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsUnique</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
entity<span class="token punctuation">.</span><span class="token function">HasQueryFilter</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token operator">!</span>o<span class="token punctuation">.</span>IsDeleted<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> MemoOrder<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderRepository</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> AppDbContext _context<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">OrderRepository</span><span class="token punctuation">(</span>AppDbContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _context <span class="token operator">=</span> context<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">AddAsync</span><span class="token punctuation">(</span>Order order<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> _context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Order<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">Pagination</span><span class="token punctuation">(</span>AppDbContext context<span class="token punctuation">,</span> <span class="token keyword">int</span> skip<span class="token punctuation">,</span> <span class="token keyword">int</span> take<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders
                    <span class="token punctuation">.</span><span class="token function">OrderBy</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">Skip</span><span class="token punctuation">(</span>skip<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>take<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Now let&rsquo;s create a special factory class that will be used in the tests to create the in-memory database and keep the connection open while the tests are running. Create the following class in the application:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> MemoOrder<span class="token punctuation">;</span>

<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>Data<span class="token punctuation">.</span>Sqlite<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">SqliteInMemoryFactory</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> AppDbContext <span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> connection <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SqliteConnection</span><span class="token punctuation">(</span><span class="token string">"DataSource=:memory:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        connection<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> options <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DbContextOptionsBuilder</span><span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">UseSqlite</span><span class="token punctuation">(</span>connection<span class="token punctuation">)</span>
            <span class="token punctuation">.</span>Options<span class="token punctuation">;</span>

        <span class="token keyword">var</span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AppDbContext</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>
        context<span class="token punctuation">.</span>Database<span class="token punctuation">.</span><span class="token function">EnsureCreated</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> context<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that to tell SQLite to use an in-memory database, we use <code>DataSource=:memory:</code> in the connection string.</p><h3 id="creating-the-test-project">Creating the Test Project</h3><p>All the code needed to implement the unit tests is ready, so let&rsquo;s create the test project and download the NuGet packages for it. To do this, you can use the following commands, simply run them in a terminal in the application&rsquo;s root directory:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new xunit -n MemoOrder.Tests
<span class="token function">cd</span> MemoOrder.Tests
dotnet add package Microsoft.NET.Test.Sdk --version 18.0.1
dotnet add package xunit --version 2.9.3
dotnet add package xunit.runner.visualstudio --version 3.1.5
dotnet add package coverlet.collector --version 6.0.4
dotnet add reference <span class="token punctuation">..</span>/MemoOrder/MemoOrder.csproj
</code></pre><p>Next, inside the project MemoOrder.Tests, create a new class called OrderRepositoryTests and let&rsquo;s add tests to it.</p><h3 id="testing-persistence">Testing Persistence</h3><p>The first test will be used to validate persistence&mdash;that is, to insert data into the database (which in this case will be the in-memory database). To do this, simply add the following test method to the <code>OrderRepositoryTests</code> class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">AddAsync_ShouldPersistOrder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// Arrange</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> repository <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderRepository</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span>
    <span class="token punctuation">{</span>
        Number <span class="token operator">=</span> <span class="token string">"ORD-2026-0001"</span><span class="token punctuation">,</span>
        TotalAmount <span class="token operator">=</span> 200m<span class="token punctuation">,</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    <span class="token comment">// Act</span>
    <span class="token keyword">await</span> repository<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Assert</span>
    <span class="token keyword">var</span> savedOrder <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">FirstAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"ORD-2026-0001"</span><span class="token punctuation">,</span> savedOrder<span class="token punctuation">.</span>Number<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span>200m<span class="token punctuation">,</span> savedOrder<span class="token punctuation">.</span>TotalAmount<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that we use <code>var context = SqliteInMemoryFactory.Create();</code> to create and represent the database in memory. It works exactly like the EF Core context that maps database tables to class entities.</p><h3 id="testing-constraint-unique">Testing Constraint UNIQUE</h3><p>Another common mistake is allowing duplicate records to reach the production environment, which in many cases can have catastrophic impacts. With in-memory testing, we can anticipate problems like this. Simply create a test that verifies inserting a duplicate record will generate an exception. In this case, we could do the following:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Should_Throw_When_Duplicated_Order_Number</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Order</span>
    <span class="token punctuation">{</span>
        Number <span class="token operator">=</span> <span class="token string">"ORD-001"</span><span class="token punctuation">,</span>
        TotalAmount <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Order</span>
    <span class="token punctuation">{</span>
        Number <span class="token operator">=</span> <span class="token string">"ORD-001"</span><span class="token punctuation">,</span>
        TotalAmount <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">,</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> Assert<span class="token punctuation">.</span><span class="token generic-method function">ThrowsAsync<span class="token punctuation">&lt;</span>DbUpdateException<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In this test, we created an instance of the database in memory, added an item to the table and saved it.</p><p>Then, we tried to add the same item, which generates an exception because, in the dbContext configuration, we declared the Number property of the Order entity as Unique: <code>entity.HasIndex(o =&gt; o.Number).IsUnique();</code>.</p><p>Thus, we have a real-world scenario, demonstrating that duplicate records will not reach the production environment, as the unit test is able to reproduce exactly the same result.</p><h3 id="testing-pagination">Testing Pagination</h3><p>To test pagination using an in-memory database, we could do the following:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Should_Return_Paginated_Orders</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> repository <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderRepository</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;=</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Order</span>
        <span class="token punctuation">{</span>
            Number <span class="token operator">=</span> $<span class="token string">"ORD-{i}"</span><span class="token punctuation">,</span>
            TotalAmount <span class="token operator">=</span> i <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">await</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    List<span class="token operator">&lt;</span>Order<span class="token operator">&gt;</span> page <span class="token operator">=</span> <span class="token keyword">await</span> repository<span class="token punctuation">.</span><span class="token function">Pagination</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span>Count<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"ORD-6"</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span><span class="token function">First</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Number<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that here we create 10 items and insert them into the in-memory database. Then, we use the <code>Pagination(context, 5, 5)</code> method to perform the pagination and finally verify if the expected quantity and item are correct.</p><p>An in-memory database wouldn&rsquo;t be mandatory to test pagination. This could be done using only regular in-memory lists. However, in this way, we can reproduce the same production scenario.</p><h3 id="testing-soft-delete">Testing Soft Delete</h3><p>Soft-delete is a technique where data is not physically removed from the database, but rather marked as inactive through a flag, such as an <code>isDeleted</code> or <code>deleted_at</code> column. This allows you to keep the entire transaction history in the database.</p><p>The problem is that in some cases soft delete can leak unwanted data into reports, new endpoints or even refactored queries. To prevent this from happening, we can use the in-memory database and simulate a search with and without soft-delete:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
 <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">SoftDeleted_Orders_Should_Not_Appear_In_Default_Queries</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
 <span class="token punctuation">{</span>
     <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

     context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">AddRange</span><span class="token punctuation">(</span>
         <span class="token keyword">new</span> <span class="token class-name">Order</span>
         <span class="token punctuation">{</span>
             Number <span class="token operator">=</span> <span class="token string">"ORD-001"</span><span class="token punctuation">,</span>
             TotalAmount <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
             CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
             IsDeleted <span class="token operator">=</span> <span class="token keyword">false</span>
         <span class="token punctuation">}</span><span class="token punctuation">,</span>
         <span class="token keyword">new</span> <span class="token class-name">Order</span>
         <span class="token punctuation">{</span>
             Number <span class="token operator">=</span> <span class="token string">"ORD-002"</span><span class="token punctuation">,</span>
             TotalAmount <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">,</span>
             CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
             IsDeleted <span class="token operator">=</span> <span class="token keyword">true</span>
         <span class="token punctuation">}</span>
     <span class="token punctuation">)</span><span class="token punctuation">;</span>

     <span class="token keyword">await</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

     <span class="token keyword">var</span> orders <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

     Assert<span class="token punctuation">.</span><span class="token function">Single</span><span class="token punctuation">(</span>orders<span class="token punctuation">)</span><span class="token punctuation">;</span>
     Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"ORD-001"</span><span class="token punctuation">,</span> orders<span class="token punctuation">.</span><span class="token function">First</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Number<span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span>
</code></pre><p>In this test, the list returns only one item because the second one is marked as deleted. This happens because we declared the query filter <code>entity.HasQueryFilter(o =&gt; !o.IsDeleted);</code> when the database is created. By using an in-memory database, we can verify this mechanism works.</p><p>It&rsquo;s also possible to ignore the query filter and still verify it works:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Admin_Query_Should_See_SoftDeleted_Orders</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">AddRange</span><span class="token punctuation">(</span>
        <span class="token keyword">new</span> <span class="token class-name">Order</span>
        <span class="token punctuation">{</span>
            Number <span class="token operator">=</span> <span class="token string">"ORD-001"</span><span class="token punctuation">,</span>
            TotalAmount <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
            IsDeleted <span class="token operator">=</span> <span class="token keyword">false</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token keyword">new</span> <span class="token class-name">Order</span>
        <span class="token punctuation">{</span>
            Number <span class="token operator">=</span> <span class="token string">"ORD-002"</span><span class="token punctuation">,</span>
            TotalAmount <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
            IsDeleted <span class="token operator">=</span> <span class="token keyword">true</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> orders <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">IgnoreQueryFilters</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> orders<span class="token punctuation">.</span>Count<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="testing-rollback">Testing Rollback</h3><p>When performing multiple operations, we run the risk of something failing during the process and the data becoming inconsistent. To keep this from happening, we can use the in-memory database to test if the rollback operation solves this problem.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Transaction_Should_Rollback_When_Error_Occurs</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">using</span> <span class="token keyword">var</span> transaction <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Database<span class="token punctuation">.</span><span class="token function">BeginTransactionAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Order</span>
    <span class="token punctuation">{</span>
        Number <span class="token operator">=</span> <span class="token string">"ORD-NMBR-1"</span><span class="token punctuation">,</span>
        TotalAmount <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">,</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> transaction<span class="token punctuation">.</span><span class="token function">RollbackAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> count <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">CountAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> count<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In this test, we added an item to the order list, saved it and then performed a rollback. Finally, we verified that the list was empty, checking that the operation was undone.</p><h3 id="testing-query-refactoring">Testing Query Refactoring</h3><p>Refactoring queries is often necessary, whether to improve performance or simply to make the code cleaner. The problem is that this can break queries that previously worked.</p><p>To see that both the old and new queries have the same result, we can use an in-memory database as demonstrated in the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Refactored_Query_Should_Return_Same_Result</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> oldResult <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>TotalAmount <span class="token operator">&gt;</span> <span class="token number">100</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> newResult <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> o<span class="token punctuation">.</span>TotalAmount <span class="token punctuation">}</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>TotalAmount <span class="token operator">&gt;</span> <span class="token number">100</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Assert<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span>oldResult<span class="token punctuation">,</span> newResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>In this example, we verify that both queries have the same result, so even after refactoring, the old logic will continue to work.</p><h3 id="query-test-with-projection-to-dto">Query Test with Projection to DTO</h3><p>When using Data Transfer Objects (DTOs) for data transport, we are subject to errors that can silently break our queries. Again, with an in-memory database, we can simulate a query using a DTO.</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">Projection_To_Dto_Should_Work</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">using</span> <span class="token keyword">var</span> context <span class="token operator">=</span> SqliteInMemoryFactory<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token keyword">await</span> context<span class="token punctuation">.</span>Orders
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token class-name">OrderSummaryDto</span>
            <span class="token punctuation">{</span>
                Number <span class="token operator">=</span> o<span class="token punctuation">.</span>Number<span class="token punctuation">,</span>
                Total <span class="token operator">=</span> o<span class="token punctuation">.</span>TotalAmount
            <span class="token punctuation">}</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Assert<span class="token punctuation">.</span><span class="token function">NotNull</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>Note that through this test, we can be sure that our DTOs will function correctly.</p><h2 id="not-all-tests-need-a-database">Not All Tests Need a Database</h2><p>Until now, we&rsquo;ve used in-memory SQLite to validate constraints (UNIQUE), global filters (Soft Delete), real pagination, GroupBy, Sum, transactions and rollback.</p><p>Tests like these depend on database behavior, but it&rsquo;s common to encounter scenarios where the database isn&rsquo;t present, and only business rules are involved. In this case, to facilitate our work and validate our tests, we can use tools like <a target="_blank" href="https://www.telerik.com/products/mocking.aspx">JustMock</a> and simulate a production-facing environment.</p><p>Next, we&rsquo;ll look at some examples of business rule tests where we can use JustMock to easily create mocks for our tests.</p><h3 id="verifying-the-repository-will-be-called">Verifying the Repository Will Be Called</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">CreateAsync_Should_Call_Repository_AddAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// Arrange</span>
    <span class="token keyword">var</span> repository <span class="token operator">=</span> Mock<span class="token punctuation">.</span><span class="token generic-method function">Create<span class="token punctuation">&lt;</span>IOrderRepository<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span>
    <span class="token punctuation">{</span>
        Id <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
        Number <span class="token operator">=</span> <span class="token string">"ORD-001"</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    Mock<span class="token punctuation">.</span><span class="token function">Arrange</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> repository<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Returns</span><span class="token punctuation">(</span>Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">MustBeCalled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderService</span><span class="token punctuation">(</span>repository<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Act</span>
    <span class="token keyword">await</span> service<span class="token punctuation">.</span><span class="token function">CreateAsync</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Assert</span>
    Mock<span class="token punctuation">.</span><span class="token function">Assert</span><span class="token punctuation">(</span>repository<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="verifying-the-gethighvalueorders-method-returns-only-orders-above-the-minimum-value">Verifying the GetHighValueOrders Method Returns Only Orders Above the Minimum Value</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">GetHighValueOrders_Should_Return_Only_Orders_Above_MinValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// Arrange</span>
    <span class="token keyword">var</span> repository <span class="token operator">=</span> Mock<span class="token punctuation">.</span><span class="token generic-method function">Create<span class="token punctuation">&lt;</span>IOrderRepository<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> orders <span class="token operator">=</span> <span class="token function">FakeOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>TotalAmount <span class="token operator">&gt;=</span> <span class="token number">150</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>o<span class="token punctuation">.</span>IsDeleted<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Mock<span class="token punctuation">.</span><span class="token function">Arrange</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> repository<span class="token punctuation">.</span><span class="token function">GetHighValueOrders</span><span class="token punctuation">(</span><span class="token number">150</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Returns</span><span class="token punctuation">(</span>orders<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderService</span><span class="token punctuation">(</span>repository<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Act</span>
    <span class="token keyword">var</span> result <span class="token operator">=</span> service<span class="token punctuation">.</span><span class="token function">GetHighValueOrders</span><span class="token punctuation">(</span><span class="token number">150</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Assert</span>
    Assert<span class="token punctuation">.</span><span class="token function">AreEqual</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> result<span class="token punctuation">.</span>Count<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Assert<span class="token punctuation">.</span><span class="token function">IsTrue</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">First</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>TotalAmount <span class="token operator">&gt;=</span> <span class="token number">150</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="verifying-the-service-forwards-exactly-the-filter-received">Verifying the Service Forwards Exactly the Filter Received</h3><pre class=" language-csharp"><code class="prism  language-csharp">       <span class="token punctuation">[</span>Fact<span class="token punctuation">]</span>
        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">GetHighValueOrders_Should_Call_Repository_With_Same_MinValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token comment">// Arrange</span>
            <span class="token keyword">var</span> repository <span class="token operator">=</span> Mock<span class="token punctuation">.</span><span class="token generic-method function">Create<span class="token punctuation">&lt;</span>IOrderRepository<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            Mock<span class="token punctuation">.</span><span class="token function">Arrange</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> repository<span class="token punctuation">.</span><span class="token function">GetHighValueOrders</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">Returns</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>Order<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">MustBeCalled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderService</span><span class="token punctuation">(</span>repository<span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Act</span>
            service<span class="token punctuation">.</span><span class="token function">GetHighValueOrders</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Assert</span>
            Mock<span class="token punctuation">.</span><span class="token function">Assert</span><span class="token punctuation">(</span>repository<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
</code></pre><p>If you run all the tests, you can verify that they all passed:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/all-tests-passed.png?sfvrsn=60e45a83_2" title="all tests passed" alt="All tests passed" /></p><h2 id="conclusion">Conclusion</h2><p>Testing web applications is common in software development, but these tests don&rsquo;t always guarantee that errors won&rsquo;t occur in the production environment. Using in-memory databases, such as in-memory SQLite, allows you to reproduce scenarios closer to reality.</p><p>In this post, we saw how this approach helps identify common errors, such as persistence, pagination and projection issues. Furthermore, we explored how JustMock can help save time when validating business rules. I hope the content covered here helps you further improve your applications, making them more reliable through even more realistic testing.</p><hr /><p><a href="https://www.telerik.com/try/justmock" target="_blank">Try JustMock</a> free for 30 days, or <a href="https://www.telerik.com/try/devcraft-ultimate" target="_blank">try out the whole Telerik DevCraft</a> suite for access to the ASP.NET Core component library and lots more (also a free 30-day trial).</p><img src="https://feeds.telerik.com/link/23052/17335630.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:88e4a370-d2a2-4d78-ae1f-86792f7c61e1</id>
    <title type="text">Loading, Accessing and Converting Office and PDF Documents with Telerik Document Processing Libraries</title>
    <summary type="text">Here’s what you need to get started with Telerik Document Processing Libraries to work with PDF, Word and Excel files (and, like any good suite, make all those document types look very much alike).</summary>
    <published>2026-03-31T15:30:52Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17310580/loading-accessing-converting-office-pdf-documents-telerik-document-processing-libraries"/>
    <content type="text"><![CDATA[<p><span class="featured">Here&rsquo;s what you need to get started with Telerik Document Processing Libraries to work with PDF, Word and Excel files (and, like any good suite, make all those document types look very much alike).</span></p><p>Progress Telerik Document Processing Libraries, in addition to letting you work with a variety of document formats (PDF, DOCX, RTF, HTML, XLSX and more), are an example of the reason you buy into a suite of tools: All the tools bear a &ldquo;family resemblance.&rdquo;</p><p>The ideal scenario, of course, would be for a single tool that made all these document formats look the same. Given the differences in format and functionality between, for example, a PDF, a Microsoft Word (or RTF or HTML) document and an Excel spreadsheet, that&rsquo;s not reasonable (though Progress Telerik has achieved that with DOCX, RTF and HTML documents).</p><p>The good news here is that, with Telerik Document Processing Libraries (DPL), the family resemblance is strong enough that, for <em>all</em> of the document types the library supports, I can show you how to load documents, start the editing process, convert between various document types and save a document in this one post.</p><h2 id="configuring-your-project">Configuring Your Project</h2><p>The sample code in this post was all written using the DPL for Windows libraries (even though I was working in ASP.NET Core&mdash;the more technical name for the version I used is &ldquo;.NET(Target OS: Windows).&rdquo; The suite is <a target="_blank" href="https://docs.telerik.com/devtools/document-processing/introduction#required-references">also available for the .NET Framework</a>.</p><p>To create an ASP.NET Core project that would work with &ldquo;all the documents,&rdquo; I added these NuGet packages to my project:</p><ul><li>To work with PDF files: Telerik.Windows.Documents.Fixed</li><li>To work with DOCX, HTML and RTF files: Telerik.Windows.Documents.Flow</li><li>To work with Excel spreadsheets: Telerik.Windows.Documents.Spreadsheet</li></ul><p>For the Excel spreadsheets, I&rsquo;m only going to work with XLSX files, so I added the Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml package to my project. (If I was going to work with, for example, XLS spreadsheet, then I would have added the Telerik.Documents.Spreadsheet.FormatProviders.Xls package.)</p><h2 id="loading-your-document">Loading Your Document</h2><p>The code to load a document from a file into any of these libraries is very similar:</p><ol><li>Create the appropriate provider.</li><li>Use the .NET<code>File</code> object&rsquo;s <code>OpenRead</code> to create a <code>Stream</code> that points to the file.</li><li>Use the provider&rsquo;s <code>Import</code> method to load the stream into the document object.</li></ol><p>Here, for example, is the code to load a PDF file into a <code>RadFixedDocument</code> object (I used this code in an ASP.NET Core application with documents in my project&rsquo;s wwwroot folder):</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFixedDocument doc<span class="token punctuation">;</span>
PdfFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">OpenRead</span><span class="token punctuation">(</span><span class="token string">@"wwwroot/documents/Priorities.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    doc <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Import</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>And here&rsquo;s the code to load a DOCX file into a <code>RadFlowDocument</code>:</p><pre class=" language-c"><code class="prism # language-c">RadFlowDocument doc<span class="token punctuation">;</span>
DocxFormatProvider prov <span class="token operator">=</span> <span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">OpenRead</span><span class="token punctuation">(</span>@<span class="token string">"wwwroot/documents/Priorities.docx"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    doc <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Import</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>As you can see, the code is identical except for the provider (<code>PdfFormatProvider</code> vs. <code>DocxFormatProvider</code>) and document objects (<code>RadFlowDocument</code> vs. <code>RadFixedDocument</code>).</p><p>Because both RTF and HTML documents load into the same <code>RadFlowDocument</code> object as a DOCX document, only the provider object changes (<code>HtmlFormatProvider</code> or <code>RtfFormatProdiver</code> instead of <code>DocxFormatProvider</code>) when working with those file formats. Here&rsquo;s the code to load an RTF document:</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFlowDocument doc<span class="token punctuation">;</span>
RtfFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">OpenRead</span><span class="token punctuation">(</span><span class="token string">@"wwwroot/documents/Priorities.rtf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    doc <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Import</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>And here&rsquo;s the almost identical code to load an HTML file:</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFlowDocument doc<span class="token punctuation">;</span>
HtmlFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">OpenRead</span><span class="token punctuation">(</span><span class="token string">@"wwwroot/documents/Priorities.html"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    doc <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Import</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The code to load an Excel workbook is also similar to what you&rsquo;ve seen before, just swapping in a new document object (<code>Workbook</code>) and provider (<code>XlsxFormatProvider</code>):</p><pre class=" language-csharp"><code class="prism  language-csharp">Workbook doc<span class="token punctuation">;</span>
XlsxFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">OpenRead</span><span class="token punctuation">(</span><span class="token string">@"wwwroot/documents/priority.xlsx"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    doc <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Import</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>A note: The Workbook object assumes that you&rsquo;re going to load the <em>whole</em> Excel workbook into memory. For very large workbooks, that may not make sense. For that scenario, you should look at the <a target="_blank" href="https://docs.telerik.com/devtools/document-processing/libraries/radspreadstreamprocessing/overview">SpreadStreamProcessing Library</a>.</p><h2 id="modifying-the-documents">Modifying the Documents</h2><p>Once you&rsquo;ve loaded the documents, you can start working with them. You can often simplify your code by using the <code>RadFixedDocumentEditor</code> with PDF documents or the <code>RadFlowDocumentEditor</code>with Word/RTF/HTML documents. Not surprisingly, the code for creating an editor is almost identical for these two document types: create an editor object and pass the document you want loaded into the editor.</p><p>The code to create an editor for a PDF document looks like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFixedDocumentEditor editor <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RadFixedDocumentEditor</span><span class="token punctuation">(</span>doc<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The code for Word/RTF/HTML documents looks like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFlowDocumentEditor editor <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RadFlowDocumentEditor</span><span class="token punctuation">(</span>doc<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>That&rsquo;s not to say that, as you start working with those documents, there aren&rsquo;t going to be differences. These are, after all, very different kinds of documents. Having said that, some functionality does work in a similar way across all the document types.</p><p>If, for example, I want to search a PDF document for the text &ldquo;ASP.NET,&rdquo; I create a <code>TextSearch</code> instance from my document object. I then use the <code>TextSearch</code> object&rsquo;s <code>FindAll</code> method to search for text in my PDF document, passing two things: my search text and a <code>TextSearchOptions</code> object that specifies how I want my search conducted. That <code>FindAll</code> object returns a collection of <code>SearchResult</code> objects that I can loop through.</p><p>Typical code, then, looks like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">TextSearch search <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TextSearch</span><span class="token punctuation">(</span>doc<span class="token punctuation">)</span><span class="token punctuation">;</span>
TextSearchOptions opts <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
CaseSensitive <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">,</span>
WholeWordsOnly <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
UseRegularExpression <span class="token operator">=</span> <span class="token keyword">true</span>
                                                              <span class="token punctuation">}</span><span class="token punctuation">;</span>

IEnumerable<span class="token operator">&lt;</span>SearchResult<span class="token operator">&gt;</span> items <span class="token operator">=</span> search<span class="token punctuation">.</span><span class="token function">FindAll</span><span class="token punctuation">(</span><span class="token string">"ASP.NET"</span><span class="token punctuation">,</span> opts<span class="token punctuation">)</span><span class="token punctuation">;</span>

Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found {items.Count()} items."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">foreach</span> <span class="token punctuation">(</span>SearchResult item <span class="token keyword">in</span> items<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found at {item.Range.StartPosition} "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found: {item.Result}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The process is almost identical for the <code>RadFlowDocument</code> object, except:</p><ul><li>You call the <code>FindAll</code>method directly from the <code>RadFlowDocumentEditor</code>.</li><li>There isn&rsquo;t a separate options object (though all the search options from the <code>TextSearch</code> object are still available).</li><li>The <code>FindAll</code> method on the editor returns <code>FindResult</code> objects instead of <code>SearchResult</code> objects.</li></ul><p>As a result, the equivalent search code for a DOCX, RTF or HTML document looks like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">RadFlowDocumentEditor editor <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RadFlowDocumentEditor</span><span class="token punctuation">(</span>doc<span class="token punctuation">)</span><span class="token punctuation">;</span>

IEnumerable<span class="token operator">&lt;</span>FindResult<span class="token operator">&gt;</span> items <span class="token operator">=</span> editor<span class="token punctuation">.</span><span class="token function">FindAll</span><span class="token punctuation">(</span><span class="token string">"ASP.NET"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found {items.Count()} items."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">foreach</span> <span class="token punctuation">(</span>FindResult item <span class="token keyword">in</span> items<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found at {item.RelativeStartIndex} "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found: {item.FullMatchText}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Searching a spreadsheet works similarly. The differences:</p><ul><li>The <code>FindAll</code> method is built right into the <code>Workbook</code> object.</li><li>You have more search options available with the spreadsheet object.</li><li>You pass your search string as part of the options object.</li></ul><p>My find code with a workbook would look like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">FindOptions opts <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
FindWhat <span class="token operator">=</span> <span class="token string">"ASP.NET"</span><span class="token punctuation">,</span>
MatchCase <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">,</span>
MatchEntireCellContents <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
                                                <span class="token punctuation">}</span><span class="token punctuation">;</span>

IEnumerable<span class="token operator">&lt;</span>FindResult<span class="token operator">&gt;</span> items <span class="token operator">=</span> doc<span class="token punctuation">.</span><span class="token function">FindAll</span><span class="token punctuation">(</span>opts<span class="token punctuation">)</span><span class="token punctuation">;</span>

Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found {items.Count()} items."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">foreach</span><span class="token punctuation">(</span>FindResult item <span class="token keyword">in</span> items<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found at {item.FoundCell.CellIndex} "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Debug<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>$<span class="token string">"Found: {item.ResultValue}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>One note: It is certainly convenient when objects from the different libraries share the same name (like the <code>FindResult</code> object that&rsquo;s defined in both the <code>RadFlowDocument</code> and <code>Workbook</code> libraries). However, if you try to use both <code>FindResult</code> objects in the same code file, the compiler will get confused because the two objects are in different namespaces. In the unlikely case that you&rsquo;re working with both Excel and Word documents in the same code file, you&rsquo;ll have to fully qualify the object names&mdash;something I haven&rsquo;t done in this post.</p><h2 id="saving-your-documents">Saving Your Documents</h2><p>To save your modified documents back to disk, you just need to use the provider&rsquo;s <code>Export</code> method. The code for all the document types is identical:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token string">@"wwwroot/documents/Prioritiesnew.&lt;filetype&gt;"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    prov<span class="token punctuation">.</span><span class="token function">Export</span><span class="token punctuation">(</span>doc<span class="token punctuation">,</span>str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="converting-documents">Converting Documents</h2><p>You can convert from one type in the suite to other types and, not surprisingly, the conversion processes look very much alike. As you&rsquo;ve seen before, it often comes down to using the right provider.</p><p>If, for example, you want plain text version of your PDF file, you use the <code>TextFormatProvider</code> object&rsquo;s <code>Export</code> method:</p><pre class=" language-csharp"><code class="prism  language-csharp">TextFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">string</span> text <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Export</span><span class="token punctuation">(</span>doc<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>For Word/HTML/RTF document types, the code is almost identical except it uses the <code>TxtFormatProvider</code> object:</p><pre class=" language-csharp"><code class="prism  language-csharp">TxtFormatProvider txProv <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">string</span> text <span class="token operator">=</span> prov<span class="token punctuation">.</span><span class="token function">Export</span><span class="token punctuation">(</span>doc<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            
</code></pre><p>One note: There is a downside to having the classes that do similar things to different document types have the same name. If you are mixing document types and, as a result, using the Fixed, Flow and spreadsheet libraries in the same application, the compiler can get confused about which class from which library you&rsquo;re using. If so, you&rsquo;ll have to <a target="_blank" href="https://docs.telerik.com/devtools/document-processing/knowledge-base/cs0104-error-pdf-format-provider">fully qualify your class names</a> by including their namespaces in the class names. That makes for hard-to-read code, so I haven&rsquo;t done that here.</p><p>But, as an example of how different documents require different functionality, you probably wouldn&rsquo;t ever want to convert an Excel workbook to a string &hellip; but you might want to save your workbook as a CSV file. As you might expect by now, the code to save your imported workbook into a CSV file just means using the <code>Export</code> method on the appropriate provider&mdash;the <code>CsvFormatProvider</code> object in this case.</p><p>Typical code would look like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">CsvFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">using</span> <span class="token punctuation">(</span>Stream str <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token string">"Priority.csv"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    prov<span class="token punctuation">.</span><span class="token function">Export</span><span class="token punctuation">(</span>doc<span class="token punctuation">,</span> str<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p><a target="_blank" href="https://www.telerik.com/blogs/quick-and-easy-conversion-to-pdf-with-telerik-document-processing-library">Converting any of these document types (Excel, Word, HTML, etc.) to PDF</a> is equally straightforward. Because all the libraries look very much alike, it&rsquo;s really just a matter of adding the library with the provider you need.</p><p>But you can also convert your <code>Workbook</code> object into a <code>RadFixedDocument</code> if you wanted to manipulate your spreadsheet as a PDF object. That conversion is handled by the PdfFormatProvider from the Telerik.Windows.Documents.Spreadsheet.Formatproviders.Pdf package and using its <code>ExportToFixedDocument</code>method:</p><pre class=" language-csharp"><code class="prism  language-csharp">PdfFormatProvider prov <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

RadFixedDocument fixedDoc <span class="token operator">=</span> 
        prov<span class="token punctuation">.</span><span class="token function">ExportToFixedDocument</span><span class="token punctuation">(</span>doc<span class="token punctuation">,</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The code is identical if you want to convert a Word/RTF/HTML document to a <code>RadFixedDocument</code> to work with it as a PDF file. That conversion also uses a <code>PdfFormatProvider</code> object but, this time, from the Telerik.Windows.Documents.Flow.FormatProviders.Pdf namespace.</p><p>But, because the providers from the two libraries have the same name, if you&rsquo;re using both libraries in the same code file, you will need to fully qualify your provider names to make sure you&rsquo;re getting the appropriate <code>PdfFormatProvider</code>.</p><p>Of course, once you start using these tools to create or modify the documents you&rsquo;ve loaded, you&rsquo;ll find more differences&mdash;the functionality in a spreadsheet is very different from the functionality in an HTML document. But, while the family resemblances among this suite won&rsquo;t eliminate those differences, it does cut those differences down to what matters: how those documents differ in their functionality. Which is, after all, what you want.</p><hr /><p>Explore Telerik Document Processing Libraries, plus component libraries, reporting and more with a free trial of the Telerik DevCraft bundle:</p><p><a href="https://www.telerik.com/download" class="Btn" target="_blank">Try DevCraft</a></p><img src="https://feeds.telerik.com/link/23052/17310580.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:1af3f9e9-0392-41b1-9eea-b11ef8e0dc60</id>
    <title type="text">How to Organize Minimal APIs</title>
    <summary type="text">Minimal APIs arrived with .NET 6 and now, in Version 10, are already part of the daily routine for many developers. In this post, we'll explore best practices for organizing your Minimal APIs and see how Carter can help make them even cleaner, more modular and more elegant.</summary>
    <published>2026-03-11T19:31:47Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17297665/how-to-organize-minimal-apis"/>
    <content type="text"><![CDATA[<p><span class="featured">Minimal APIs arrived with .NET 6 and now, in Version 10, are already part of the daily routine for many developers. In this post, we'll explore best practices for organizing your Minimal APIs and see how Carter can help make them even cleaner, more modular and more elegant.</span></p><p>In contrast to traditional Controller classes, minimal APIs offer a more compact way to create web APIs. The downside of this approach is that the Program class can quickly become bloated. To overcome this problem, we can use some approaches and libraries that help organize the mess and bring order to things.</p><p>In this post, we&rsquo;ll create a Minimal API with a disorganized Program class and then refactor it into a clean and elegant version. We&rsquo;ll also explore a very versatile approach using the Carter library.</p><h2 id="one-class-to-rule-them-all-">One Class to Rule Them All </h2><p>.NET 6 introduced a new format for creating web APIs, eliminating the need for the Startup class and even the Controller classes, responsible for HTTP input and output methods.</p><p>In the Minimal APIs model, the Program class, previously responsible only for bootstrapping the application, now also concentrates everything that was previously in Startup and Controller: service configuration, middleware definition and endpoint mapping.</p><p>This unification considerably reduced the number of classes in the project, but also opened the door to a common problem: the tendency to transform Program.cs into an inflated, confusing and difficult-to-maintain file.</p><p>Without a structured approach to organizing these responsibilities, what should be minimalist can quickly become something far from it. Therefore, adopting strategies and tools that help modularize routes, configurations and business rules is not only a good practice but also essential for maintaining the scalability and clarity of the project.</p><h2 id="organization-is-the-key-️">Organization Is the Key ️</h2><p>When working with Minimal APIs, the tendency is to put everything in the Program class, but the truth is that this class wasn&rsquo;t designed to be a repository, but rather to orchestrate the application. Keeping this class clean, small and focused on composition is the secret to preserving readability and facilitating code evolution.</p><p>And this is only possible when we use consistent organization: separating modules, extracting configurations, delegating routes and preventing implementation details from leaking to the highest level of the application.</p><p>In this section, we&rsquo;ll first create a messy example and then transform Program.cs into a truly minimalist entry point, taking in only what it should take in, while moving the rest to the most appropriate places.</p><h2 id="creating-the-project">Creating the Project</h2><p>The complete application code is available in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/customer-admin">Customer Admin source code</a>.</p><p>To create the base application, you can use the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -n CustomerAdmin
</code></pre><p>Then, open the application and add the following dependencies to the .csproj file:</p><pre class=" language-csharp"><code class="prism  language-csharp"> <span class="token operator">&lt;</span>ItemGroup<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore .SqlServer"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore .Design"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>IncludeAssets<span class="token operator">&gt;</span>runtime<span class="token punctuation">;</span> build<span class="token punctuation">;</span> native<span class="token punctuation">;</span> contentfiles<span class="token punctuation">;</span> analyzers<span class="token punctuation">;</span> buildtransitive<span class="token operator">&lt;</span><span class="token operator">/</span>IncludeAssets<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>PrivateAssets<span class="token operator">&gt;</span>all<span class="token operator">&lt;</span><span class="token operator">/</span>PrivateAssets<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>PackageReference<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>ItemGroup<span class="token operator">&gt;</span>
</code></pre><p>Now let&rsquo;s move on to the grand finale, the monstrous Program.cs class. Replace the code in the Program class with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddDbContext<span class="token punctuation">&lt;</span>AppDbContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
    options<span class="token punctuation">.</span><span class="token function">UseSqlServer</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddLogging</span><span class="token punctuation">(</span>logging <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    logging<span class="token punctuation">.</span><span class="token function">ClearProviders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    logging<span class="token punctuation">.</span><span class="token function">AddConsole</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddHttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddEndpointsApiExplorer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddCors</span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    options<span class="token punctuation">.</span><span class="token function">AddPolicy</span><span class="token punctuation">(</span><span class="token string">"Default"</span><span class="token punctuation">,</span> policy <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">{</span>
        policy<span class="token punctuation">.</span><span class="token function">AllowAnyOrigin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AllowAnyHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AllowAnyMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>IEmailSender<span class="token punctuation">,</span> SmtpEmailSender<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">UseCors</span><span class="token punctuation">(</span><span class="token string">"Default"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Use</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> logger <span class="token operator">=</span> context<span class="token punctuation">.</span>RequestServices<span class="token punctuation">.</span><span class="token generic-method function">GetRequiredService<span class="token punctuation">&lt;</span>ILoggerFactory<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">CreateLogger</span><span class="token punctuation">(</span><span class="token string">"RequestLogger"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Request: {method} {url}"</span><span class="token punctuation">,</span> context<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Method<span class="token punctuation">,</span> context<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Path<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">await</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/customers"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>CustomerDto dto<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">,</span> IEmailSender email<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Name is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">new</span> <span class="token class-name">EmailAddressAttribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Invalid email"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> entity <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span>
    <span class="token punctuation">{</span>
        Name <span class="token operator">=</span> dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
        Email <span class="token operator">=</span> dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
        CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">await</span> email<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span> <span class="token string">"Welcome!"</span><span class="token punctuation">,</span> <span class="token string">"Thanks for registering!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/customers/{entity.Id}"</span><span class="token punctuation">,</span> entity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/customers"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>AppDbContext db<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> items <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>items<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/customers/{id:int}"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> customer <span class="token keyword">is</span> <span class="token keyword">null</span> <span class="token operator">?</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/customers/{id:int}/activate"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>customer <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>customer<span class="token punctuation">.</span>IsActive<span class="token punctuation">)</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Customer already active"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    customer<span class="token punctuation">.</span>IsActive <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Customer</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Name <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsActive <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateTime CreatedAt <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomerDto</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Name <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">IEmailSender</span>
<span class="token punctuation">{</span>
    Task <span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> to<span class="token punctuation">,</span> <span class="token keyword">string</span> subject<span class="token punctuation">,</span> <span class="token keyword">string</span> body<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SmtpEmailSender</span> <span class="token punctuation">:</span> IEmailSender
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Task <span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> to<span class="token punctuation">,</span> <span class="token keyword">string</span> subject<span class="token punctuation">,</span> <span class="token keyword">string</span> body<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Sending email to {to}: {subject}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AppDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">AppDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Customer<span class="token operator">&gt;</span> Customers <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Customer<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The code above demonstrates the type of architecture that starts simple but quickly becomes a serious problem as the application grows. The Program class should only be the compose root of the application, where services are registered, middlewares are configured and high-level endpoints are mapped. Instead, the code above:</p><ul><li>Validates data</li><li>Accesses the database</li><li>Implements business rules</li><li>Sends emails</li><li>Defines DTOs, entities, services and DbContext</li><li>Registers all services manually</li><li>Writes middleware logic directly in it</li><li>Maps detailed and complex endpoints</li></ul><p>The result is a single file that performs the work of about 10 different files, resulting in something that grows uncontrollably and prevents the healthy evolution of the application. Each new feature exacerbates the problem, leaving the class disorganized and prone to errors. Furthermore, it hinders teamwork, generates merge conflicts and makes the integration of new developers much slower.</p><h2 id="putting-things-in-order-">Putting Things in Order </h2><p>Now that we&rsquo;ve seen a bad example of a bloated and messy Program class, let&rsquo;s put things in order, separating each part into its proper place.</p><p>The image below shows what the complete application structure will look like at the end of the post:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/project-structure.png?sfvrsn=dd618d2c_2" title="project structure" alt="Project structure" /></p><p>So, let&rsquo;s start with the entities, for which we can create separate classes. In this case, create a new folder called &ldquo;Model&rdquo; and inside it create the classes below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Customer</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Name <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsActive <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateTime CreatedAt <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomerDto</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Name <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now let&rsquo;s create all the other things related to the Infrastructure layer, which refers to everything that communicates with external services such as databases and web APIs. Create a new folder called &ldquo;Infrastructure&rdquo; and inside it create a new folder called &ldquo;Data&rdquo; and add the following class to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AppDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">AppDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">)</span>
        <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Customer<span class="token operator">&gt;</span> Customers <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Customer<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>All subsequent folders should be created inside the Infrastructure folder. So, add another folder called &ldquo;Email&rdquo; and, inside it, add the following interface and class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Email<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">IEmailSender</span>
<span class="token punctuation">{</span>
    Task <span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> to<span class="token punctuation">,</span> <span class="token keyword">string</span> subject<span class="token punctuation">,</span> <span class="token keyword">string</span> body<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Email<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SmtpEmailSender</span> <span class="token punctuation">:</span> IEmailSender
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Task <span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> to<span class="token punctuation">,</span> <span class="token keyword">string</span> subject<span class="token punctuation">,</span> <span class="token keyword">string</span> body<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Sending email to {to}: {subject}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here we create an interface and a class for sending emails, which is used only for demonstration purposes and is therefore very simple, but in real applications it can become large, so separating it from the Program class is essential.</p><p>The next step is to create the extension methods for the controllers, endpoints and policies. Create a new folder called &ldquo;Extensions&rdquo; and, inside it, create the classes below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Email<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">CustomerEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">CreateCustomer</span><span class="token punctuation">(</span>
        CustomerDto dto<span class="token punctuation">,</span>
        AppDbContext db<span class="token punctuation">,</span>
        IEmailSender email
    <span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Name is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">new</span> <span class="token class-name">EmailAddressAttribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Invalid email"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> entity <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span>
        <span class="token punctuation">{</span>
            Name <span class="token operator">=</span> dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            Email <span class="token operator">=</span> dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> email<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span> <span class="token string">"Welcome!"</span><span class="token punctuation">,</span> <span class="token string">"Thanks for registering!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/customers/{entity.Id}"</span><span class="token punctuation">,</span> entity<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetAll</span><span class="token punctuation">(</span>AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span><span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetById</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> customer <span class="token keyword">is</span> <span class="token keyword">null</span> <span class="token operator">?</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">Activate</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>customer <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>customer<span class="token punctuation">.</span>IsActive<span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Customer already active"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        customer<span class="token punctuation">.</span>IsActive <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here we add all the endpoints for the new customer registration API. Note that the methods are static so they can be used by the endpoint mapper that we will create next.</p><p>So now create a new class called <code>CustomersModule</code> and add the following code to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">CustomersModule</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> IEndpointRouteBuilder <span class="token function">MapCustomerEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> IEndpointRouteBuilder app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> <span class="token keyword">group</span> <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/customers"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> CustomerEndpoints<span class="token punctuation">.</span>CreateCustomer<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> CustomerEndpoints<span class="token punctuation">.</span>GetAll<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/{id:int}"</span><span class="token punctuation">,</span> CustomerEndpoints<span class="token punctuation">.</span>GetById<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/{id:int}/activate"</span><span class="token punctuation">,</span> CustomerEndpoints<span class="token punctuation">.</span>Activate<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> app<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, we define an endpoint module where we use an extension method to encapsulate the mapping of routes related to customers. All routes are grouped with the prefix <code>/customers</code>, which allows for scalable application growth and facilitates API organization.</p><p>Now let&rsquo;s create another extension class to define the registration of application services in the ASP.NET Core dependency injection container. This class will centralize the configuration of application dependencies such as infrastructure, HTTP communication, API documentation and CORS policies. So, still in the &ldquo;Extensions&rdquo; folder, add the class below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Email<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ServiceCollectionExtensions</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> IServiceCollection <span class="token function">AddApplicationServices</span><span class="token punctuation">(</span><span class="token keyword">this</span> IServiceCollection services<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>IEmailSender<span class="token punctuation">,</span> SmtpEmailSender<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> services<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> IServiceCollection <span class="token function">AddInfrastructure</span><span class="token punctuation">(</span>
        <span class="token keyword">this</span> IServiceCollection services<span class="token punctuation">,</span>
        IConfiguration config
    <span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        services<span class="token punctuation">.</span><span class="token generic-method function">AddDbContext<span class="token punctuation">&lt;</span>AppDbContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
            options<span class="token punctuation">.</span><span class="token function">UseSqlServer</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        services<span class="token punctuation">.</span><span class="token function">AddHttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> services<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> IServiceCollection <span class="token function">AddApiDocumentation</span><span class="token punctuation">(</span><span class="token keyword">this</span> IServiceCollection services<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        services<span class="token punctuation">.</span><span class="token function">AddEndpointsApiExplorer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> services<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> IServiceCollection <span class="token function">AddCorsPolicies</span><span class="token punctuation">(</span><span class="token keyword">this</span> IServiceCollection services<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        services<span class="token punctuation">.</span><span class="token function">AddCors</span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            options<span class="token punctuation">.</span><span class="token function">AddPolicy</span><span class="token punctuation">(</span>
                <span class="token string">"Default"</span><span class="token punctuation">,</span>
                policy <span class="token operator">=</span><span class="token operator">&gt;</span> policy<span class="token punctuation">.</span><span class="token function">AllowAnyOrigin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AllowAnyHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AllowAnyMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> services<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that here we have several configurations grouped into a separate file, leaving the Program class free for what it is actually responsible for.</p><p>Now let&rsquo;s create the class responsible for implementing the configurations that belong to the WebApplication class. So, create a new class called <code>WebApplicationExtensions</code> and add the code below to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Middlewares<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">WebApplicationExtensions</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> IApplicationBuilder <span class="token function">UseApiDocumentation</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> app<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> IApplicationBuilder <span class="token function">UseCorsPolicies</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token function">UseCors</span><span class="token punctuation">(</span><span class="token string">"Default"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> app<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> WebApplication <span class="token function">UseRequestLogging</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token generic-method function">UseMiddleware<span class="token punctuation">&lt;</span>RequestLoggingMiddleware<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> app<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The last configuration class will be used to implement logging middleware. So, create a new class called <code>RequestLoggingMiddleware</code> and add the code below to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Middlewares<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RequestLoggingMiddleware</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> RequestDelegate _next<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ILogger<span class="token operator">&lt;</span>RequestLoggingMiddleware<span class="token operator">&gt;</span> _logger<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">RequestLoggingMiddleware</span><span class="token punctuation">(</span>RequestDelegate next<span class="token punctuation">,</span> ILogger<span class="token operator">&lt;</span>RequestLoggingMiddleware<span class="token operator">&gt;</span> logger<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _next <span class="token operator">=</span> next<span class="token punctuation">;</span>
        _logger <span class="token operator">=</span> logger<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">InvokeAsync</span><span class="token punctuation">(</span>HttpContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Request: {method} {url}"</span><span class="token punctuation">,</span> context<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Method<span class="token punctuation">,</span> context<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Path<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> <span class="token function">_next</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>With all the implementations organized, we can finally implement the method calls in the Program class. So, replace the existing code there with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder
    <span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddApplicationServices</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddInfrastructure</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddApiDocumentation</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddCorsPolicies</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">UseRequestLogging</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseApiDocumentation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseCorsPolicies</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapCustomerEndpoints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>See how the Program class looks now. Instead of spreading configurations across multiple files, it centralizes the entire application initialization process in a fluid way.</p><p>Starting with the creation of the <code>WebApplicationBuilder</code>, responsible for preparing the environment, the configuration of services is done in a chained manner, which has greatly improved readability compared to the first version.</p><p>Each extension method represents a well-defined block of responsibility, such as registering application services, configuring the infrastructure (database, external integrations, etc.), API documentation and CORS policies.</p><p>Finally, the HTTP request pipeline, middleware and endpoint mapping are initiated. Thus, this refactored approach makes the Program class leaner and more expressive, serving as a high-level view of how the application is composed and initialized.</p><h2 id="taking-a-shortcut-with-carter-">Taking a Shortcut with Carter </h2><p><a target="_blank" href="https://github.com/CarterCommunity/Carter">Carter</a> is an open-source NuGet package for defining HTTP routes and request handlers using a simple and declarative syntax.</p><p>Below, we&rsquo;ll use Carter in the refactored version and see how it can be a great option for organizing minimal APIs.</p><p>To download Carter to the project, simply run the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Carter --version 10.0.0
</code></pre><p>Next, create a new folder in the project called &ldquo;Modules,&rdquo; inside it another folder called &ldquo;Customers&rdquo; and, inside that folder, create the class below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerAdmin<span class="token punctuation">.</span>Modules<span class="token punctuation">.</span>Customers<span class="token punctuation">;</span>

<span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomersModule</span> <span class="token punctuation">:</span> ICarterModule
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">AddRoutes</span><span class="token punctuation">(</span>IEndpointRouteBuilder app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> <span class="token keyword">group</span> <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/customers"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> Create<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> GetAll<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/{id:int}"</span><span class="token punctuation">,</span> GetById<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/{id:int}/activate"</span><span class="token punctuation">,</span> Activate<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">Create</span><span class="token punctuation">(</span>CustomerDto dto<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">,</span> IEmailSender email<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Name is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">new</span> <span class="token class-name">EmailAddressAttribute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Invalid email"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span>
        <span class="token punctuation">{</span>
            Name <span class="token operator">=</span> dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            Email <span class="token operator">=</span> dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> email<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Email<span class="token punctuation">,</span> <span class="token string">"Welcome!"</span><span class="token punctuation">,</span> <span class="token string">"Thanks for registering!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/customers/{customer.Id}"</span><span class="token punctuation">,</span> customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetAll</span><span class="token punctuation">(</span>AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span><span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetById</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> customer <span class="token keyword">is</span> <span class="token keyword">null</span> <span class="token operator">?</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">Activate</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> AppDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Customers<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>customer <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>customer<span class="token punctuation">.</span>IsActive<span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Customer already active"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        customer<span class="token punctuation">.</span>IsActive <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>customer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that the class we created inherits from the Carter interface: <code>ICarterModule</code>, which allows grouping related endpoints without coupling them to Program.cs. This isolates route definitions, validations and integrations into separate modules, making the code scalable and aligned with the true purpose of Minimal APIs: simplicity with organization.</p><p>To configure the Program class with Carter, replace the existing code with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Carter<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerAdmin<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Extensions<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder
    <span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddApplicationServices</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddInfrastructure</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddApiDocumentation</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddCorsPolicies</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddCarter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">UseRequestLogging</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseApiDocumentation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseCorsPolicies</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapCarter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Note that the Program class remains simple and clean using Carter. Another important point to highlight is that we have no coupling to the Program class. Instead, we pass all responsibility for the modules to Carter through dependency injection (DI), implemented by the <code>app.MapCarter();</code> method.</p><h2 id="conclusion-">Conclusion </h2><p>Minimal APIs streamline development by offering a simple approach to creating compact endpoints. However, as the application grows, it&rsquo;s common for the Program class to become extensive and disorganized if project structure isn&rsquo;t carefully considered.</p><p>To minimize the chances of having an inflated Program class, we can adopt some organizational strategies. In this post, we saw an approach that clearly separates the responsibilities of each class and, to shorten the process, we used the Carter library, leaving the Program class responsible only for the configurations that truly belong to it.</p><p>I hope this post helps you create more organized APIs that are ready to evolve!</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">What&rsquo;s New with APIs in .NET 10: Taking a Look at Real Improvements</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Learn about <a target="_blank" href="https://www.telerik.com/blogs/whats-new-apis-net-10-real-improvements">.NET 10 and C# 14 updates from the perspective of an API developer</a> using a real-world example: an order management API with validation, OpenAPI docs and Entity Framework Core.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17297665.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e4d019ae-d92f-4524-94a9-8bbfed2b9992</id>
    <title type="text">Building a RAG (Retrieval-Augmented Generation) in ASP.NET Core</title>
    <summary type="text">RAG is a technique that enhances language models by integrating them with internal knowledge sources. In this post, you’ll understand the concept of RAG and learn how to implement it in an ASP.NET Core application, exploring a practical, real-world scenario.</summary>
    <published>2026-02-02T17:13:06Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17268545/building-rag-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">RAG is a technique that enhances language models by integrating them with internal knowledge sources. In this post, you&rsquo;ll understand the concept of RAG and learn how to implement it in an ASP.NET Core application, exploring a practical, real-world scenario.</span></p><p>The use of artificial intelligence has become increasingly common in web applications, so it&rsquo;s important to consider how to optimize the use of AI via large language models (LLMs) for efficiency and cost reduction.</p><p>In this article, we&rsquo;ll explore how to use the Retrieval-Augmented Generation (RAG) concept in ASP,NET Core projects to create an automated return policy system. We&rsquo;ll understand how RAG combines information retrieval and text generation to enable LLMs to respond based on real, up-to-date data, reducing the need for retraining.</p><h2 id="understanding-the-rag-concept">Understanding the RAG Concept</h2><p>Retrieval Augmented Generation, or RAG, is a technique (sometimes referred to as an architecture) used to optimize the performance of an AI model. It consists of connecting a model to an internal knowledge base, which can be a text file or even a database, to provide more relevant answers without the need for additional training.</p><p>In simple terms, instead of relying only on its training data, RAG allows the model to retrieve up-to-date or domain-specific information to generate a more accurate answer.</p><h2 id="how-does-rag-work">How Does RAG Work?</h2><p>The operation of RAG basically involves combining information retrieval models with generative AI models, and then returning a more accurate result. RAG systems typically follow a five-stage process:</p><ol><li><p><strong>User Input (User Prompt)</strong><br />The user asks a question or sends a command, for example, &ldquo;What are the company&rsquo;s security policies?&rdquo;</p></li><li><p><strong>Information Retrieval (Retrieval)</strong><br />A retrieval model, such as a vector search engine, is triggered to search for relevant data in an external knowledge base, such as corporate documents or databases. This step transforms the prompt into embeddings and searches for the semantically closest documents.</p></li><li><p><strong>Integration (Integration / Context Assembly)</strong><br />The most relevant information found is returned and combined with the original prompt. In this stage, the system assembles an augmented prompt that contains both the user&rsquo;s question and the relevant snippets retrieved from the internal knowledge base.</p></li><li><p><strong>Generation (Augmented Generation)</strong><br />The language model receives this augmented prompt and generates a contextualized response, taking into account the retrieved data.</p></li><li><p><strong>Output to the User (Response Delivery)</strong><br />The final result is then delivered to the user, usually accompanied by references to the sources or links that support the answer.</p></li></ol><p>These five steps constitute the complete RAG workflow, which goes beyond a simple question-and-answer approach. It combines querying, filtering, context assembly and contextualized generation, enabling more accurate and up-to-date responses. The image below summarizes this workflow:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/rag-stages.png?sfvrsn=85e59f61_2" title="rag-stages" alt="RAG stages" /></p><h2 id="creating-a-rag-in-asp.net-core">Creating a RAG in ASP,NET Core</h2><p>To practice using RAG, we will develop an API in ASP,NET Core that will answer questions about a product return policy. The API will retrieve the information from a knowledge base, a text file containing the return policy. This data will be converted into embeddings and stored in an SQLite database.</p><p>Then, the API will send the relevant content to the OpenAI API, which will generate a contextualized response and return it in the request response.</p><p>You can check the complete source code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/return-policy-ai">Return Policy source code</a>.</p><h3 id="prerequisites">Prerequisites</h3><p>To practice the example in this tutorial, you will need to have the following:</p><ol><li><p>API Key. If you don&rsquo;t already have an API key, you can use this tutorial to create it: <a target="_blank" href="https://www.telerik.com/blogs/get-started-integrating-ai-aspnet-core-applications">Get Started Integrating AI in Your ASP.NET Core Applications</a>.</p></li><li><p>A project created on the OpenAI website</p></li><li><p>The following models configured in your API key:</p></li></ol><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/open-ai-models.png?sfvrsn=598565c9_2" title="open ai models" alt="Open AI models" /></p><p>You can use other models of your choice, but other libraries and additional configurations may be necessary.</p><p>So, to create the sample application and download the packages you can use the following commands on the terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o ReturnPolicy
</code></pre><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Microsoft.Data.Sqlite --version 9.0.10
dotnet add package OpenAI --version 2.5.0
</code></pre><p>Next, let&rsquo;s create the single model class used in the application, it will be used to request the data. So, create a new folder called &ldquo;Models&rdquo; and, inside it, add the following record:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> ReturnPolicy<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">QuestionRequest</span><span class="token punctuation">(</span><span class="token keyword">string</span> Question<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="creating-the-return-policy-file">Creating the Return Policy File</h3><p>Now let&rsquo;s create a text file that will serve as the knowledge base to be sent to the model to formulate the response. It will contain a common example of a return policy. Create a new folder called &ldquo;Data&rdquo; and, inside it, create a file called <code>return_policy.txt</code> and add to it the following text:</p><pre class=" language-text"><code class="prism  language-text">Return Policy - Updated July 2025

Our customers may return most new, unopened items within 30 days of delivery for a full refund.
Products that are defective or damaged can be returned or exchanged at any time.

To be eligible for a return:

- The product must be in the same condition as received.
- Proof of purchase is required.
- Returns after 30 days are subject to manager approval.

Please contact our support team before sending any returns.
</code></pre><h3 id="creating-the-logic-for-generating-the-response">Creating the Logic for Generating the Response</h3><p>Now, let&rsquo;s create the logic to generate, register and retrieve the embeddings, as well as formulate the response generated by the model.</p><p>Create a new folder called &ldquo;Service&rdquo; and, inside it, add the class below.</p><p>Note that we will first create the class and throughout the post we will add methods to it until it is complete. This is intended to explain each method separately for better understanding.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>Data<span class="token punctuation">.</span>Sqlite<span class="token punctuation">;</span>
<span class="token keyword">using</span> OpenAI<span class="token punctuation">;</span>
<span class="token keyword">using</span> OpenAI<span class="token punctuation">.</span>Chat<span class="token punctuation">;</span>
<span class="token keyword">using</span> OpenAI<span class="token punctuation">.</span>Embeddings<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> ReturnPolicy<span class="token punctuation">.</span>Services
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PolicyService</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> ChatClient _chatClient<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> EmbeddingClient _embeddingClient<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span> _policyPath<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span> _dbPath<span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token function">PolicyService</span><span class="token punctuation">(</span>IConfiguration config<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> apiKey <span class="token operator">=</span> config<span class="token punctuation">[</span><span class="token string">"OpenAI:ApiKey"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>apiKey<span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Missing OpenAI:ApiKey in configuration."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OpenAIClient</span><span class="token punctuation">(</span>apiKey<span class="token punctuation">)</span><span class="token punctuation">;</span>

            _chatClient <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">GetChatClient</span><span class="token punctuation">(</span><span class="token string">"gpt-4o-mini"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            _embeddingClient <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">GetEmbeddingClient</span><span class="token punctuation">(</span><span class="token string">"text-embedding-3-small"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _policyPath <span class="token operator">=</span> Path<span class="token punctuation">.</span><span class="token function">Combine</span><span class="token punctuation">(</span>Directory<span class="token punctuation">.</span><span class="token function">GetCurrentDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Data"</span><span class="token punctuation">,</span> <span class="token string">"return_policy.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            _dbPath <span class="token operator">=</span> Path<span class="token punctuation">.</span><span class="token function">Combine</span><span class="token punctuation">(</span>Directory<span class="token punctuation">.</span><span class="token function">GetCurrentDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Data"</span><span class="token punctuation">,</span> <span class="token string">"embeddings.db"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token function">InitializeDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token function">LoadPolicyIntoDatabaseAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here we are using the <code>PolicyService</code> class to integrate the application with the OpenAI API and prepare the necessary data to work with RAG.</p><p>At the beginning, four private fields are declared: <code>_chatClient</code> and <code>_embeddingClient</code> are responsible for communicating with the OpenAI API. The first handles the chat model (in this case, gpt-4o-mini), while the second works with the embeddings model.</p><p>Meanwhile, <code>_policyPath</code> stores the path to the text file containing the return policy we created earlier, and <code>_dbPath</code> indicates the location where the SQLite database will be created.</p><p>The class constructor starts by reading the OpenAI API key from the application&rsquo;s configuration file. If the key is not present, an exception is thrown indicating that it is required. Then, an OpenAIClient object is created, which serves as an access point to the different OpenAI services.</p><p>With this client, two components are initialized: the <code>_chatClient</code>, which will be used to generate intelligent responses, and the <code>_embeddingClient</code>, which will handle the creation of the text embeddings for the policy. After that, we define the paths of the data files, so that both the original text and the embeddings database are stored within the project&rsquo;s Data folder.</p><p>Finally, two important actions are performed: <code>InitializeDatabase()</code> to prepare the SQLite database, creating the necessary tables if they do not already exist, and <code>LoadPolicyIntoDatabaseAsync().Wait()</code>, which reads the content of the return policy file, generates the embeddings and saves them to the database for later use.</p><p>Now, let&rsquo;s create the methods to insert and retrieve the embeddings from the database. In the <code>PolicyService</code> class, add the methods below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">InitializeDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> conn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SqliteConnection</span><span class="token punctuation">(</span>$<span class="token string">"Data Source={_dbPath}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    conn<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> cmd <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">CreateCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    cmd<span class="token punctuation">.</span>CommandText <span class="token operator">=</span> <span class="token string">@"CREATE TABLE IF NOT EXISTS PolicyChunks (
                        Id INTEGER PRIMARY KEY AUTOINCREMENT,
                        Text TEXT NOT NULL,
                        Embedding BLOB NOT NULL
                        );"</span><span class="token punctuation">;</span>
    cmd<span class="token punctuation">.</span><span class="token function">ExecuteNonQuery</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">async</span> Task <span class="token function">LoadPolicyIntoDatabaseAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> policyText <span class="token operator">=</span> <span class="token keyword">await</span> File<span class="token punctuation">.</span><span class="token function">ReadAllTextAsync</span><span class="token punctuation">(</span>_policyPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> chunks <span class="token operator">=</span> <span class="token function">SplitIntoChunks</span><span class="token punctuation">(</span>policyText<span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">using</span> <span class="token keyword">var</span> conn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SqliteConnection</span><span class="token punctuation">(</span>$<span class="token string">"Data Source={_dbPath}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    conn<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> chunk <span class="token keyword">in</span> chunks<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> checkCmd <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">CreateCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        checkCmd<span class="token punctuation">.</span>CommandText <span class="token operator">=</span> <span class="token string">"SELECT COUNT(*) FROM PolicyChunks WHERE Text = $text"</span><span class="token punctuation">;</span>

        checkCmd<span class="token punctuation">.</span>Parameters<span class="token punctuation">.</span><span class="token function">AddWithValue</span><span class="token punctuation">(</span><span class="token string">"$text"</span><span class="token punctuation">,</span> chunk<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">bool</span> exists <span class="token operator">=</span> Convert<span class="token punctuation">.</span><span class="token function">ToInt32</span><span class="token punctuation">(</span>checkCmd<span class="token punctuation">.</span><span class="token function">ExecuteScalar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>exists<span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> embeddingResult <span class="token operator">=</span> <span class="token keyword">await</span> _embeddingClient<span class="token punctuation">.</span><span class="token function">GenerateEmbeddingAsync</span><span class="token punctuation">(</span>chunk<span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            <span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> vector <span class="token operator">=</span> embeddingResult<span class="token punctuation">.</span>Value<span class="token punctuation">.</span><span class="token function">ToFloats</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> insertComand <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">CreateCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            insertComand<span class="token punctuation">.</span>CommandText <span class="token operator">=</span> <span class="token string">"INSERT INTO PolicyChunks (Text, Embedding) VALUES ($text, $embedding)"</span><span class="token punctuation">;</span>

            insertComand<span class="token punctuation">.</span>Parameters<span class="token punctuation">.</span><span class="token function">AddWithValue</span><span class="token punctuation">(</span><span class="token string">"$text"</span><span class="token punctuation">,</span> chunk<span class="token punctuation">)</span><span class="token punctuation">;</span>
            insertComand<span class="token punctuation">.</span>Parameters<span class="token punctuation">.</span><span class="token function">AddWithValue</span><span class="token punctuation">(</span><span class="token string">"$embedding"</span><span class="token punctuation">,</span> <span class="token function">FloatArrayToBytes</span><span class="token punctuation">(</span>vector<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            insertComand<span class="token punctuation">.</span><span class="token function">ExecuteNonQuery</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>ex<span class="token punctuation">.</span>Message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here, the <code>InitializeDatabase()</code> method creates the basic structure of the SQLite database, if it doesn&rsquo;t already exist. First, it establishes a connection to the file defined in <code>_dbPath</code>, which is the same path configured in the class constructor. Then, it opens this connection and executes an SQL command responsible for creating the table <code>PolicyChunks</code>.</p><p>Note that the table consists of three columns: <code>Id</code> for the primary key, <code>Text</code>, which will store the text segment (or chunk) extracted from the policy file, and <code>Embedding</code>, a BLOB (Binary Large Object), that is, a binary field where the numerical vector that semantically represents that text segment will be stored. This means that the database is ready to receive and store the text data and their respective embeddings.</p><p>The <code>LoadPolicyIntoDatabaseAsync()</code> method is responsible for loading the policy content and saving its embeddings to the database.</p><p>First, it reads the entire content of the policy file, located at <code>_policyPath</code>, and then divides it into smaller parts using the <code>SplitIntoChunks()</code> method. This division is important because OpenAI&rsquo;s language models have input size limits. Therefore, the text is broken into blocks of up to 500 characters or tokens.</p><p>Then, a new connection to the database is opened, and each text segment (chunk) is processed. For each segment, it checks if the content is already stored in the table. This is done through a query that counts how many records have the same text. If the segment already exists, it is ignored to avoid duplication.</p><p>When a new segment is found, the method requests the OpenAI embeddings model to generate a numerical vector representing the meaning of that text. The result is then converted to a float array, which is then transformed into bytes using the <code>FloatArrayToBytes()</code> method, to be compatible with the BLOB type of the database.</p><p>Finally, the vector and the text are inserted together into the <code>PolicyChunks</code> table.</p><p>If any error occurs during the process, such as a communication failure with the API, for example, the exception is displayed in the console, allowing processing to continue with the remaining segments.</p><p>Now let&rsquo;s add the most important part of the service, where the intelligent search and generation of responses based on the company&rsquo;s return policy takes place. So, add the following code to the <code>PolicyService</code> class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> <span class="token function">GetAnswerAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> question<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> queryEmbedding <span class="token operator">=</span> <span class="token keyword">await</span> _embeddingClient<span class="token punctuation">.</span><span class="token function">GenerateEmbeddingAsync</span><span class="token punctuation">(</span>question<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> queryVector <span class="token operator">=</span> queryEmbedding<span class="token punctuation">.</span>Value<span class="token punctuation">.</span><span class="token function">ToFloats</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> topChunks <span class="token operator">=</span> <span class="token function">GetTopChunks</span><span class="token punctuation">(</span>queryVector<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> context <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">Join</span><span class="token punctuation">(</span><span class="token string">"\n\n"</span><span class="token punctuation">,</span> topChunks<span class="token punctuation">)</span><span class="token punctuation">;</span>

    List<span class="token operator">&lt;</span>ChatMessage<span class="token operator">&gt;</span> messages <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        ChatMessage<span class="token punctuation">.</span><span class="token function">CreateSystemMessage</span><span class="token punctuation">(</span><span class="token string">"You are a helpful assistant that answers based on company return policies."</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        ChatMessage<span class="token punctuation">.</span><span class="token function">CreateUserMessage</span><span class="token punctuation">(</span>$<span class="token string">"Use only the following policy text to answer the question:\n\n{context}\n\nQuestion: {question}"</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> response <span class="token operator">=</span> <span class="token keyword">await</span> _chatClient<span class="token punctuation">.</span><span class="token function">CompleteChatAsync</span><span class="token punctuation">(</span>messages<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> response<span class="token punctuation">.</span>Value<span class="token punctuation">.</span>Content<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>Text<span class="token punctuation">.</span><span class="token function">Trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> List<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> <span class="token function">GetTopChunks</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> queryEmbedding<span class="token punctuation">,</span> <span class="token keyword">int</span> topN<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> conn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SqliteConnection</span><span class="token punctuation">(</span>$<span class="token string">"Data Source={_dbPath}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    conn<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> selectCmd <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">CreateCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    selectCmd<span class="token punctuation">.</span>CommandText <span class="token operator">=</span> <span class="token string">"SELECT Text, Embedding FROM PolicyChunks"</span><span class="token punctuation">;</span>
   
    <span class="token keyword">using</span> <span class="token keyword">var</span> reader <span class="token operator">=</span> selectCmd<span class="token punctuation">.</span><span class="token function">ExecuteReader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> scoredChunks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">string</span> Text<span class="token punctuation">,</span> <span class="token keyword">double</span> Score<span class="token punctuation">)</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">while</span> <span class="token punctuation">(</span>reader<span class="token punctuation">.</span><span class="token function">Read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> text <span class="token operator">=</span> reader<span class="token punctuation">.</span><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> embeddingBytes <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>reader<span class="token punctuation">[</span><span class="token string">"Embedding"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> embedding <span class="token operator">=</span> <span class="token function">BytesToFloatArray</span><span class="token punctuation">(</span>embeddingBytes<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> similarity <span class="token operator">=</span> <span class="token function">CosineSimilarity</span><span class="token punctuation">(</span>embedding<span class="token punctuation">,</span> queryEmbedding<span class="token punctuation">)</span><span class="token punctuation">;</span>
        scoredChunks<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token punctuation">(</span>text<span class="token punctuation">,</span> similarity<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> scoredChunks
        <span class="token punctuation">.</span><span class="token function">OrderByDescending</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Score<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>topN<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Text<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> <span class="token function">SplitIntoChunks</span><span class="token punctuation">(</span><span class="token keyword">string</span> text<span class="token punctuation">,</span> <span class="token keyword">int</span> maxLength<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> text<span class="token punctuation">.</span>Length<span class="token punctuation">;</span> i <span class="token operator">+</span><span class="token operator">=</span> maxLength<span class="token punctuation">)</span>
        <span class="token keyword">yield</span> <span class="token keyword">return</span> text<span class="token punctuation">.</span><span class="token function">Substring</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">Min</span><span class="token punctuation">(</span>maxLength<span class="token punctuation">,</span> text<span class="token punctuation">.</span>Length <span class="token operator">-</span> i<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">double</span> <span class="token function">CosineSimilarity</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> v1<span class="token punctuation">,</span> <span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> v2<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">double</span> dot <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">,</span> mag1 <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">,</span> mag2 <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> v1<span class="token punctuation">.</span>Length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        dot <span class="token operator">+</span><span class="token operator">=</span> v1<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">*</span> v2<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
        mag1 <span class="token operator">+</span><span class="token operator">=</span> v1<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">*</span> v1<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
        mag2 <span class="token operator">+</span><span class="token operator">=</span> v2<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">*</span> v2<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> dot <span class="token operator">/</span> <span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">Sqrt</span><span class="token punctuation">(</span>mag1<span class="token punctuation">)</span> <span class="token operator">*</span> Math<span class="token punctuation">.</span><span class="token function">Sqrt</span><span class="token punctuation">(</span>mag2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">FloatArrayToBytes</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> array<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> bytes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">byte</span><span class="token punctuation">[</span>array<span class="token punctuation">.</span>Length <span class="token operator">*</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    Buffer<span class="token punctuation">.</span><span class="token function">BlockCopy</span><span class="token punctuation">(</span>array<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bytes<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bytes<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> bytes<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">float</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">BytesToFloatArray</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> floats <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">float</span><span class="token punctuation">[</span>bytes<span class="token punctuation">.</span>Length <span class="token operator">/</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    
    Buffer<span class="token punctuation">.</span><span class="token function">BlockCopy</span><span class="token punctuation">(</span>bytes<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> floats<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bytes<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> floats<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now, let&rsquo;s analyze each method:</p><p><strong>1. GetAnswerAsync(string question)</strong></p><p>This method receives the user&rsquo;s question and returns an AI-generated answer based on the policy content stored in the database.</p><p>First, it transforms the question into an embedding vector, using the same embedding model configured previously. This numerical vector (<code>queryVector</code>) represents the semantic meaning of the question.</p><p>Next, the method calls <code>GetTopChunks()</code>, which searches the database for the chunks most similar to the meaning of the question, i.e., the parts of the policy that contain information relevant to the answer. It requests the three most relevant chunks (<code>topChunks</code>), and then combines these texts into a single string called context.</p><p>With the context ready, the method assembles a list of messages to send to the chat model. The first message is an instruction, stating that the assistant should only answer based on the company policy. The second message contains both the policy text and the user&rsquo;s original question.</p><p>Finally, the code calls the <code>CompleteChatAsync()</code> method of the OpenAI client, which returns a generated response based on the provided context. The final text is extracted, cleaned and then returned.</p><p><strong>2. GetTopChunks(float[] queryEmbedding, int topN)</strong></p><p>This method identifies which chunks in the database are most semantically similar to the question asked.</p><p>It opens a connection to the SQLite database and reads all records from the PolicyChunks table, which contains the texts and their embeddings.</p><p>For each record, it calculates the cosine similarity between the query vector (<code>queryEmbedding</code>) and the stored text vector. This calculation generates a number between 0 and 1; the closer to 1, the more similar the meaning between the two texts.</p><p>After calculating the scores, the method sorts the results in descending order and returns the <code>topN</code> most relevant chunks (in this case, three). These chunks will serve as the knowledge base for the model to answer correctly.</p><p><strong>3. SplitIntoChunks(string text, int maxLength)</strong></p><p>This method divides long texts into smaller parts, respecting a defined maximum size (for example, 500 characters).</p><p>It iterates through the original text and, in each iteration, returns a chunk that can be processed without exceeding the model&rsquo;s token limit. It is essential to use techniques like this when working with large documents in RAG systems.</p><p><strong>4. CosineSimilarity(float[] v1, float[] v2)</strong></p><p>This method is the mathematical basis of the semantic search system. It calculates the angle between two vectors in the embedding space&mdash;the smaller the angle (or the larger the cosine), the closer the meanings of the texts represented by those vectors.</p><p>It is with this metric that the system determines which sections of the policy are most relevant to a specific question.</p><p><strong>5. FloatArrayToBytes(float[] array) and BytesToFloatArray(byte[] bytes)</strong></p><p>These two methods convert between arrays of numbers and bytes. Since SQLite does not have a native data type to store arrays of floats, the embeddings are converted into a binary format (BLOB) before being saved to the database. When they need to be used again, these bytes are converted back into an array of floats, preserving all the information of the original vector.</p><h3 id="adding-the-api-key">Adding the API Key</h3><p>To access the OpenAI API, you need an API key. With the key in hand, add the following code to the application&rsquo;s <code>appsettings.json</code> file:</p><pre class=" language-json"><code class="prism  language-json"><span class="token string">"OpenAI"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
  <span class="token string">"ApiKey"</span><span class="token punctuation">:</span> <span class="token string">"YOUR_OPEN_AI_API_KEY"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
</code></pre><p>Finally, in the Program class, add the following code:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> ReturnPolicy<span class="token punctuation">.</span>Services<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddEndpointsApiExplorer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>PolicyService<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">MapControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h2 id="running-the-application-and-testing-the-rag">Running the Application and Testing the RAG</h2><p>Now that everything is configured, we can run the application and test the endpoint that will generate the response. In this post, we&rsquo;ll use Progress Telerik <a href="https://www.telerik.com/fiddler/fiddler-everywhere" target="_blank">Fiddler Everywhere</a> for this. Run the application and make the following request:</p><p>Route: POST - <code>https://localhost:PORT/api/policy/ask</code></p><p>Body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
    <span class="token string">"question"</span><span class="token punctuation">:</span> <span class="token string">"Can I return an opened product?"</span>
<span class="token punctuation">}</span>
</code></pre><p>So the generated response will be something like this:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/rag-response.png?sfvrsn=8fa57c8b_2" title="policy response" alt="Policy response" /></p><p>The complete response returned by the model was:</p><pre class=" language-text"><code class="prism  language-text">According to the return policy, most new, unopened items can be returned within 30 days for a full refund. If you have an opened product that is defective or damaged, it can be returned or exchanged at any time. If you need further assistance, please contact our support team.
</code></pre><p>Note that the answer provided by the model is aligned with the policy, which states that new and unused items can be returned within 30 days. Furthermore, it demonstrates clarity and objectivity by directly addressing the user&rsquo;s question, conveying confidence and concern for the customer, which contributes to a good support experience.</p><h2 id="conclusion">Conclusion</h2><p>The RAG technique allows for the generation of contextualized and intelligent responses, integrating the retrieval of relevant information with advanced synthesis capabilities. The responses produced by RAGs reduce ambiguities, improve the user experience and increase the reliability of interactions, especially in scenarios where document-based accuracy is essential.</p><p>In this post, we created a complete RAG system in ASP,NET Core, integrating it with OpenAI services for generating embeddings and producing contextualized responses. I hope this content serves as a practical reference, facilitating the adoption of the RAG technique whenever you have the opportunity to apply it in your projects.</p><p>If this all seems like a lot of work, there are professional RAG platforms you can explore, such as Progress Agentic RAG.</p><h3 id="progress-agentic-rag">Progress Agentic RAG</h3><p><a target="_blank" href="https://www.progress.com/agentic-rag">Progress Agentic RAG</a> is a RAG-as-a-Service platform that simplifies the creation of augmented reality (AR) retrieval and generation solutions. Instead of requiring proprietary infrastructure or multiple separate tools, it offers a ready-to-use environment for indexing documents, files and even videos, along with integrated metrics to evaluate RAG quality.</p><p>In practice, Progress Agentic RAG stands out for:</p><ul><li>Generative search for websites, which can interpret user intent and build responses using existing content on the page itself.</li><li>Use in sensitive areas, such as the financial sector, supporting decision-making without compromising privacy and security.</li><li>The ability to handle unstructured data, covering more than 60 formats: PDFs, videos, spreadsheets, texts, among others. This greatly helps teams like legal departments, who need quick and accurate answers.</li><li>Intelligent video indexing, allowing the location of specific segments and generating responses based on audiovisual content.</li></ul><p>For developers, Progress Agentic RAG functions as an intelligence layer that can be integrated with minimal effort. This reduces the need to build a RAG pipeline from scratch and accelerates the development of generative AI-based solutions.</p><p><strong>Keep reading:</strong> <a target="_blank" href="https://www.telerik.com/blogs/understanding-rag-retrieval-augmented-generation">Understand more about RAG</a> and get a <a target="_blank" href="https://www.telerik.com/blogs/introductory-walk-through-progress-agentic-rag-dashboard">walk-through of Progress Agentic RAG</a>.</p><img src="https://feeds.telerik.com/link/23052/17268545.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:a2228ce0-ff8c-42cd-a991-cae03f6490b0</id>
    <title type="text">Getting the Right Row on the Screen in the Kendo Grid</title>
    <summary type="text">You can move any row into the user’s view screen (and save the user scrolling to find it) using the KendoGrid scrollToItem method.</summary>
    <published>2026-01-22T17:22:33Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17260966/getting-the-right-row-on-the-screen-in-the-kendo-grid"/>
    <content type="text"><![CDATA[<p><span class="featured">You can move any row into the user&rsquo;s view screen (and save the user scrolling to find it) using the KendoGrid scrollToItem method.</span></p><p>It&rsquo;s probably too obvious to mention, but: When your user is working with a Graphical User Interface (like, for example, a webpage) you&rsquo;re mandated to get everything the user needs <em>on the screen</em>.</p><p>If you have a grid that has more rows than will fit on the screen, that mandate boils down to getting &ldquo;the row the user wants&rdquo; onto the screen, ideally without forcing the user to scroll-and-scan to find it.</p><p>In many of the Progress Kendo UI Grids, the <code>scrollToItem</code> method lets you get the row you or the user wants onto the screen, just by asking for the object the user wants. Using <code>scrollToItem</code>, you can pull a row that&rsquo;s part of the grid&rsquo;s page but not currently visible onto the screen, pull a row already on the screen to the top of the grid, or (in many cases) even pull rows not in the grid&rsquo;s current page onto the screen. Your users, rather than having to scroll to find the item they want, can jump straight to it or you can put a row you know your user needs to see onto the screen.</p><p>The <code>scrollToItem</code> method is available in the KendoGrid for <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/grid/api/scrolltoitemrequest">Angular</a>, <a target="_blank" href="https://www.telerik.com/aspnet-mvc/documentation/html-helpers/data-management/grid/scrolling/scroll-to-item">ASP.NET MVC</a> and <a target="_blank" href="https://www.telerik.com/kendo-jquery-ui/documentation/api/javascript/ui/grid/methods/scrolltoitem">jQuery</a>.</p><p>While the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">React Data Grid</a> and the <a target="_blank" href="https://demos.telerik.com/blazor-ui/grid/overview">Blazor Grid</a> don&rsquo;t have <code>scrollToItem</code>, you can get some of the same behavior by accessing a row&rsquo;s underlying HTML <code>&lt;tr&gt;</code> element and using the DOM&rsquo;s <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView"><code>scrollIntoView</code> method</a> &hellip; but the Kendo <code>scrollToItem</code> method is simpler to use and, even better, ties into your data model rather than the page&rsquo;s HTML. The only real limitation on <code>scrollToItem</code> is that it doesn&rsquo;t work with <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/html-helpers/data-management/grid/grouping/group-paging">grouped paging</a>.</p><h2 id="configuring-the-project">Configuring the Project</h2><p>To demonstrate using <code>scrollToItem</code>, I used the KendoGrid for ASP.NET MVC in an ASP.NET Core 8 Razor Page project. My first step was to add the Telerik.UI.for.AspNet.Core and Microsoft.AspNetCore.Mvc.NewtonSoft.Json NuGet packages to my project. After that, in the project&rsquo;s Program.cs file, I added these statements before the <code>builder.Build()</code> statement:</p><pre class=" language-csharp"><code class="prism  language-csharp">builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddKendo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddMvc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AddNewtonsoftJson</span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
   options<span class="token punctuation">.</span>SerializerSettings<span class="token punctuation">.</span>ContractResolver <span class="token operator">=</span>
    <span class="token keyword">new</span> <span class="token class-name">DefaultContractResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>I generated a <code>List</code> of <code>Product</code> objects for my grid to display in an action method in an action method I called <code>Get</code> in a controller I called <code>ProductsManager</code>. To support retrieving data from my grid through that action method, I also added this statement after the Program.cs file&rsquo;s <code>builder.Build()</code> statement:</p><pre class=" language-csharp"><code class="prism  language-csharp">app<span class="token punctuation">.</span><span class="token function">MapControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>To complete configuring the project, I added these <code>&lt;link&gt;</code> and <code>&lt;script&gt;</code> tags to my project&rsquo;s _Layout.cshtml file:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~/lib/bootstrap/dist/css/bootstrap.min.css<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~/css/site.css<span class="token punctuation">"</span></span> <span class="token attr-name">asp-append-version</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~/ScrollItem.styles.css<span class="token punctuation">"</span></span> <span class="token attr-name">asp-append-version</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~/lib/bootstrap/dist/js/bootstrap.bundle.min.js<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~/js/site.js<span class="token punctuation">"</span></span> <span class="token attr-name">asp-append-version</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://kendo.cdn.telerik.com/themes/12.0.1/bootstrap/bootstrap-main.css<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://code.jquery.com/jquery-3.7.0.min.js<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://kendo.cdn.telerik.com/2025.3.1002/js/kendo.all.min.js<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://kendo.cdn.telerik.com/2025.3.1002/js/kendo.aspnetmvc.min.js<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre><h2 id="configuring-the-grid">Configuring the Grid</h2><p>For my case study, I set up a grid with a very short window:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;kendo-grid name="productGrid" height="250"&gt;

&lt;/kendo-grid&gt;
</code></pre><p>Inside that kendo-grid element, I nested a <code>datasource</code> element that would retrieve 20 rows into that short space, so that I&rsquo;ll have lots of rows in the grid&rsquo;s page that weren&rsquo;t &ldquo;on the screen&rdquo; to play with. Inside the <code>datasource</code> element, I put a <code>schema</code> element, with a <code>model</code> element inside it.</p><p>That <code>model</code> element is key to having the <code>scrollToItem</code> method work: Users can ask for a row in the grid to be displayed by using the property specified in the <code>model</code> element&rsquo;s <code>id</code> attribute. The property you use here must be unique for each row on the page so, nine times out of ten, the property you pick is going to be the property on your object&rsquo;s that holds the corresponding table&rsquo;s primary key.</p><p>In my case, for the <code>Product</code> object I&rsquo;m displaying in each row of my grid, I used the object&rsquo;s <code>ProductID</code> property:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;datasource type="DataSourceTagHelperType.Ajax" page-size="20"&gt;
  &lt;schema&gt;
    &lt;model id="ProductID"&gt;
    &lt;/model&gt;
  &lt;/schema&gt;
</code></pre><p>The <code>scrollToItem</code> method works as long as scrolling is enabled for the grid, in either of the grid&rsquo;s <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/api/kendo.mvc.taghelpers/gridscrollablesettingstaghelper">virtual or endless scrolling modes</a>.</p><p>That means that you don&rsquo;t need to include a <code>scrollable</code> element in your grid&rsquo;s definition (the defaults are fine), but, if you do include the element:</p><ul><li>If the <code>enabled</code> attribute is present, it must be set to <code>true</code></li><li>If either of the <code>virtual</code> or <code>endless</code> attributes are present, at lest one of them must be set to <code>true</code></li></ul><p>Inside the <code>datasource</code> element, you&rsquo;ll also need a <code>transport</code> element to specify where your data is coming from. (I used the <code>Url</code> object&rsquo;s <code>Action</code> method to call my <code>Get</code> action method in my <code>ProductsManager</code> controller.):</p><pre class=" language-razor"><code class="prism  language-razor">  &lt;/schema&gt;
  &lt;transport&gt;
    &lt;read url='@Url.Action("Get", "ProductsManager")'  /&gt;
  &lt;/transport&gt;
&lt;/datasource&gt;
</code></pre><p>Following the <code>datasource</code> element, you define the columns you want displayed in the grid (and you don&rsquo;t need to include the column that you&rsquo;ll use to pull the right row onto the screen):</p><pre class=" language-razor"><code class="prism  language-razor">&lt;columns&gt;
  &lt;column field="ProductName" title="Name" /&gt;
  &lt;column field="Category" title="Category" /&gt;
  &lt;column field="Price" title="Price" /&gt;
&lt;/columns&gt;
</code></pre><h2 id="using-scrolltoitem">Using scrollToItem</h2><p>With all of that in place, you can write some JavaScript code to pull a row onto the screen. For this case study, I added a textbox to allow the user to enter the ProductID of the item they wanted to pull onto the screen. In the textbox&rsquo;s <code>onblur</code> event I called a function I named <code>MoveToItem</code>, passing a reference to the textbox to that function:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>br</span><span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">onblur</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MoveToItem(this)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>br</span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>To support calling the grid&rsquo;s <code>scrollToItem</code> method, I need a reference to the grid. In any real application, I&rsquo;ll probably need that reference multiple times so, rather than keep retrieving that reference, I declared a global variable to hold the reference and loaded that variable from a jQuery <code>onready</code> function:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token operator">&lt;</span>script<span class="token operator">&gt;</span>
    <span class="token keyword">let</span> grid<span class="token punctuation">;</span>
    <span class="token function">$</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        grid <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#productGrid"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">kendoGrid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Below that function, I added my <code>MoveToItem</code> function. In that function, I called the grid&rsquo;s <code>scrollToItem</code> method, passing the method the <code>value</code> property of the textbox I passed to the function (after checking that the textbox has something in it <code>value</code> property, of course):</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">MoveToItem</span> <span class="token operator">=</span> <span class="token punctuation">(</span>txt<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> 
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>txt<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        grid<span class="token punctuation">.</span><span class="token function">scrollToItem</span><span class="token punctuation">(</span>txt<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><p>When that command executes, the row with the matching <code>ProductID</code> becomes the top row in the grid (assuming there are enough rows to fill the rest of the grid&mdash;if there aren&rsquo;t enough rows, the grid will display the last rows in the grid, including the row with the matching <code>ProductID</code>).</p><h2 id="fetching-new-data">Fetching New Data</h2><p>That is, as long as the user passes a valid <code>ProductID</code> or that <code>ProductID</code> is on the current page. What do you do when that isn&rsquo;t true?</p><p>The best solution for ensuring only valid <code>ProductID</code>s are passed to your function is, of course, to have the user select a <code>ProductID</code> from a dropdown list rather than entering whatever they want into a textbox. A dropdown list would also let the user select a product name from the dropdown list but allow you to pass a <code>ProductID</code> to your function.</p><p>A typical dropdown list would look like this:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">onchange</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MoveToItem(this)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>top<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Select a product<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>A1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Chai Tea<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">&gt;</span></span>
 ...
</code></pre><p>And the JavaScript function to work with it would look like this:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">MoveToItem</span> <span class="token operator">=</span> <span class="token punctuation">(</span>sel<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">alert</span><span class="token punctuation">(</span>sel<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
  grid<span class="token punctuation">.</span><span class="token function">scrollToItem</span><span class="token punctuation">(</span>sel<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>But what if, for some reason, a dropdown list isn&rsquo;t an option or the user&rsquo;s entry is valid but the object isn&rsquo;t in the grid&rsquo;s current page? Provided you have virtual scrolling enabled, you can handle both of those scenarios by passing a callback function as the second parameter to the <code>scrollToItem</code> method.</p><p>The callback function is only called if <code>scrollToItem</code> can&rsquo;t scroll to the item specified in its first parameter. So, for the &ldquo;invalid choice&rdquo; scenario, you can use that callback function to provide an error message. Basic code would look like this:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">MoveToItem</span> <span class="token operator">=</span> <span class="token punctuation">(</span>txt<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> 
<span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>txt<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    grid<span class="token punctuation">.</span><span class="token function">scrollToItem</span><span class="token punctuation">(</span>txt<span class="token punctuation">.</span>value<span class="token punctuation">,</span> 
    <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">&lt;</span>&hellip;check <span class="token keyword">for</span> invalid value&hellip;<span class="token operator">&gt;</span><span class="token punctuation">)</span> 
        <span class="token punctuation">{</span>
          <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"Invalid ProductID: "</span> <span class="token operator">+</span> txt<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>However, that callback function is also passed an object that you can use to pull a row not in the current grid page onto the screen &hellip; provided you know the row&rsquo;s position in your virtual list. All you need to do (after you determine the row&rsquo;s position in your virtual list) is pass the row&rsquo;s position to the object&rsquo;s <code>success</code> method.</p><p>This sample code doesn&rsquo;t attempt to find the right object that but just pulls the first row back onto the screen when the requested object isn&rsquo;t found on the current page:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">MoveToItem</span> <span class="token operator">=</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> 
<span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    grid<span class="token punctuation">.</span><span class="token function">scrollToItem</span><span class="token punctuation">(</span>txt<span class="token punctuation">.</span>value<span class="token punctuation">,</span> 
      <span class="token keyword">function</span> <span class="token punctuation">(</span>o<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">&lt;</span>&hellip;check <span class="token keyword">for</span> invalid value&hellip;<span class="token operator">&gt;</span><span class="token punctuation">)</span> 
      <span class="token punctuation">{</span>
        <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"Invalid ProductID: "</span> <span class="token operator">+</span> txt<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">else</span>
      <span class="token punctuation">{</span>
        o<span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In a GUI, your first goal is to provide your user whatever they need right in front of them. The <code>scrollToItem</code> method is the tool that enables you to do that while letting the user jump straight to the row they want.</p><hr /><p>Explore all the grids Progress provides with a free 30-day trial of the Progress Telerik DevCraft bundle:</p><p><a target="_blank" href="https://www.telerik.com/devcraft">Try Telerik DevCraft</a></p><img src="https://feeds.telerik.com/link/23052/17260966.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:bb4618ee-4e41-4708-9846-e5b913535da2</id>
    <title type="text">Building Resilient APIs with the Retry Pattern</title>
    <summary type="text">Learn how to implement the Retry Pattern in ASP.NET Core to create more reliable web APIs.</summary>
    <published>2026-01-14T16:26:26Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17253334/building-resilient-apis-retry-pattern"/>
    <content type="text"><![CDATA[<p><span class="featured">Sending and receiving data is the essence of any web API. But what happens when something goes wrong and communication fails? To avoid unexpected issues and enable greater resilience, we can use the Retry Pattern. Learn how to implement this pattern in ASP.NET Core to make your APIs more reliable.</span></p><p>The Retry Pattern helps developers create APIs that are prepared for adverse situations when communicating with external services, such as databases and even other APIs.</p><p>In this post, we&rsquo;ll explore the main challenges faced when working with distributed applications that communicate with each other and discover how the Retry Pattern can be used as a resilience strategy to deal with common failures in these scenarios.</p><p>We&rsquo;ll also see how to implement the Retry Pattern in an ASP.NET Core application using a retry pipeline through the Polly library.</p><h2 id="common-integration-issues">Common Integration Issues</h2><p>Any application that communicates with APIs or external services is susceptible to the risk of communication failures, including timeouts, temporary unavailability, network errors or request limits. While often underestimated, these issues are common and can compromise the user experience and system reliability.</p><p>In this context, we can highlight the following issues that modern systems face when handling integrations:</p><h3 id="temporary-network-errors">Temporary Network Errors</h3><p>When accessing the network, 100% availability is not always guaranteed. Packets can be lost, connections can drop and providers can experience momentary instability. For example, a payment API takes longer than expected to respond, and its call times out (<code>TimeoutException</code>). In practice, the transaction may have been processed, but the application did not receive confirmation.</p><h3 id="temporary-unavailability-of-external-services">Temporary Unavailability of External Services</h3><p>Services may undergo maintenance, experience overload or be offline for a few minutes. For example, an ecommerce website queries a pricing API to update a product&rsquo;s price. If the API is unavailable, the system may fail and display the outdated price to the customer, causing harm and stress for both the consumer and the company selling the product.</p><h3 id="inconsistent-or-invalid-data">Inconsistent or Invalid Data</h3><p>There is always a risk that external APIs may return incomplete data, in an unexpected format or with business errors. For example, a registration API returns addresses without a ZIP code or with invalid characters, breaking the system&rsquo;s internal validations.</p><p>These are just some of the many scenarios in which an API can fail to perform its intended task, resulting in significant problems and losses for the development team and the company. Fortunately, some solutions help mitigate these and other risks by automatically retrying a failed operation in the hope that it will succeed on a second attempt. Next, we&rsquo;ll explore the Retry Pattern, which stands out as a solution for dealing with these temporary failures.</p><h2 id="understanding-the-retry-pattern">Understanding the Retry Pattern</h2><p>The Retry Pattern is a resilience pattern whose central idea is to retry a failed operation, rather than simply giving up on the first attempt. This is useful and, in many cases, indispensable, as distributed systems tend to experience temporary problems, such as momentary network instability or even server overload. Therefore, when a communication or processing failure occurs between two services, it is advisable to at least try again.</p><p>These failures are usually corrected after a period of time. If the action that triggered the failure is retried after a reasonable delay, it is very likely to be successful. For example, imagine a payment service is momentarily overloaded and returns a &ldquo;Service Unavailable (503)&rdquo; error. In this case, it does not mean that the service is permanently down, but rather that it was unable to process the request at that time. If the application retries after a few seconds, it is very likely that the payment will be completed successfully.</p><p>Scenarios like this show that the Retry Pattern can be extremely useful, as it helps prevent temporary glitches from resulting in significant losses, maintaining a stable user experience and reducing the need for potential manual intervention.</p><p>The image below demonstrates two scenarios, with and without the use of the Retry Pattern.</p><p><img title="using retry pattern" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/using-retry-pattern.png?sfvrsn=39007a84_2" alt="Using retry pattern" /></p><p><img title="without using retry pattern" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/without-using-retry-pattern.png?sfvrsn=1463e64e_2" alt="Without using retry pattern" /></p><h2 id="implementing-retry-pattern-in-asp.net-core">Implementing Retry Pattern in ASP.NET Core</h2><p>To implement the Retry Pattern, we&rsquo;ll consider a scenario where a product catalog API needs to access another API to retrieve product image data. The premise is that we can&rsquo;t leave the client without the product image. So, even if the first request fails, we have to try again.</p><p>So, first, we&rsquo;ll create a simple product catalog API and implement a retry policy and a secondary API to return product images. Finally, we&rsquo;ll force an error on the first two attempts and a success on the third, to validate that the policy is working.</p><p>You can access the complete source code in this GitHub repository: <a href="https://github.com/zangassis/PollyProducts">PollyProducts source code</a>.</p><h2 id="-polly-for-resilience"> Polly for Resilience</h2><p><a href="https://www.pollydocs.org/index.html">Polly</a> is a library focused on resilience and fault tolerance for .NET applications.</p><p>It is widely known and helps systems handle temporary failures when calling external APIs, databases, network services and more, allowing developers to implement resilience policies to anticipate temporary unavailability scenarios.</p><p>In addition to basic features like automatic operation retry, Polly offers advanced features like circuit breaker, which temporarily stops new calls after a consecutive number of failures, avoiding overloading unavailable services. Another important feature is fallback, which defines an alternative action when the main operation fails, for example, returning cached data if the API is down.</p><h2 id="creating-the-product-catalog-api">Creating the Product Catalog API</h2><p>So, first, let&rsquo;s create the API that will make requests to the secondary API to return data from the product catalog. This is where we&rsquo;ll create a pipeline with a retry policy.</p><p>To create the base application, you can run the following commands in your terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o PollyProducts
</code></pre><p>Then, run the following commands to download and install the Polly NuGet Packages:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Polly

dotnet add package Microsoft.Extensions.Http.Polly
</code></pre><p>Next, let&rsquo;s create the retry policy class. For that, create a new folder called &ldquo;Policies&rdquo; and, inside it, create the following class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Polly<span class="token punctuation">;</span>
<span class="token keyword">using</span> Polly<span class="token punctuation">.</span>Extensions<span class="token punctuation">.</span>Http<span class="token punctuation">;</span>
<span class="token keyword">using</span> Polly<span class="token punctuation">.</span>Retry<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Net<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> PollyProducts<span class="token punctuation">.</span>Policies<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ContextRetryPolicy</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> ResiliencePipeline<span class="token operator">&lt;</span>HttpResponseMessage<span class="token operator">&gt;</span> <span class="token function">CreatePipeline</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> builder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ResiliencePipelineBuilder</span><span class="token operator">&lt;</span>HttpResponseMessage<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">AddRetry</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RetryStrategyOptions</span><span class="token operator">&lt;</span>HttpResponseMessage<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            MaxRetryAttempts <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span>

            DelayGenerator <span class="token operator">=</span> args <span class="token operator">=</span><span class="token operator">&gt;</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> delay <span class="token operator">=</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">Pow</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span> args<span class="token punctuation">.</span>AttemptNumber<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
                <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ValueTask</span><span class="token operator">&lt;</span>TimeSpan<span class="token operator">?</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>delay<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>

            ShouldHandle <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PredicateBuilder</span><span class="token operator">&lt;</span>HttpResponseMessage<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token generic-method function">Handle<span class="token punctuation">&lt;</span>HttpRequestException<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">HandleResult</span><span class="token punctuation">(</span>response <span class="token operator">=</span><span class="token operator">&gt;</span>
                    <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span>response<span class="token punctuation">.</span>StatusCode <span class="token operator">&gt;=</span> <span class="token number">500</span> <span class="token operator">||</span> 
                    response<span class="token punctuation">.</span>StatusCode <span class="token operator">==</span> HttpStatusCode<span class="token punctuation">.</span>RequestTimeout
                <span class="token punctuation">)</span><span class="token punctuation">,</span>

            OnRetry <span class="token operator">=</span> args <span class="token operator">=</span><span class="token operator">&gt;</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> reason <span class="token operator">=</span> args<span class="token punctuation">.</span>Outcome<span class="token punctuation">.</span>Exception<span class="token operator">?</span><span class="token punctuation">.</span>Message
                             <span class="token operator">?</span><span class="token operator">?</span> args<span class="token punctuation">.</span>Outcome<span class="token punctuation">.</span>Result<span class="token operator">?</span><span class="token punctuation">.</span>StatusCode<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                             <span class="token operator">?</span><span class="token operator">?</span> <span class="token string">"Unknown reason"</span><span class="token punctuation">;</span>

                Console<span class="token punctuation">.</span>ForegroundColor <span class="token operator">=</span> ConsoleColor<span class="token punctuation">.</span>Yellow<span class="token punctuation">;</span>
                Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"[Retry {args.AttemptNumber}] Retrying in {args.RetryDelay.TotalSeconds}s due to {reason}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                Console<span class="token punctuation">.</span><span class="token function">ResetColor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                <span class="token keyword">return</span> <span class="token keyword">default</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s analyze the code above. In it, we use <code>ResiliencePipelineBuilder&lt;HttpResponseMessage&gt;</code> to implement the pipeline concept available in Polly 8. This allows the creation of configurable flows that can seamlessly combine multiple strategies such as retry, circuit breaker, fallback and timeout. In this case, the pipeline specializes in working with HTTP responses.</p><p>The <code>AddRetry()</code> configuration adds a retry policy. We define three attempts (<code>MaxRetryAttempts = 3</code>) and an exponential interval between them, where: first attempt: 4&sup1; = 4s, second attempt: 4&sup2; = 16s, and third attempt: 4&sup3; = 64s. This avoids bombarding the service with requests in succession and gives it time to recover.</p><p>Furthermore, we use the <code>ShouldHandle</code> to define when the retry should occur, covering two scenarios: network exceptions (<code>HttpRequestException</code>) and server errors (5xx)/timeouts (408). This avoids unnecessary retries on client errors (4xx), which are usually not temporary.</p><p>Finally, every time the retry occurs, we print a colored message to the console, which we will use later to verify the retry behavior in action.</p><p>The next step is to configure the Program class and create the endpoint to request, so in the Program class, add the following code:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> PollyProducts<span class="token punctuation">.</span>Policies<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddHttpClient</span><span class="token punctuation">(</span><span class="token string">"LocalProductImageClient"</span><span class="token punctuation">,</span> client <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    client<span class="token punctuation">.</span>BaseAddress <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Uri</span><span class="token punctuation">(</span><span class="token string">"http://localhost:5005/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> retryPipeline <span class="token operator">=</span> ContextRetryPolicy<span class="token punctuation">.</span><span class="token function">CreatePipeline</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/products/{id}/"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> IHttpClientFactory httpClientFactory<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> client <span class="token operator">=</span> httpClientFactory<span class="token punctuation">.</span><span class="token function">CreateClient</span><span class="token punctuation">(</span><span class="token string">"LocalProductImageClient"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">int</span> attempt <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

    HttpResponseMessage response <span class="token operator">=</span> <span class="token keyword">await</span> retryPipeline<span class="token punctuation">.</span><span class="token function">ExecuteAsync</span><span class="token punctuation">(</span><span class="token keyword">async</span> token <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">{</span>
        attempt<span class="token operator">++</span><span class="token punctuation">;</span>

        Console<span class="token punctuation">.</span>ForegroundColor <span class="token operator">=</span> ConsoleColor<span class="token punctuation">.</span>Cyan<span class="token punctuation">;</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"[Attempt {attempt}] Requesting /photos/{id}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        Console<span class="token punctuation">.</span><span class="token function">ResetColor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> resp <span class="token operator">=</span> <span class="token keyword">await</span> client<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span>$<span class="token string">"/photos/{id}"</span><span class="token punctuation">,</span> token<span class="token punctuation">)</span><span class="token punctuation">;</span>

        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"[Attempt {attempt}] Response: {(int)resp.StatusCode} {resp.StatusCode}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> resp<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>response<span class="token punctuation">.</span>IsSuccessStatusCode<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Problem</span><span class="token punctuation">(</span><span class="token string">"Image service is temporarily unavailable."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">var</span> content <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token function">ReadAsStringAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span><span class="token keyword">new</span>
    <span class="token punctuation">{</span>
        ProductId <span class="token operator">=</span> id<span class="token punctuation">,</span>
        ImageInfo <span class="token operator">=</span> content<span class="token punctuation">.</span><span class="token function">Substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">Min</span><span class="token punctuation">(</span>content<span class="token punctuation">.</span>Length<span class="token punctuation">,</span> <span class="token number">120</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"..."</span><span class="token punctuation">,</span>
        RetrievedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
        Attempts <span class="token operator">=</span> attempt
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the <code>ContextRetryPolicy</code> class, we created a pipeline with a retry policy using Polly. Now, let&rsquo;s see this policy in action.</p><p>In the code above, we use the API to consume another local HTTP service and retrieve product images, which we will create later.</p><p>First, we register the HttpClient (<code>LocalProductImageClient</code>) pointing to the base URL of the image service. Then, the application creates the pipeline using <code>ContextRetryPolicy.CreatePipeline()</code>. Within the pipeline, the retry policy is created, which will be used to encapsulate the call to the external service, so that if something fails, the system will retry before giving up.</p><p>Thus, whenever the <code>/products/{id}/images</code> endpoint is called, the application obtains an HttpClient from the factory and initiates the request to <code>/photos/{id}</code> on the external service.</p><p>With each retry attempt, the attempt number, the requested route and the response result are printed to the console. This will be useful for understanding real-time retry behavior, allowing us to see when Polly intervenes when testing the application.</p><p>If, after all attempts, the response is still unsuccessful, the API will return an error indicating that the image service is temporarily unavailable. Otherwise, it reads the response content, extracts a snippet for display, and returns a JSON object containing the product ID, a summary of the response, the time of the request and the number of attempts made.</p><h3 id="creating-the-product-image-api">Creating the Product Image API</h3><p>The Product Image API will simulate the return of product image data. We&rsquo;ll use it to test the retry policy in the Catalog API. So, to create the base application, run the command below:</p><pre class=" language-csharp"><code class="prism  language-csharp">dotnet <span class="token keyword">new</span> <span class="token class-name">web</span> <span class="token operator">-</span>o BaseImages
</code></pre><p>Then, in the Program class, add the following code:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/photos/{id}"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span><span class="token keyword">new</span>
    <span class="token punctuation">{</span>
        Id <span class="token operator">=</span> id<span class="token punctuation">,</span>
        Url <span class="token operator">=</span> $<span class="token string">"https://samplepics.photos/id/{id}/200/200"</span><span class="token punctuation">,</span>
        Title <span class="token operator">=</span> $<span class="token string">"Photo {id}"</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">"http://localhost:5005"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the code above, we define a minimal API that acts as a local image service.</p><p>First, we define a GET route (<code>/photos/{id}</code>) that receives an image identifier and returns a mock response in JSON format. Finally, the application runs on a fixed port (<a href="http://localhost:5005">http://localhost:5005</a>). Setting the port is important in this context because it keeps the base address used in the main API&rsquo;s HttpClient stable.</p><p>So, everything we needed to implement the retry policy is ready. Now let&rsquo;s run both APIs and test the possible scenarios.</p><h3 id="-simulating-retries"> Simulating Retries</h3><p>To simulate retries, first run the Catalog API and make a request to the route: <code>https://localhost:PORT/products/1</code> and observe the logs in the console. The first and second attempts will appear in the console:</p><p><img title="first and second attempts" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/first-and-second-attempts.png?sfvrsn=da5a147c_2" alt="First and second attempts" /></p><p>The attempt error occurred because the images API is not enabled. Then run the second API (<code>BaseImage</code>), so now the Catalog API will be able to connect to the Images API, and the third attempt will be successful, as shown in the image below:</p><p><img title="third attempt" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/third-attempt.png?sfvrsn=379638d5_2" alt="Third attempt" /></p><p>Note that the first attempts resulted in an error, but the third managed to connect to the image API and returned the requested data.</p><h2 id="-conclusion-and-next-steps"> Conclusion and Next Steps</h2><p>Scenarios where applications communicate with each other, receiving and sending data, are common in distributed systems. Therefore, it&rsquo;s essential to design a system that verifies this communication actually occurs. This is where the Retry Pattern shines, for the construction of web APIs that are resistant to unexpected failures.</p><p>In this post, we covered the main problems that can occur in distributed applications and how to deal with them by implementing a retry policy with the open-source Polly library for ASP.NET Core.</p><p>And it doesn&rsquo;t end there. Polly has many other powerful features you can explore, such as fallback, which lets you do something else when a request fails, or hedging, which allows you to send multiple requests at once and use the fastest response.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">What&rsquo;s New with APIs in .NET 10: Taking a Look at Real Improvements</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Learn about <a target="_blank" href="https://www.telerik.com/blogs/whats-new-apis-net-10-real-improvements">.NET 10 and C# 14 updates from the perspective of an API developer</a> using a real-world example: an order management API with validation, OpenAPI docs and Entity Framework Core.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17253334.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:7e4ca7c4-6c94-43c4-9c4c-d002818c10a6</id>
    <title type="text">What’s New with APIs in .NET 10: Taking a Look at Real Improvements</title>
    <summary type="text">This post walks through .NET 10 and C# 14 updates from the perspective of an API developer using a real-world example: an order management API with validation, OpenAPI docs and Entity Framework Core.</summary>
    <published>2025-12-31T13:29:02Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17244500/whats-new-apis-net-10-real-improvements"/>
    <content type="text"><![CDATA[<p><span class="featured">This post walks through .NET 10 and C# 14 updates from the perspective of an API developer using a real-world example: an order management API with validation, OpenAPI docs and Entity Framework Core.</span></p><p>It&rsquo;s that time of year: A new version of the .NET platform has shipped. .NET 10 <a target="_blank" href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-10/">landed last month</a> as <a target="_blank" href="https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core">an LTS release</a>, with support through November 2028.</p><p>To complement Jon Hilton&rsquo;s <a target="_blank" href="https://www.telerik.com/blogs/net-10-has-arrived-heres-whats-changed-blazor"><em>.NET 10 Has Arrived&mdash;Here&rsquo;s What Changed for Blazor</em></a> article and Assis Zang&rsquo;s <em><a href="https://www.telerik.com/blogs/whats-new-net-10-aspnet-core" target="_blank">What&rsquo;s New in .NET 10 for ASP.NET Core</a></em>, let&rsquo;s look at .NET 10 improvements through the viewpoint of an API developer.</p><p>Throughout this post, we&rsquo;ll walk through updates using a real-world example: an order management API with validation, OpenAPI docs and Entity Framework Core.</p><p><strong>NOTE</strong>: The examples use Minimal APIs for brevity, but most of these improvements can be used for controller-based APIs as well.</p><h2 id="built-in-validation-for-minimal-apis">Built in Validation for Minimal APIs</h2><p>Before .NET 10, teams building Minimal APIs ended up rolling their own validation. The result? Endpoint code that was more about policing inputs than implementing business logic.</p><p>Here&rsquo;s a simplified example that shows the problem.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">OrderEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">MapOrderEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> <span class="token keyword">group</span> <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/api/orders"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> CreateOrder<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">,</span> GetOrder<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">CreateOrder</span><span class="token punctuation">(</span>
        CreateOrderRequest request<span class="token punctuation">,</span>
        OrderDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>CustomerEmail<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Customer email is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>request<span class="token punctuation">.</span>Items <span class="token keyword">is</span> <span class="token keyword">null</span> <span class="token operator">||</span> request<span class="token punctuation">.</span>Items<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Order must contain at least one item"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> item <span class="token keyword">in</span> request<span class="token punctuation">.</span>Items<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>item<span class="token punctuation">.</span>Quantity <span class="token operator">&lt;</span> <span class="token number">1</span><span class="token punctuation">)</span>
                <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Quantity must be at least 1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>item<span class="token punctuation">.</span>ProductId <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
                <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"Invalid product ID"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span>
        <span class="token punctuation">{</span>
            CustomerEmail <span class="token operator">=</span> request<span class="token punctuation">.</span>CustomerEmail<span class="token punctuation">,</span>
            Items <span class="token operator">=</span> request<span class="token punctuation">.</span>Items<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>i <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token class-name">OrderItem</span>
            <span class="token punctuation">{</span>
                ProductId <span class="token operator">=</span> i<span class="token punctuation">.</span>ProductId<span class="token punctuation">,</span>
                Quantity <span class="token operator">=</span> i<span class="token punctuation">.</span>Quantity
            <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        db<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/api/orders/{order.Id}"</span><span class="token punctuation">,</span> order<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetOrder</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">,</span> OrderDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">FindAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> order <span class="token keyword">is</span> <span class="token keyword">null</span> <span class="token operator">?</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>order<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>There were ways around this: you could use filters, helper methods or third-party validators. Even so, it was frustrating that Minimal APIs didn&rsquo;t have the baked-in validation experience you have with controller-based APIs.</p><p>.NET 10 adds built-in validation support for Minimal APIs. You can enable it with one registration call:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddDbContext<span class="token punctuation">&lt;</span>OrderDbContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Enables built-in validation for Minimal APIs</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Once enabled, ASP.NET Core automatically applies <code>DataAnnotations</code> validation to Minimal API parameters. This includes query, header and request body binding.</p><p>You can also disable validation for a specific endpoint using <code>DisableValidation()</code>, which is handy for internal endpoints or partial updates where you intentionally accept incomplete payloads.</p><p>With validation handled by the framework and the attributes added to our models, endpoints can focus on business logic.</p><h3 id="validation-error-responses">Validation Error Responses</h3><p>When validation fails, ASP.NET Core returns a standardized <code>ProblemDetails</code> response with an <code>errors</code> dictionary.</p><p>A typical response looks like this.</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
  <span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"https://tools.ietf.org/html/rfc9110#section-15.5.1"</span><span class="token punctuation">,</span>
  <span class="token string">"title"</span><span class="token punctuation">:</span> <span class="token string">"One or more validation errors occurred."</span><span class="token punctuation">,</span>
  <span class="token string">"status"</span><span class="token punctuation">:</span> <span class="token number">400</span><span class="token punctuation">,</span>
  <span class="token string">"errors"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"CustomerEmail"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
      <span class="token string">"The CustomerEmail field is required."</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token string">"Items[0].Quantity"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
      <span class="token string">"Quantity must be between 1 and 1000"</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="openapi-3.1-modernizing-api-documentation">OpenAPI 3.1: Modernizing API Documentation</h2><p>.NET 10&rsquo;s built-in OpenAPI document generation supports OpenAPI 3.1 and <a target="_blank" href="https://json-schema.org/draft/2020-12/release-notes">JSON Schema 2020-12</a>. The default OpenAPI version for generated documents is now 3.1.</p><p>OpenAPI 3.1 aligns better with modern JSON schema expectations and improves how tools interpret your schemas.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>OpenApi<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>OpenApi<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddOpenApi</span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token comment">// OpenAPI 3.1 is the default, but you can be explicit</span>
    options<span class="token punctuation">.</span>OpenApiVersion <span class="token operator">=</span> Microsoft<span class="token punctuation">.</span>OpenApi<span class="token punctuation">.</span>OpenApiSpecVersion<span class="token punctuation">.</span>OpenApi3_1<span class="token punctuation">;</span>

    options<span class="token punctuation">.</span><span class="token function">AddDocumentTransformer</span><span class="token punctuation">(</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span> context<span class="token punctuation">,</span> cancellationToken<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">{</span>
        document<span class="token punctuation">.</span>Info <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OpenApiInfo</span>
        <span class="token punctuation">{</span>
            Title <span class="token operator">=</span> <span class="token string">"Order Management API"</span><span class="token punctuation">,</span>
            Version <span class="token operator">=</span> <span class="token string">"v1"</span><span class="token punctuation">,</span>
            Description <span class="token operator">=</span> <span class="token string">"Enterprise order processing system"</span><span class="token punctuation">,</span>
            Contact <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OpenApiContact</span>
            <span class="token punctuation">{</span>
                Name <span class="token operator">=</span> <span class="token string">"API Support"</span><span class="token punctuation">,</span>
                Email <span class="token operator">=</span> <span class="token string">"api-support@company.com"</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>app<span class="token punctuation">.</span>Environment<span class="token punctuation">.</span><span class="token function">IsDevelopment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// JSON at /openapi/v1.json</span>
    app<span class="token punctuation">.</span><span class="token function">MapOpenApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// YAML at /openapi/v1.yaml</span>
    app<span class="token punctuation">.</span><span class="token function">MapOpenApi</span><span class="token punctuation">(</span><span class="token string">"/openapi/{documentName}.yaml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Note the YAML route: in .NET 10, you generate YAML by using a route ending in <code>.yaml</code> or <code>.yml</code>, typically with <code>{documentName}</code> in the path.</p><h3 id="schema-improvements-in-practice">Schema Improvements in Practice</h3><p>OpenAPI 3.0 often expressed nullability using <code>nullable: true</code>.</p><pre class=" language-yaml"><code class="prism  language-yaml"><span class="token key atrule">components</span><span class="token punctuation">:</span>
  <span class="token key atrule">schemas</span><span class="token punctuation">:</span>
    <span class="token key atrule">ShippingAddress</span><span class="token punctuation">:</span>
      <span class="token key atrule">type</span><span class="token punctuation">:</span> object
      <span class="token key atrule">nullable</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
      <span class="token key atrule">properties</span><span class="token punctuation">:</span>
        <span class="token key atrule">street</span><span class="token punctuation">:</span>
          <span class="token key atrule">type</span><span class="token punctuation">:</span> string
          <span class="token key atrule">nullable</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
        <span class="token key atrule">city</span><span class="token punctuation">:</span>
          <span class="token key atrule">type</span><span class="token punctuation">:</span> string
</code></pre><p>OpenAPI 3.1 allows us to use union types:</p><pre class=" language-yaml"><code class="prism  language-yaml"><span class="token key atrule">components</span><span class="token punctuation">:</span>
  <span class="token key atrule">schemas</span><span class="token punctuation">:</span>
    <span class="token key atrule">ShippingAddress</span><span class="token punctuation">:</span>
      <span class="token key atrule">type</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"object"</span><span class="token punctuation">,</span> <span class="token string">"null"</span><span class="token punctuation">]</span>
      <span class="token key atrule">properties</span><span class="token punctuation">:</span>
        <span class="token key atrule">street</span><span class="token punctuation">:</span>
          <span class="token key atrule">type</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"string"</span><span class="token punctuation">,</span> <span class="token string">"null"</span><span class="token punctuation">]</span>
        <span class="token key atrule">city</span><span class="token punctuation">:</span>
          <span class="token key atrule">type</span><span class="token punctuation">:</span> string
</code></pre><p>This tends to play nicer with tooling that relies heavily on JSON Schema semantics such as OpenAPI Generator and NSwag.</p><h2 id="ef-core-10-named-query-filters">EF Core 10: Named Query Filters</h2><p>Global query filters are <a target="_blank" href="https://www.milanjovanovic.tech/blog/named-query-filters-in-ef-10-multiple-query-filters-per-entity">a staple for multi-tenant apps and soft deletes</a>. The classic problem was granularity: <code>IgnoreQueryFilters()</code> disabled all filters at once.</p><p>EF Core 10 introduces <a target="_blank" href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew#named-query-filters">named query filters</a>, so you can selectively disable one filter while keeping another.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token keyword">int</span> _tenantId<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">OrderDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>OrderDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">,</span> ITenantProvider tenant<span class="token punctuation">)</span>
        <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span>
        <span class="token operator">=</span><span class="token operator">&gt;</span> _tenantId <span class="token operator">=</span> tenant<span class="token punctuation">.</span>TenantId<span class="token punctuation">;</span>

    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Order<span class="token operator">&gt;</span> Orders <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Order<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnModelCreating</span><span class="token punctuation">(</span>ModelBuilder modelBuilder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        modelBuilder<span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>Order<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">HasQueryFilter</span><span class="token punctuation">(</span><span class="token string">"SoftDelete"</span><span class="token punctuation">,</span> o <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token operator">!</span>o<span class="token punctuation">.</span>IsDeleted<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">HasQueryFilter</span><span class="token punctuation">(</span><span class="token string">"TenantIsolation"</span><span class="token punctuation">,</span> o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>TenantId <span class="token operator">==</span> _tenantId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Now an admin endpoint can disable soft delete without disabling tenant isolation:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">AdminEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">MapAdminEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> <span class="token keyword">group</span> <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/api/admin"</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">RequireAuthorization</span><span class="token punctuation">(</span><span class="token string">"Admin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">group</span><span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/orders/deleted"</span><span class="token punctuation">,</span> GetDeletedOrders<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithSummary</span><span class="token punctuation">(</span><span class="token string">"Gets deleted orders for the current tenant only"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">GetDeletedOrders</span><span class="token punctuation">(</span>OrderDbContext db<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> deletedOrders <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Orders
            <span class="token punctuation">.</span><span class="token function">IgnoreQueryFilters</span><span class="token punctuation">(</span><span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token string">"SoftDelete"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> 
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>IsDeleted<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> o<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> o<span class="token punctuation">.</span>CustomerEmail<span class="token punctuation">,</span> o<span class="token punctuation">.</span>DeletedAt <span class="token punctuation">}</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>deletedOrders<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This capability is small, but it&rsquo;s exactly the kind of real-world safety improvement that helps prevent cross-tenant data leaks.</p><h2 id="c-14-improvements">C# 14 Improvements</h2><p>.NET 10 ships alongside C# 14. For API developers, a few features immediately reduce boilerplate and improve readability.</p><h3 id="the-field-keyword-eliminate-backing-field-boilerplate">The field Keyword: Eliminate Backing-field Boilerplate</h3><p>C# 14 introduces field-backed properties, where you can reference the compiler-generated backing field directly using the <code>field</code> keyword.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">sealed</span> <span class="token keyword">class</span> <span class="token class-name">Order</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> CustomerEmail
    <span class="token punctuation">{</span>
        <span class="token keyword">get</span><span class="token punctuation">;</span>
        <span class="token keyword">set</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Email cannot be empty."</span><span class="token punctuation">,</span> <span class="token function">nameof</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            field <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">Trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="null-conditional-assignments">Null-conditional Assignments</h3><p>C# 14 allows null-conditional operators (<code>?.</code> and <code>?[]</code>) on the left-hand side of assignments and compound assignments. The right-hand side is evaluated only when the receiver isn&rsquo;t null. This is great for processing patches.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">sealed</span> record <span class="token function">OrderPatchRequest</span><span class="token punctuation">(</span><span class="token keyword">string</span><span class="token operator">?</span> NewStatus<span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token operator">?</span> NewPriority<span class="token punctuation">,</span> <span class="token keyword">string</span><span class="token operator">?</span> NewCity<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">OrderPatchService</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">ApplyPatch</span><span class="token punctuation">(</span>Order<span class="token operator">?</span> order<span class="token punctuation">,</span> OrderPatchRequest<span class="token operator">?</span> patch<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>patch <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

        order<span class="token operator">?</span><span class="token punctuation">.</span>Status <span class="token operator">=</span> patch<span class="token punctuation">.</span>NewStatus <span class="token operator">?</span><span class="token operator">?</span> order<span class="token operator">?</span><span class="token punctuation">.</span>Status<span class="token punctuation">;</span>
        order<span class="token operator">?</span><span class="token punctuation">.</span>Priority <span class="token operator">=</span> patch<span class="token punctuation">.</span>NewPriority <span class="token operator">?</span><span class="token operator">?</span> order<span class="token operator">?</span><span class="token punctuation">.</span>Priority<span class="token punctuation">;</span>
        order<span class="token operator">?</span><span class="token punctuation">.</span>Shipping<span class="token operator">?</span><span class="token punctuation">.</span>City <span class="token operator">=</span> patch<span class="token punctuation">.</span>NewCity <span class="token operator">?</span><span class="token operator">?</span> order<span class="token operator">?</span><span class="token punctuation">.</span>Shipping<span class="token operator">?</span><span class="token punctuation">.</span>City<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p><strong>Note:</strong> Increment/decrement operators (<code>++</code>, <code>--</code>) aren&rsquo;t allowed with null-conditional assignments. Compound assignments like <code>+=</code> are supported.</p><p>For more details on this feature, check out the post <a target="_blank" href="https://www.telerik.com/blogs/write-cleaner-code-csharp-14-null-conditional-assignment-operator"><em>Write Cleaner Code with C# 14&rsquo;s Null-Conditional Assignment Operator</em></a>.</p><h3 id="extension-members-properties-static-members-and-operators">Extension Members: Properties, Static Members and Operators</h3><p>C# 14&rsquo;s headline feature is extension members, which add extension properties, static extension members and even operators using the new <code>extension</code> block syntax.</p><p>Don&rsquo;t you just <em>love</em> the syntax?</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">OrderExtensions</span>
<span class="token punctuation">{</span>
    <span class="token function">extension</span><span class="token punctuation">(</span>Order source<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">public</span> <span class="token keyword">decimal</span> TotalValue <span class="token operator">=</span><span class="token operator">&gt;</span>
            source<span class="token punctuation">.</span>Items<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span>i <span class="token operator">=</span><span class="token operator">&gt;</span> i<span class="token punctuation">.</span>Quantity <span class="token operator">*</span> i<span class="token punctuation">.</span>UnitPrice<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">bool</span> IsHighValue <span class="token operator">=</span><span class="token operator">&gt;</span> source<span class="token punctuation">.</span>TotalValue <span class="token operator">&gt;</span> 1000m<span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">string</span> Summary <span class="token operator">=</span><span class="token operator">&gt;</span>
            $<span class="token string">"Order #{source.Id}: {source.Items.Count} items, ${source.TotalValue:F2} total"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token function">extension</span><span class="token punctuation">(</span>Order<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">public</span> <span class="token keyword">static</span> Order <span class="token function">CreateEmpty</span><span class="token punctuation">(</span><span class="token keyword">int</span> tenantId<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token class-name">Order</span>
        <span class="token punctuation">{</span>
            TenantId <span class="token operator">=</span> tenantId<span class="token punctuation">,</span>
            CreatedAt <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">,</span>
            Items <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>OrderItem<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            Status <span class="token operator">=</span> <span class="token string">"Draft"</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>For more details on extension members, check out <a target="_blank" href="https://www.telerik.com/blogs/extension-properties-csharp-14-game-changing-feature-cleaner-code"><em>Extension Properties: C# 14&rsquo;s Game-Changing Feature for Cleaner Code</em></a>. (I don&rsquo;t get paid by the click, I swear.)</p><h2 id="server-sent-events-simplifying-real-time-updates">Server-Sent Events: Simplifying Real-Time Updates</h2><p>ASP.NET Core in .NET 10 adds a built-in <code>ServerSentEvents</code> result for Minimal APIs, so you can stream updates over a single HTTP connection without manually formatting frames.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">.</span>Runtime<span class="token punctuation">.</span>CompilerServices<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>Http<span class="token punctuation">.</span>HttpResults<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">OrderStatusUpdate</span><span class="token punctuation">(</span><span class="token keyword">int</span> OrderId<span class="token punctuation">,</span> <span class="token keyword">string</span> Status<span class="token punctuation">,</span> <span class="token keyword">string</span> Message<span class="token punctuation">,</span> DateTime Timestamp<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">OrderStreamingEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">MapStreamingEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/api/orders/{id:int}/status-stream"</span><span class="token punctuation">,</span> StreamOrderStatus<span class="token punctuation">)</span>
           <span class="token punctuation">.</span><span class="token function">WithSummary</span><span class="token punctuation">(</span><span class="token string">"Stream real-time order status updates"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> ServerSentEventsResult<span class="token operator">&lt;</span>OrderStatusUpdate<span class="token operator">&gt;</span> <span class="token function">StreamOrderStatus</span><span class="token punctuation">(</span>
        <span class="token keyword">int</span> id<span class="token punctuation">,</span>
        OrderDbContext db<span class="token punctuation">,</span>
        CancellationToken ct<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">async</span> IAsyncEnumerable<span class="token operator">&lt;</span>OrderStatusUpdate<span class="token operator">&gt;</span> <span class="token function">GetUpdates</span><span class="token punctuation">(</span>
            <span class="token punctuation">[</span>EnumeratorCancellation<span class="token punctuation">]</span> CancellationToken cancellationToken<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">string</span><span class="token operator">?</span> last <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>cancellationToken<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> order <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Orders<span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>o <span class="token operator">=</span><span class="token operator">&gt;</span> o<span class="token punctuation">.</span>Id <span class="token operator">==</span> id<span class="token punctuation">,</span> cancellationToken<span class="token punctuation">)</span><span class="token punctuation">;</span>

                <span class="token keyword">if</span> <span class="token punctuation">(</span>order <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">yield</span> <span class="token keyword">return</span> <span class="token keyword">new</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> <span class="token string">"ERROR"</span><span class="token punctuation">,</span> <span class="token string">"Order not found"</span><span class="token punctuation">,</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>

                <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">Equals</span><span class="token punctuation">(</span>order<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> last<span class="token punctuation">,</span> StringComparison<span class="token punctuation">.</span>Ordinal<span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">yield</span> <span class="token keyword">return</span> <span class="token keyword">new</span><span class="token punctuation">(</span>order<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> order<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> $<span class="token string">"Order is now {order.Status}"</span><span class="token punctuation">,</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    last <span class="token operator">=</span> order<span class="token punctuation">.</span>Status<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>

                <span class="token keyword">if</span> <span class="token punctuation">(</span>order<span class="token punctuation">.</span>Status <span class="token keyword">is</span> <span class="token string">"Delivered"</span> or <span class="token string">"Cancelled"</span><span class="token punctuation">)</span>
                    <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>

                <span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span>TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cancellationToken<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> TypedResults<span class="token punctuation">.</span><span class="token function">ServerSentEvents</span><span class="token punctuation">(</span><span class="token function">GetUpdates</span><span class="token punctuation">(</span>ct<span class="token punctuation">)</span><span class="token punctuation">,</span> eventType<span class="token punctuation">:</span> <span class="token string">"order-status"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Client-side consumption is quite simple, too:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token operator">&lt;</span>script<span class="token operator">&gt;</span>
  <span class="token keyword">function</span> <span class="token function">trackOrderStatus</span><span class="token punctuation">(</span>orderId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> es <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EventSource</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`/api/orders/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>orderId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/status-stream`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    es<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> update <span class="token operator">=</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>update<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>update<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">"Delivered"</span> <span class="token operator">||</span> update<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">"Cancelled"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        es<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    es<span class="token punctuation">.</span><span class="token function-variable function">onerror</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"SSE connection error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> es<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
</code></pre><h2 id="should-you-upgrade-to-.net-10">Should You Upgrade to .NET 10?</h2><p>.NET 10 is an LTS release. If you&rsquo;re starting a new project, it&rsquo;s a no-brainer.</p><p>If you&rsquo;re upgrading from .NET 8 or .NET 9, a few things to keep in mind:</p><ul><li><strong>Minimal API validation</strong>: If you&rsquo;ve been hand-rolling validation, .NET 10&rsquo;s <code>AddValidation</code> support can remove a surprising amount of custom code.</li><li><strong>OpenAPI</strong>: Built-in OpenAPI generation defaults to 3.1 and supports YAML endpoints via <code>.yaml</code>/<code>.yml</code> routes.</li><li><strong>EF Core</strong>: Named query filters are a real safety upgrade for multi-tenant apps, and JSON column support continues to improve (including bulk update support).</li><li><strong>C# 14</strong>: You can adopt new features incrementally. Even if you ignore extension members entirely, <code>field</code> and null-conditional assignment will show up in your codebase quickly.</li></ul><p>The upgrade path is generally smooth: change the target in your <code>.csproj</code>, run tests, fix warnings and ship.</p><h2 id="wrapping-up">Wrapping Up</h2><p>.NET 10 delivers meaningful improvements for API developers through thoughtful enhancements rather than revolutionary changes. The combination of built-in Minimal API validation, OpenAPI 3.1 and C# 14 quality-of-life features add up to a more productive and safer development experience.</p><p>Happy coding!</p><h2 id="references">References</h2><h3 id="platform-updates">Platform updates</h3><ul><li><a target="_blank" href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-10/">Announcing .NET 10</a> (Microsoft)</li><li><a target="_blank" href="https://dotnet.microsoft.com/en-us/platform/support/policy">.NET Support Policy</a> (Microsoft)</li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview">What&rsquo;s new in .NET 10</a> (Microsoft Learn)</li></ul><h3 id="asp.net-coreapi-updates">ASP.NET Core/API Updates</h3><ul><li><a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-10.0#validation-support-in-minimal-apis">Minimal APIs quick reference &ndash; validation support</a> (Microsoft Learn)</li><li><a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?view=aspnetcore-10.0&amp;tabs=visual-studio%2Cvisual-studio-code">Generate OpenAPI documents</a> (Microsoft Learn)</li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.typedresults.serversentevents?view=aspnetcore-10.0">TypedResults.ServerSentEvents API docs</a> (Microsoft Learn)</li><li><a target="_blank" href="https://timdeschryver.dev/blog/aspnet-10-validating-incoming-models-in-minimal-apis">ASP.NET 10: Validating incoming models in Minimal APIs</a> (Tim Deschryver)</li><li><a target="_blank" href="https://blog.safia.rocks/2025/11/10/aspnetcore-ten/">And just like that .NET 10 ships tomorrow</a> (Safia Abdalla)</li></ul><h3 id="language-updates">Language Updates</h3><ul><li><a target="_blank" href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew">What&rsquo;s new in EF Core 10</a> (Microsoft Learn)</li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14">What&rsquo;s new in C# 14</a> (Microsoft Learn)</li><li><a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-csharp-14/">Introducing C# 14</a> (Microsoft .NET Blog)</li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/extension-members">Extension members tutorial</a> (Microsoft Learn)</li></ul><img src="https://feeds.telerik.com/link/23052/17244500.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:87399f3c-6e85-4d0e-8174-21ef6db28b2f</id>
    <title type="text">What’s New in .NET 10 for ASP.NET Core</title>
    <summary type="text">.NET 10 brings significant changes to ASP.NET Core. In this post, we'll review some of the main changes, covering practical, everyday examples and the contexts in which they best fit.</summary>
    <published>2025-12-22T18:21:49Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17239934/whats-new-net-10-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">.NET 10 brings significant changes to ASP.NET Core. In this post, we'll review some of the main changes, covering practical, everyday examples and the contexts in which they best fit.</span></p><p>.NET 10 has arrived, bringing important improvements to ASP.NET Core with a focus on efficiency and productivity. In this post, you will see the main new features: from single-file applications written in C#, to new extension methods for LINQ and EF Core, among other updates that promise to further improve your development experience.</p><h2 id="new-c-file-based-applications">1. New C# File-Based Applications</h2><p>Despite support for higher-level instructions (introduced in .NET 6), before .NET 10, a traditional project with a .csproj file was still required to create a C# application.</p><p>With the arrival of .NET 10, a new file-based application model was introduced, allowing the creation of a complete application using only a single file with the .cs extension, without the need for a separate project file.</p><p>Let&rsquo;s look at an example where we need to create an application that calls an external API and returns user data. In this case, we could do the following:</p><ol><li>Create a file with the <code>.cs</code> extension called <code>GetUserData.cs</code>.</li><li>Open the file with your favorite code editor and insert the following code:</li></ol><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">.</span>Net<span class="token punctuation">.</span>Http<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks<span class="token punctuation">;</span>

<span class="token keyword">var</span> url <span class="token operator">=</span> <span class="token string">"https://jsonplaceholder.typicode.com/users/1"</span><span class="token punctuation">;</span>

<span class="token keyword">using</span> <span class="token keyword">var</span> http <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> response <span class="token operator">=</span> <span class="token keyword">await</span> http<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span>

response<span class="token punctuation">.</span><span class="token function">EnsureSuccessStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> content <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token function">ReadAsStringAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"API Response:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This code makes a call to the JSON Placeholder API to return some sample data.</p><ol start="3"><li>Open a terminal where the file is located and run the following command:</li></ol><pre class=" language-bash"><code class="prism  language-bash">dotnet run GetUserData.cs
</code></pre><p>The result of the request is then displayed in the console:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/file-based-app.png?sfvrsn=759812e1_2" title="file based app" alt="File-based app" /></p><p>In this example, we only display the data in the console, but imagine all the possibilities! With just a single .cs file, we can quickly create proofs of concept (POCs), automate small routines or even perform simple data update tasks.</p><p>Another important point to highlight about file-based applications is that they support SDK and NuGet package references through the <code>#sdk</code> and <code>#package</code> directives. Simply add them to the beginning of the file. Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token preprocessor property">#:sdk Microsoft.NET.Sdk.Web</span>
<span class="token preprocessor property">#:package Newtonsoft.Json@13.0.3</span>

<span class="token keyword">using</span> Newtonsoft<span class="token punctuation">.</span>Json<span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/serialize"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> user <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> Id <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> Name <span class="token operator">=</span> <span class="token string">"Alice"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> JsonConvert<span class="token punctuation">.</span><span class="token function">SerializeObject</span><span class="token punctuation">(</span>user<span class="token punctuation">,</span> Formatting<span class="token punctuation">.</span>Indented<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span><span class="token punctuation">;</span>
</code></pre><p>Here, we have another file called <code>SerializeApp.cs</code> which contains simple code to return serialized data. It uses the <code>Microsoft.NET.Sdk.Web</code> SDK and the <code>Newtonsoft.Json@13.0.3</code> package. Note that we didn&rsquo;t install anything, we only used the directives. So, we can run the application with the command <code>dotnet run SerializeApp.cs</code>, make a request to <code>http://localhost:5000/serialize</code> and see that it is 100% functional:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/serialize-app.png?sfvrsn=66e0ac95_2" title="serialize app" alt="Serialize app" /></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/serialize-app-result.png?sfvrsn=a0c5ef92_2" title="serialize app result" alt="Serialize app result" /></p><h2 id="extension-members">2. Extension Members</h2><p>C# 14 is the latest version of C#, released alongside .NET 10, and it brought several improvements to the language, such as extension properties. Extension properties extend the concept of extension methods to add properties to types you don&rsquo;t control, without needing to create wrappers or artificial inheritance. Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">EnumerableExtensions</span>
<span class="token punctuation">{</span>
    <span class="token generic-method function">extension<span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> source<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">public</span> <span class="token keyword">bool</span> IsEmpty <span class="token operator">=</span><span class="token operator">&gt;</span> source<span class="token punctuation">.</span><span class="token function">Any</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token function">Where</span><span class="token punctuation">(</span>Func<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> <span class="token keyword">bool</span><span class="token operator">&gt;</span> predicate<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>predicate <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentNullException</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>predicate<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">return</span> <span class="token function">Iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token function">Iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> item <span class="token keyword">in</span> source<span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">predicate</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">)</span>
                        <span class="token keyword">yield</span> <span class="token keyword">return</span> item<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">public</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> Identity <span class="token operator">=</span><span class="token operator">&gt;</span> Enumerable<span class="token punctuation">.</span><span class="token generic-method function">Empty<span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token keyword">operator</span> <span class="token operator">+</span><span class="token punctuation">(</span>IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> first<span class="token punctuation">,</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> second<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            first<span class="token punctuation">.</span><span class="token function">Concat</span><span class="token punctuation">(</span>second<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><ol><li><code>extension&lt;T&gt;(IEnumerable&lt;T&gt; source)</code></li></ol><p>This block is the new extension scope. Here, <code>source</code> is treated as the receiving object, similar to <code>this</code> in classic extension examples. This means that any property or method defined within this block is perceived as part of <code>IEnumerable&lt;T&gt;</code> itself.</p><ol start="2"><li>Extension property: <code>IsEmpty</code></li></ol><p><code>IsEmpty</code> is declared as an instance property attached to <code>IEnumerable&lt;T&gt;</code>, where <code>source</code> is the collection being extended.</p><p>The property returns <code>true</code> when the sequence has no elements, equivalent to <code>bool empty = !items.Any();</code>, only now exposed as a property instead of a method.</p><p>Example of use:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> items <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// the property is used normally, as a member of the instance</span>
<span class="token keyword">bool</span> empty <span class="token operator">=</span> items<span class="token punctuation">.</span>IsEmpty<span class="token punctuation">;</span> 
</code></pre><ol start="3"><li>Custom Extension Method: Where</li></ol><pre class=" language-sharp"><code class="prism  language-sharp">public IEnumerable&lt;T&gt; Where(Func&lt;T, bool&gt; predicate)
{
...
}
</code></pre><p>This method conceptually overrides the traditional <code>Enumerable Where()</code>, demonstrating that methods can also be declared as extension members within the new block.</p><ol start="4"><li>Static Extension Property: Identity</li></ol><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> Identity <span class="token operator">=</span><span class="token operator">&gt;</span> Enumerable<span class="token punctuation">.</span><span class="token generic-method function">Empty<span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Now it&rsquo;s possible to create static properties within the extension scope. The <code>Identity</code> property always returns an empty string of the corresponding type.</p><p>Example of use:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> empty <span class="token operator">=</span> EnumerableExtensions<span class="token punctuation">.</span>Identity<span class="token punctuation">;</span>
</code></pre><p>Note that static properties still need to be accessed by the type that declares the extension; they don&rsquo;t become direct members of the target type.</p><ol start="5"><li>Extension Operator: <code>operator +</code></li></ol><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token keyword">operator</span> <span class="token operator">+</span><span class="token punctuation">(</span>IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> first<span class="token punctuation">,</span> IEnumerable<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> second<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>

first<span class="token punctuation">.</span><span class="token function">Concat</span><span class="token punctuation">(</span>second<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This is one of the most interesting new features: operators can be defined as extensions, even when the original type doesn&rsquo;t support them. This allows you to write operations like this:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> merged <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token operator">+</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// Result: {1,2,3,4}</span>
</code></pre><aside><hr /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Extension Properties: C# 14&rsquo;s Game-Changing Feature for Cleaner Code</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Learn more about <a target="_blank" href="https://www.telerik.com/blogs/extension-properties-csharp-14-game-changing-feature-cleaner-code">extension properties</a>, the new feature of C# 14 that allows you to use properties in your extension methods.</p></div></div><hr class="u-mb3" /></aside><h2 id="the-field-keyword">3. The Field Keyword</h2><p>The <code>field</code> keyword allows you to write the code for a property without having to manually create the field that stores its value. The compiler creates this field automatically, and <code>field</code> serves as a reference to this internal value.</p><p>Before C# 14, when you wanted to prevent a string property from receiving null, for example, it was necessary to declare a private field and manually write the get and set methods using that field. With <code>field</code>, all of this becomes more straightforward.</p><p>How it was done before:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">int</span> _quantity<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">int</span> Quantity
<span class="token punctuation">{</span>
    <span class="token keyword">get</span> <span class="token operator">=</span><span class="token operator">&gt;</span> _quantity<span class="token punctuation">;</span>
    <span class="token keyword">set</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">value</span> <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentOutOfRangeException</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Quantity cannot be negative."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        _quantity <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>And now, using field:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">int</span> Quantity
<span class="token punctuation">{</span>
    <span class="token keyword">get</span><span class="token punctuation">;</span>
    <span class="token keyword">set</span> <span class="token operator">=</span><span class="token operator">&gt;</span> field <span class="token operator">=</span> <span class="token keyword">value</span> <span class="token operator">&gt;=</span> <span class="token number">0</span>
        <span class="token operator">?</span> <span class="token keyword">value</span>
        <span class="token punctuation">:</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentOutOfRangeException</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Quantity cannot be negative."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>You use it exactly like any normal property.</p><p>Setting a valid value:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> item <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Product</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

item<span class="token punctuation">.</span>Quantity <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token comment">// OK</span>
</code></pre><p>Setting an invalid value:</p><pre class=" language-csharp"><code class="prism  language-csharp">item<span class="token punctuation">.</span>Quantity <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">5</span><span class="token punctuation">;</span> <span class="token comment">// throws ArgumentOutOfRangeException</span>
</code></pre><h2 id="assignment-using-null-conditional">4. Assignment Using Null-Conditional</h2><p>Starting with C# 14, you can assign values using the null-conditional operator <code>?</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp">order<span class="token operator">?</span><span class="token punctuation">.</span>City <span class="token operator">=</span> <span class="token function">Normalize</span><span class="token punctuation">(</span>city<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Before .NET 10, to perform a null check with assignment, you needed to do this:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">if</span> <span class="token punctuation">(</span>order <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    order<span class="token punctuation">.</span>City <span class="token operator">=</span> <span class="token function">Normalize</span><span class="token punctuation">(</span>city<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//or</span>

order <span class="token operator">=</span> order <span class="token keyword">is</span> <span class="token keyword">null</span> 
    <span class="token operator">?</span> <span class="token keyword">null</span> 
    <span class="token punctuation">:</span> <span class="token punctuation">(</span>order<span class="token punctuation">.</span>City <span class="token operator">=</span> <span class="token function">Normalize</span><span class="token punctuation">(</span>city<span class="token punctuation">)</span><span class="token punctuation">,</span> order<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h2 id="minimal-api-built-in-validation">5. Minimal API Built-in Validation</h2><p>In .NET 10, Minimal APIs have built-in validation, eliminating the need for external libraries or manual <code>if</code> blocks to validate DTOs.</p><p>In integrated validation, the model sent by the client is automatically validated as soon as it reaches the endpoint, using validation attributes from the <code>System.ComponentModel.DataAnnotations</code> namespace. If there are errors in the process, .NET automatically returns <code>400 Bad Request</code> with details of the problem.</p><p>To implement this, simply do the following:</p><p><strong>Program class</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/users"</span><span class="token punctuation">,</span>
    <span class="token punctuation">(</span>CreateUserRequest user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        TypedResults<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/users/{user.Username}"</span><span class="token punctuation">,</span> user<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><strong>Data Transfer Object (DTO)</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateUserRequest</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span>Required<span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">MinLength</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Username <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>Required<span class="token punctuation">]</span>
    <span class="token punctuation">[</span>EmailAddress<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Range</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">120</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Age <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that to implement the new integrated validation, we use the extension method <code>builder.Services.AddValidation();</code>. So if we execute the route POST - <code>/users</code> with the following body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
    <span class="token string">"username"</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
    <span class="token string">"email"</span><span class="token punctuation">:</span> <span class="token string">"wrong-email"</span><span class="token punctuation">,</span>
    <span class="token string">"age"</span><span class="token punctuation">:</span> <span class="token operator">-</span><span class="token number">1</span>
<span class="token punctuation">}</span>
</code></pre><p>We will receive the response below:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
    <span class="token string">"title"</span><span class="token punctuation">:</span> <span class="token string">"One or more validation errors occurred."</span><span class="token punctuation">,</span>
    <span class="token string">"errors"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
        <span class="token string">"Username"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token string">"The Username field is required."</span>
        <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string">"Email"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token string">"The Email field is not a valid e-mail address."</span>
        <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string">"Age"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token string">"The field Age must be between 1 and 120."</span>
        <span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>You can also disable built-in validation on endpoints using the <code>.DisableValidation();</code> extension method:</p><pre class=" language-csharp"><code class="prism  language-csharp">
app<span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/users"</span><span class="token punctuation">,</span>
    <span class="token punctuation">(</span>CreateUserRequest user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        TypedResults<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/users/{user.Username}"</span><span class="token punctuation">,</span> user<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">DisableValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h2 id="new-methods-for-extending-ef-core-10">6. New Methods for Extending EF Core 10</h2><p>.NET 10 brought some improvements to Entity Framework Core 10. Among them are the new extension methods, which always help to shorten the path. Let&rsquo;s check out some of them.</p><ol><li><code>LeftJoin()</code> and <code>RightJoin()</code></li></ol><p>It is now possible to directly use <code>.LeftJoin(...)</code> and <code>.RightJoin(...)</code> in LINQ queries with EF, instead of having to use the old pattern with <code>GroupJoin</code> + <code>DefaultIfEmpty()</code>. EF Core provider translates LEFT JOIN / RIGHT JOIN in SQL.</p><p>The examples below demonstrate how to use both methods:</p><p><strong>LeftJoin</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> req <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CreateUserRequest</span>
        <span class="token punctuation">{</span>
            Username <span class="token operator">=</span> <span class="token string">"john"</span><span class="token punctuation">,</span>
            Email <span class="token operator">=</span> <span class="token string">"john@email.com"</span><span class="token punctuation">,</span>
            Age <span class="token operator">=</span> <span class="token number">22</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> requested <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> req <span class="token punctuation">}</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> result <span class="token operator">=</span> requested
            <span class="token punctuation">.</span><span class="token function">LeftJoin</span><span class="token punctuation">(</span>
                db<span class="token punctuation">.</span>Users<span class="token punctuation">,</span>
                req <span class="token operator">=</span><span class="token operator">&gt;</span> req<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
                user <span class="token operator">=</span><span class="token operator">&gt;</span> user<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
                <span class="token punctuation">(</span>req<span class="token punctuation">,</span> user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> Requested <span class="token operator">=</span> req<span class="token punctuation">,</span> ExistingUser <span class="token operator">=</span> user <span class="token punctuation">}</span>
            <span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Even if there is no user in the database, the item on the left (<code>CreateUserRequest</code>) always appears.</p><p><strong>RightJoin</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> requests <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>CreateUserRequest<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                Username <span class="token operator">=</span> <span class="token string">"bob"</span><span class="token punctuation">,</span>
                Email <span class="token operator">=</span> <span class="token string">"bob@mail.com"</span><span class="token punctuation">,</span>
                Age <span class="token operator">=</span> <span class="token number">22</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> result <span class="token operator">=</span> requests
            <span class="token punctuation">.</span><span class="token function">RightJoin</span><span class="token punctuation">(</span>
                db<span class="token punctuation">.</span>Users<span class="token punctuation">,</span>
                req <span class="token operator">=</span><span class="token operator">&gt;</span> req<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
                user <span class="token operator">=</span><span class="token operator">&gt;</span> user<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
                <span class="token punctuation">(</span>req<span class="token punctuation">,</span> user<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> Request <span class="token operator">=</span> req<span class="token punctuation">,</span> User <span class="token operator">=</span> user <span class="token punctuation">}</span>
            <span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>All database users are located on the &ldquo;right&rdquo; side. Even if there is no corresponding request, the user appears.</p><h2 id="executeupdate-and-executeupdateasync-for-json-columns">7. ExecuteUpdate and ExecuteUpdateAsync for JSON Columns</h2><p>EF Core 10 offers full support for updating JSON data (when the database supports it, SQL Server 2025, Azure SQL for example). This allows you to map complex properties to JSON columns.</p><p>Thus, it&rsquo;s possible to use <code>ExecuteUpdate</code> and <code>ExecuteUpdateAsync</code> to directly update properties within the JSON, without needing to load the entire entity into memory. This brings an advantage when storing semi-structured data within JSON columns. The example below demonstrates how to use this new feature:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> db
            <span class="token punctuation">.</span>Users<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Email <span class="token operator">==</span> request<span class="token punctuation">.</span>Email<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ExecuteUpdateAsync</span><span class="token punctuation">(</span>setters <span class="token operator">=</span><span class="token operator">&gt;</span>
                setters
                    <span class="token punctuation">.</span><span class="token function">SetProperty</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Username<span class="token punctuation">,</span> request<span class="token punctuation">.</span>Username<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">SetProperty</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Age<span class="token punctuation">,</span> request<span class="token punctuation">.</span>Age<span class="token punctuation">)</span>
            <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//Updating the property directly</span>
</code></pre><h2 id="improvements-to-mapping-complex-types-structs-and-owned-types">8. Improvements to Mapping Complex Types, Structs and Owned Types</h2><p>EF Core 10 expands support for complex types. Now it&rsquo;s possible to map composite (complex) types that don&rsquo;t have their own identity, assigning complex properties:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserProfile</span> 
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Bio <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">User</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span><span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Username <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Age <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> UserProfile Profile <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnModelCreating</span><span class="token punctuation">(</span>ModelBuilder modelBuilder<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
        modelBuilder<span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>User<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ComplexProperty</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Profile<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> complexUser <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span>
        <span class="token punctuation">{</span>
            Username <span class="token operator">=</span> user<span class="token punctuation">.</span>Username<span class="token punctuation">,</span>
            Email <span class="token operator">=</span> user<span class="token punctuation">.</span>Email<span class="token punctuation">,</span>
            Age <span class="token operator">=</span> user<span class="token punctuation">.</span>Age<span class="token punctuation">,</span>
            Profile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserProfile</span> <span class="token punctuation">{</span> Bio <span class="token operator">=</span> $<span class="token string">"Account for {user.Username}"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        db<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Note that we informed EF that the User entity has a complex property (Profile) through the extension method: <code>.ComplexProperty(u =&gt; u.Profile);</code>.</p><h2 id="named-query-filters">9. Named Query Filters</h2><p>It is now possible to define multiple global filters (query filters) for an entity, each with its own name, and then enable or disable specific filters in queries. This allows you to have several different logical filters and control which one to apply to each query:</p><pre class=" language-csharp"><code class="prism  language-csharp">modelBuilder<span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>User<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">HasQueryFilter</span><span class="token punctuation">(</span><span class="token string">"MinAgeFilter"</span><span class="token punctuation">,</span> u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Age <span class="token operator">&gt;=</span> <span class="token number">18</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

modelBuilder
            <span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>User<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">HasQueryFilter</span><span class="token punctuation">(</span><span class="token string">"EmailDomainFilter"</span><span class="token punctuation">,</span> u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>Email<span class="token punctuation">.</span><span class="token function">EndsWith</span><span class="token punctuation">(</span><span class="token string">"@company.com"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Ignoring filters:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// keeps the age filter, ignoring the email domain filter</span>
<span class="token keyword">var</span> adults <span class="token operator">=</span> db<span class="token punctuation">.</span>Users<span class="token punctuation">.</span><span class="token function">IgnoreQueryFilters</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"EmailDomainFilter"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h2 id="conclusion">Conclusion</h2><p>.NET 10 brought several improvements to ASP.NET Core, making features like EF Core even more versatile. In addition, one of the most anticipated new features was file-based apps, which allow you to run C# code using only a .cs file.</p><p>In this post, we covered the main points with practical examples for everyday use. I hope these new features help you implement simpler, more modern and efficient solutions in your projects.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Designing Microservices: An Architectural and Practical Approach</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Microservices provide flexibility, scalability and independence when managing systems that require decentralized resources. But how do these qualities translate into practice? Let&rsquo;s explore microservices from an architectural perspective and <a target="_blank" href="https://www.telerik.com/blogs/designing-microservices-architectural-practical-approach">build a feedback microservice from scratch in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17239934.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:549512db-9c1c-488e-99d5-7574d1ebeaa1</id>
    <title type="text">Managing Content Security in Telerik ASP.NET Core Applications</title>
    <summary type="text">The new Telerik UI for ASP.NET Core project template gives you better protection—it’s how to make CSP work for you.</summary>
    <published>2025-12-15T21:13:06Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17234525/managing-content-security-telerik-aspnet-core-applications"/>
    <content type="text"><![CDATA[<p><span class="featured">The new Telerik UI for ASP.NET Core project template gives you better protection&mdash;it&rsquo;s how to make CSP work for you.</span></p><p>Let&rsquo;s say, for example, that you&rsquo;ve been happily building ASP.NET Core applications using the <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/getting-started/first-steps-vs-extensions">Progress Telerik Visual Studio extensions</a> to generate the start point for your projects. All is going well until you start to build a new application, confident that you know what you&rsquo;re doing and how all of this stuff works &hellip; except, this time, when you start your application, it doesn&rsquo;t actually <em>work</em>.</p><p>If the symptoms are that images aren&rsquo;t displayed or stylesheets aren&rsquo;t applied or video doesn&rsquo;t play, or web service requests aren&rsquo;t issued, then the problem may be a Content Security Policy (CSP).</p><p>To confirm if the problem is CSP, just press F12 in your browser, check the messages in the console window and see if you find a message like this one (the asterisks represent message content that will vary, depending on the problem):</p><p>Refused to load the <code>https://</code> because it violates the following Content Security Policy directive.</p><p>If you&rsquo;re using the default Telerik template as a starting point for your project, the culprit is a <code>&lt;meta&gt;</code> tag that&rsquo;s automatically included in the project&rsquo;s Layout.cshtml file (the file is the default base for all your views). Here&rsquo;s the current version of that tag, formatted to make it easier to read:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span>
  <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>default-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>;
  
    img-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
      data:;

    script-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
      https://kendo.cdn.telerik.com
      https://code.jquery.com/
      https://cdn.kendostatic.com
      https://unpkg.com
      https://cdn.jsdelivr.net <span class="token punctuation">'</span>nonce-Telerik-Examples<span class="token punctuation">'</span>;
 
    style-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
      https://kendo.cdn.telerik.com
      https://unpkg.com
      https://cdn.jsdelivr.net;

    font-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>  
      https://unpkg.com;

    connect-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
      ws:
      http:;<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>You could make your error go away by just deleting the tag, but that&rsquo;s probably a mistake. This Content Security Policy (CSP) <code>&lt;meta&gt;</code> tag helps protect against code injection and cross-site-scripting (XSS) attacks.</p><p>Fundamentally, this CSP helps protect your project by limiting the sources where your page can retrieve scripts, stylesheets and images (or any other resource) to the sources listed in the <code>&lt;meta&gt;</code> tag.</p><p>However, it also means that if you&rsquo;re downloading resources from something that Progress Telerik can&rsquo;t consider (your organization&rsquo;s stylesheet site, for example), well, then your application won&rsquo;t work.</p><p>To fix it, you first need to know how to read the CSP added in the <code>&lt;meta&gt;</code> tag.</p><h2 id="reading-the-default-meta-tag">Reading the Default <code>&lt;meta&gt;</code> Tag</h2><p>A CSP like the one added in the <code>&lt;meta&gt;</code> tag is divided into multiple sections (called directives), separated by semicolons. A directive begins with the directive name and is followed by series of space-delimited sources.</p><p>The key directive is <code>default-src</code> which, unless overridden in other directives, specifies where the page can download resources from (in CSP, <code>default-src</code> is considered the fallback when other directives aren&rsquo;t provided). Here&rsquo;s the <code>default-src</code> directive from the <code>&lt;meta&gt;</code> tag, listing one source&mdash;the keyword <code>'self'</code>:</p><pre class=" language-html"><code class="prism  language-html">default-src 'self';
</code></pre><p>The <code>'self'</code> keyword limits downloads to resources from the same domain that your page was downloaded from. The other directives override that default list for specific types of content to add additional sources.</p><p>The <code>img-src</code> directive, for example, lets you specify sites that you want to enable for downloading images. The <code>&lt;meta&gt;</code> tag in your Telerik-generated project uses <code>'self'</code> to download images from the same domain as the page but also adds any image using a URI that begins with data. (The data: URI lets you embed base64 encoded files directly into your page rather than having to download them separately.)</p><pre class=" language-html"><code class="prism  language-html">img-src 'self'
  data:;
</code></pre><p>The other directives specify sources for:</p><ul><li>Downloading JavaScript files: Your page&rsquo;s domain, the Telerik site, plus some public and content delivery network sites like <a target="_blank" href="http://unpkg.com">unpkg.com</a> (<code>script-src</code>). The <code>nonce</code> keyword causes the server to generate a one-time random key that is included in the download and checked by the browser to verify that the script is coming from the requested server:</li></ul><pre class=" language-html"><code class="prism  language-html">script-src 'self'
  https://kendo.cdn.telerik.com
  &hellip;
  https://cdn.jsdelivr.net 'nonce-Telerik-Examples';
</code></pre><ul><li>Downloading stylesheets: Same kind of sites as with script files (<code>style-src</code>)</li><li>Downloading fonts: Both your page&rsquo;s domain and <a target="_blank" href="http://unpkg.com">unpkg.com</a> (<code>font-src</code>)</li><li>Calling WebSockets and web services: Requests to your page&rsquo;s domain plus all WebSocket requests and HTTP/HTTPS requests (<code>connect-src</code>)</li></ul><p>The result of this CPS is that every source <em>not</em> in those lists is blocked&mdash;that includes stylesheets, images, audio/video files and so on that aren&rsquo;t coming from your application&rsquo;s domain. Getting your application running, then, comes down to extending those lists to include those other sources.</p><p>Side note: When reading or modifying a CSP, allowing HTTP also allows HTTPS (and vice versa). An <code>img-src</code> directive that includes <code>http:phvis.com</code>, for example, would still allow this image tag to download its image over HTTPS:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span>https://phvis.com/SomePictureOnPetersSite.jpg</span> <span class="token punctuation">/&gt;</span></span>
</code></pre><h2 id="tailoring-the-telerik-meta-tag">Tailoring the Telerik <code>&lt;meta&gt;</code> Tag</h2><p>Typically, you&rsquo;ll run into a CSP problem because you&rsquo;re accessing a resource from somewhere other than your page&rsquo;s domain (for example, some shared image/stylesheet site or a video from a streaming site). When you do run into this problem, you have two ways of fixing it:</p><ul><li>Extend the <code>default-src</code> directive to include the other site. This makes sense if you&rsquo;re downloading multiple resources from that site and have confidence in its security (a central corporate site with resources to be used on all of your organization&rsquo;s pages).</li><li>Extend the directive for the specific resource that&rsquo;s being blocked (e.g., adding the URL where your organization&rsquo;s stylesheets/images are kept).</li></ul><p>That last option may include adding a new directive if the resource isn&rsquo;t one of the types already listed in the CSP (e.g., if the blocked resource is a video or audio file on another site). For a video or audio file, for example, you need to add the <code>media-src</code> directive to the CSP (see the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#directives">directives list</a> for other types of resources).</p><p>On the other hand, if your organization requires something more restrictive (the &ldquo;Block All, Allow Some&rdquo; strategy) or you&rsquo;d prefer something more flexible (the &ldquo;Allow All, Control Some&rdquo; strategy), you may want to swap in a different CSP altogether.</p><h2 id="defining-a-content-security-policy">Defining a Content Security Policy</h2><p>If you want to go beyond setting CSP pages for individual pages, you can configure your web server to return a CSP as one of the response headers for any request. If you do, you&rsquo;ll also have more options than are available using the <code>&lt;meta&gt;</code> tag. I&rsquo;ll continue with the <code>&lt;meta&gt;</code> tag, however.</p><p>The starting point for your CSP is a policy that has no content: A policy that specifies nothing allows everything. Continuing to use the <code>&lt;meta&gt;</code> tag as my example, a policy that allows everything would look like this:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>Or, more explicitly:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  content="" /&gt;
</code></pre><p>Building from that, you can implement one of three strategies: &ldquo;Allow All, Control Some,&rdquo; &ldquo;Block All, Allow Some&rdquo; or &ldquo;Allow Some, Control Some.&rdquo;</p><h3 id="allow-all-control-some">Allow All, Control Some</h3><p>Implementing an &ldquo;Allow All&rdquo; strategy begins with omitting the <code>default-src</code> directive which provides the fallback for any omitted directive. Without a <code>default-src</code>, any directive you <em>don&rsquo;t</em> provide allows everything.</p><p>Starting from there, you control just the resources you&rsquo;re interested in by including directives for those resources. This example only blocks scripts (they must come from the page&rsquo;s domain or my site) while allowing every other kind of resource:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span>
  <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>script-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
    http://phvis.com<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>This strategy makes sense when you feel there are only a small number of resources you need to control. This is the riskiest strategy (have you controlled all the right resources?) but will have the least impact on your applications.</p><h3 id="block-all-allow-some">Block All, Allow Some</h3><p>Another strategy is to block everything and then allow specific resources. With this strategy, your starting point is to provide a CSP with a <code>default-src</code> with no value&mdash;that will block all resources:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span>
  <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>default-src;<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>If you want to be more explicit, you can use <code>'none'</code> to make it clear that you&rsquo;re blocking everything:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span>
  <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>default-src <span class="token punctuation">'</span>none<span class="token punctuation">'</span>;<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>You can now selectively allow some resources to be downloaded. This example lets in images from the page&rsquo;s domain and my site but still blocks stylesheets, WebSocket access, web service requests and everything else not mentioned in this CSP:</p><pre class=" language-html"><code class="prism  language-html">&lt;meta http-equiv="Content-Security-Policy"
  content="default-src 'none';"
  img-src  'self'
    http://phvis.com" /&gt;
</code></pre><p>This is probably the safest strategy but will have the biggest impact on your applications (to be more exact: more applications will stop working). It will also lead to the longest CSP list and maintenance effort as you&rsquo;ll keep having to add more directives as your sites access a wider variety of resources from a longer list of sources.</p><h3 id="allow-some-control-some">Allow Some, Control Some</h3><p>The third strategy is to use <code>default-src</code> to specify any common source for all the resources you will allow (typically that&rsquo;s just <code>'self'</code>&mdash;your page&rsquo;s domain). You then add directives for specific resources that require something more. This is the strategy that Telerik has used in its CSP.</p><p>As you add those other directives, remember that those new directives don&rsquo;t extend <code>default-src</code> but, instead, override it. Essentially, that means that most/all of your additional directives will begin with whatever you have in the <code>default-src</code> directive.</p><p>This example allows any resource from my page&rsquo;s domain and my site to be downloaded, except for images. For images, this policy allows images from the page&rsquo;s domain, my site and the Telerik site:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span>
  <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>default-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
    http://phvis.com;

    img-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>
      http://phvis.com
      http://Telerik.com<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>This strategy is, essentially, a trade-off between the other two: By providing a fallback in <code>default-src</code> that allows resources from your &ldquo;safe&rdquo; sites, it reduces the number of directives that you&rsquo;ll have to add. You&rsquo;ll now only need to add directives where you need to specify a different or longer set of resources than your default list of &ldquo;safe&rdquo; sites.</p><p>So: Yes, CSP can be annoying. But CSP helps protect you from being attacked. And, considering the embarrassment that comes from finding your site defaced or hacked, it&rsquo;s probably worth implementing the strategy that saves you from explaining how that happened.</p><hr /><blockquote><p>Learn more about Progress&nbsp;<a target="_blank" href="https://www.telerik.com/aspnet-core-ui">Telerik UI for ASP.NET Core</a> and <a href="https://www.telerik.com/try/aspnet-core-ui" target="_blank">try it</a> yourself, free for 30 days.</p></blockquote><img src="https://feeds.telerik.com/link/23052/17234525.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:70cad1bd-8a4f-4a06-95de-97073703340f</id>
    <title type="text">Designing Microservices: An Architectural and Practical Approach</title>
    <summary type="text">Microservices provide flexibility, scalability and independence when managing systems that require decentralized resources. But how do these qualities translate into practice? Let’s explore microservices from an architectural perspective and build a feedback microservice from scratch in ASP.NET Core.</summary>
    <published>2025-12-09T14:01:02Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17227547/designing-microservices-architectural-practical-approach"/>
    <content type="text"><![CDATA[<p><span class="featured">Microservices provide flexibility, scalability and independence when managing systems that require decentralized resources. But how do these qualities translate into practice? Let&rsquo;s explore microservices from an architectural perspective and build a feedback microservice from scratch in ASP.NET Core.</span></p><p>In a technology-driven and highly consumer-oriented world, using classic approaches like monoliths isn&rsquo;t always the best choice. In these cases, microservices stand out as a flexible and viable option for creating and deploying independent services.</p><p>In this post, we&rsquo;ll explore how microservices fit together architecturally, their advantages and disadvantages, and how to build a backend microservice in ASP.NET Core to manage customer feedback.</p><h2 id="️-microservices-in-architecture">️ Microservices in Architecture</h2><p>If you&rsquo;ve worked or even studied programming in recent years, you&rsquo;ve probably seen the word &ldquo;microservice&rdquo; somewhere. This is because microservices have never been so popular. Large technology companies have made this term almost synonymous with scalability, resilience and innovation.</p><p>However, it&rsquo;s important to understand that microservices are not a ready-made formula for any project. They are, first and foremost, an architectural choice, and like any architectural decision, they should be based on the system&rsquo;s needs, not simply on the desire to adopt the most popular approach.</p><p>In practice, microservices present themselves as an alternative to the monolithic model. In a monolith, all business logic coexists within a single application. However, in microservices, the application is divided into smaller, independent services, each responsible for a specific context.</p><p>The choice of microservices directly impacts the architecture and has proven to be an excellent choice in many scenarios. Let&rsquo;s consider a scenario in which a microservice would be a recommended approach.</p><p>Imagine a scenario where we have three main services: Order, Payment and Feedback. In an ecommerce business, for example, Order and Payment are fundamental to the business&rsquo;s operation, while Feedback, although important, plays a more secondary role. Now, consider the following situation: a bug occurs in the Feedback service. How would this impact the application if it were structured as a monolith vs. as a set of microservices?</p><p>In the monolithic model, all services are grouped in a single system, sharing the same codebase and running in the same process. This means that, even if the problem is limited to the Feedback functionality alone, there is a risk of compromising the stability of the entire system.</p><p>Furthermore, fixing Function requires a complete redeployment, including Order and Payment, even if these services were not affected. In other words, a small detail in a secondary area can become a bottleneck for the entire business, as demonstrated in the image below:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/monolith-approach.png?sfvrsn=53413aba_2" title="monolith approach" alt="Monolith approach" /></p><p>In the microservices model, each part of the system is isolated in its own process, with independent deployment, scalability and database. In this scenario, a bug in Feedback only impacts that specific service, and since it&rsquo;s not essential to the purchasing process, it can be offline for a while without major complications.</p><p>Meanwhile, Order and Payment continue to function normally, without compromising the purchasing flow or payment processing, which are the heart of the business. Fixes are also faster, as only the problematic service needs to be updated.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/microservice-approach.png?sfvrsn=94d497bb_2" title="microservice approach" alt="Microservice approach" /></p><p>It&rsquo;s important to emphasize that, in this hypothetical scenario, we&rsquo;re considering a worst-case scenario without going into too much detail. In this sense, while in a monolith, a seemingly harmless bug can cause instability throughout the system, the impact in microservices is much more localized and controlled. This is why, in scenarios where certain services are business-critical, such as Order and Payment, the microservices architecture offers significant advantages in terms of resilience and operational continuity.</p><h2 id="-disadvantages-of-microservices"> Disadvantages of Microservices</h2><p>While they offer benefits such as scalability, resilience and separation of responsibilities, microservices also have disadvantages that must be considered.</p><p>One of the main ones is architectural complexity. Dividing a system into dozens or hundreds of independent services requires significantly more orchestration, monitoring, deployment and communication between components. Furthermore, network traffic increases significantly, as calls that were previously internal to a monolithic process are now distributed among different services, potentially becoming a long-term risk.</p><p>Another challenge faced when choosing to use microservices is related to data management and transactional consistency. Each microservice tends to have its own database, which makes it difficult to maintain consistency between them and often requires the adoption of more sophisticated strategies, such as sagas or event sourcing. This complexity increases the team&rsquo;s learning curve, demands greater maturity in DevOps practices and can increase infrastructure and operational costs.</p><p>Therefore, for organizations that do not have a solid foundation in these aspects, adopting microservices can result in a more fragile and expensive system than a well-structured monolith.</p><h2 id="️-building-a-microservice">️ Building a Microservice</h2><p>Now that we understand the software architecture aspects of microservices, let&rsquo;s build a microservice with ASP.NET Core that uses SQL Server as a database and runs it in a Docker container.</p><p>First, let&rsquo;s think about domain design. We need to clearly define the microservice&rsquo;s responsibilities. It should solve a specific problem within the larger context of the system, without accumulating functions that don&rsquo;t belong there. This clear boundary is what prevents a microservice from becoming a &ldquo;mini-monolith.&rdquo;</p><p>In this sense, the microservice we&rsquo;re going to create will be responsible for managing customer feedback. It should receive basic customer data such as the customer&rsquo;s name, the product or service they&rsquo;re referring to, their rating (using a scale of 1-5, where 1 is very bad and 5 is very good), and an optional description. It should then insert this data into a database. Furthermore, it should make the entered data available for future evaluation.</p><p>From a design perspective, this microservice refers to a simple data-driven CRUD, where modeling and implementation are guided by the data and the database. CRUD (Create, Read, Update, Delete) refers to the fact that the microservice exposes simple endpoints that allow creating, querying, updating and deleting records, as shown in the image below.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/data-driven-microservice.png?sfvrsn=7b5c8f7d_2" title="data driven microservice" alt="Data-driven microservice" /></p><h3 id="creating-the-project-and-downloading-the-dependencies">Creating the Project and Downloading the Dependencies</h3><p>You can access the complete application code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/customer-insights">Customer Insights Source Code</a>.</p><p>To create the project and solution, you can use the .NET commands below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new sln -n CustomerInsights

dotnet new webapi -n CustomerInsights

dotnet sln CustomerInsights.sln add CustomerInsights/CustomerInsights.csproj
</code></pre><p>Then run the following commands to add the NuGet packages to the project:</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">cd</span> CustomerInsights

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

dotnet add package Microsoft.EntityFrameworkCore.Tools

dotnet add package Microsoft.EntityFrameworkCore.Design
</code></pre><h3 id="creating-the-entity-class">Creating the Entity Class</h3><p>Now, let&rsquo;s define the entity that will represent the microservice responsible for receiving, storing and providing customer feedback. In this case, we&rsquo;ll have a class called Feedback.</p><p>So, within the project, create a new folder called &ldquo;Entities&rdquo; and add the following class to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Feedback</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> CustomerName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Product <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Rating <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Description <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token function">Feedback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token function">Feedback</span><span class="token punctuation">(</span><span class="token keyword">string</span> customerName<span class="token punctuation">,</span> <span class="token keyword">string</span> product<span class="token punctuation">,</span> <span class="token keyword">int</span> rating<span class="token punctuation">,</span> <span class="token keyword">string</span><span class="token operator">?</span> description <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>customerName<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Customer name is required."</span><span class="token punctuation">,</span> <span class="token function">nameof</span><span class="token punctuation">(</span>customerName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>product<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Product or service is required."</span><span class="token punctuation">,</span> <span class="token function">nameof</span><span class="token punctuation">(</span>product<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>rating <span class="token operator">&lt;</span> <span class="token number">1</span> <span class="token operator">||</span> rating <span class="token operator">&gt;</span> <span class="token number">5</span><span class="token punctuation">)</span> 
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentOutOfRangeException</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>rating<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"Rating must be between 1 and 5."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        CustomerName <span class="token operator">=</span> customerName<span class="token punctuation">;</span>
        Product <span class="token operator">=</span> product<span class="token punctuation">;</span>
        Rating <span class="token operator">=</span> rating<span class="token punctuation">;</span>
        Description <span class="token operator">=</span> description<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here we have a simple entity that represents customer feedback. Despite its simplicity, it follows best practices by keeping its properties private. Furthermore, creating a new record undergoes validations within the class itself, so that the domain is expressive and reveals its intent through the behavior it contains.</p><h3 id="creating-the-data-layer">Creating the Data Layer</h3><p>The data layer will contain the class that will communicate with the database. Since we&rsquo;re using Entity Framework Core as the ORM (Object Relational Mapping), we need to set the <code class="inline-code">DbContext</code> class.</p><p>So, create a new folder called &ldquo;Data&rdquo; and add the following class inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AppDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Feedback<span class="token operator">&gt;</span> Feedbacks <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token operator">!</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">AppDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">)</span>
        <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnModelCreating</span><span class="token punctuation">(</span>ModelBuilder modelBuilder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">OnModelCreating</span><span class="token punctuation">(</span>modelBuilder<span class="token punctuation">)</span><span class="token punctuation">;</span>

        modelBuilder<span class="token punctuation">.</span><span class="token generic-method function">Entity<span class="token punctuation">&lt;</span>Feedback<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>entity <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            entity<span class="token punctuation">.</span><span class="token function">ToTable</span><span class="token punctuation">(</span><span class="token string">"Feedbacks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            entity<span class="token punctuation">.</span><span class="token function">HasKey</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>

            entity<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>CustomerName<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">HasMaxLength</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            entity<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Product<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">HasMaxLength</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            entity<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Rating<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            entity<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Description<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">HasMaxLength</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The <code class="inline-code">AppDbContext</code> class will map the database entities, in this case, Feedback, in addition to making some properties, such as <code class="inline-code">CustomerName</code>, are mandatory when generating database migrations.</p><h3 id="creating-the-dtos">Creating the DTOs</h3><p>To insert and recover data, we&rsquo;ll create a DTO (Data Transfer Object) that will be received in the request. So, create a new folder called &ldquo;Dtos&rdquo; and within it, create the following record and class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Dtos<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CreateCustomerFeedbackDto</span><span class="token punctuation">(</span><span class="token keyword">string</span> CustomerName<span class="token punctuation">,</span> <span class="token keyword">string</span> Product<span class="token punctuation">,</span> <span class="token keyword">int</span> Rating<span class="token punctuation">,</span> <span class="token keyword">string</span><span class="token operator">?</span> Description<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Dtos<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedbackResponseDto</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> CustomerName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Product <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Rating <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Description <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="creating-the-mappings">Creating the Mappings</h3><p>Mapping classes is necessary to avoid exposing entity classes outside the API, nor bringing in data from outside without proper treatment. So, create a new folder called &ldquo;Mappings&rdquo; and, inside it, add the following class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Dtos<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Mappings<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">FeedbackMappings</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> FeedbackResponseDto <span class="token function">ToDto</span><span class="token punctuation">(</span><span class="token keyword">this</span> Feedback feedback<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">FeedbackResponseDto</span>
        <span class="token punctuation">{</span>
            Id <span class="token operator">=</span> feedback<span class="token punctuation">.</span>Id<span class="token punctuation">,</span>
            CustomerName <span class="token operator">=</span> feedback<span class="token punctuation">.</span>CustomerName<span class="token punctuation">,</span>
            Product <span class="token operator">=</span> feedback<span class="token punctuation">.</span>Product<span class="token punctuation">,</span>
            Rating <span class="token operator">=</span> feedback<span class="token punctuation">.</span>Rating<span class="token punctuation">,</span>
            Description <span class="token operator">=</span> feedback<span class="token punctuation">.</span>Description
        <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> IEnumerable<span class="token operator">&lt;</span>FeedbackResponseDto<span class="token operator">&gt;</span> <span class="token function">ToDtoList</span><span class="token punctuation">(</span><span class="token keyword">this</span> IEnumerable<span class="token operator">&lt;</span>Feedback<span class="token operator">&gt;</span> feedbacks<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> 
        feedbacks<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span><span class="token function">ToDto</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> Feedback <span class="token function">ToEntity</span><span class="token punctuation">(</span><span class="token keyword">this</span> CreateCustomerFeedbackDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Feedback</span><span class="token punctuation">(</span>
            dto<span class="token punctuation">.</span>CustomerName<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>Product<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>Rating<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>Description
        <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="creating-the-controller-class">Creating the Controller Class</h3><p>The next step is to create a controller class to receive API calls and perform actions on the database. Create a new folder called &ldquo;Controllers&rdquo; and add the following controller class inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Dtos<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Mappings<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>Mvc<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CustomerInsights<span class="token punctuation">.</span>Controllers<span class="token punctuation">;</span>

<span class="token punctuation">[</span>ApiController<span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token function">Route</span><span class="token punctuation">(</span><span class="token string">"api/v1/[controller]"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedbackController</span> <span class="token punctuation">:</span> ControllerBase
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> AppDbContext _dbContext<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">FeedbackController</span><span class="token punctuation">(</span>AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _dbContext <span class="token operator">=</span> dbContext<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// POST api/v1/feedback</span>
    <span class="token punctuation">[</span>HttpPost<span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">ProducesResponseType</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>Feedback<span class="token punctuation">)</span><span class="token punctuation">,</span> StatusCodes<span class="token punctuation">.</span>Status201Created<span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">ProducesResponseType</span><span class="token punctuation">(</span>StatusCodes<span class="token punctuation">.</span>Status400BadRequest<span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">Create</span><span class="token punctuation">(</span><span class="token punctuation">[</span>FromBody<span class="token punctuation">]</span> CreateCustomerFeedbackDto feedbackDto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> feedback <span class="token operator">=</span> feedbackDto<span class="token punctuation">.</span><span class="token function">ToEntity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _dbContext<span class="token punctuation">.</span>Feedbacks<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>feedback<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">await</span> _dbContext<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">return</span> <span class="token function">CreatedAtAction</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>GetById<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> id <span class="token operator">=</span> feedback<span class="token punctuation">.</span>Id <span class="token punctuation">}</span><span class="token punctuation">,</span> feedback<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ArgumentOutOfRangeException</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token punctuation">{</span> error <span class="token operator">=</span> ex<span class="token punctuation">.</span>Message <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ArgumentException</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token punctuation">{</span> error <span class="token operator">=</span> ex<span class="token punctuation">.</span>Message <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// GET api/v1/feedback?pageNumber=1&amp;pageSize=10</span>
    <span class="token punctuation">[</span>HttpGet<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">GetAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span> pageNumber <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span> pageSize <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> feedbacks <span class="token operator">=</span> <span class="token keyword">await</span> _dbContext<span class="token punctuation">.</span>Feedbacks
            <span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">OrderBy</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Skip</span><span class="token punctuation">(</span><span class="token punctuation">(</span>pageNumber <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> pageSize<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>pageSize<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">Ok</span><span class="token punctuation">(</span>feedbacks<span class="token punctuation">.</span><span class="token function">ToDtoList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// GET api/v1/feedback/product/{product}?pageNumber=1&amp;pageSize=10</span>
    <span class="token punctuation">[</span><span class="token function">HttpGet</span><span class="token punctuation">(</span><span class="token string">"product/{product}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">GetByProduct</span><span class="token punctuation">(</span><span class="token keyword">string</span> product<span class="token punctuation">,</span> <span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span> pageNumber <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span> pageSize <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> feedbacks <span class="token operator">=</span> <span class="token keyword">await</span> _dbContext<span class="token punctuation">.</span>Feedbacks
            <span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Product <span class="token operator">==</span> product<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">OrderBy</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Skip</span><span class="token punctuation">(</span><span class="token punctuation">(</span>pageNumber <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> pageSize<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>pageSize<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">Ok</span><span class="token punctuation">(</span>feedbacks<span class="token punctuation">.</span><span class="token function">ToDtoList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// GET api/v1/feedback/{id}</span>
    <span class="token punctuation">[</span><span class="token function">HttpGet</span><span class="token punctuation">(</span><span class="token string">"{id}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">GetById</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> feedback <span class="token operator">=</span> <span class="token keyword">await</span> _dbContext<span class="token punctuation">.</span>Feedbacks
            <span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>f <span class="token operator">=</span><span class="token operator">&gt;</span> f<span class="token punctuation">.</span>Id <span class="token operator">==</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>feedback <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">Ok</span><span class="token punctuation">(</span>feedback<span class="token punctuation">.</span><span class="token function">ToDto</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="configuring-the-program-class">Configuring the Program Class</h3><p>Now, let&rsquo;s configure the Program class, adding dependency injections and database initialization (<code class="inline-code">DatabaseInit.MigrationInitialization</code>). Add the following code to the Program class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CustomerInsights<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Add services to the container.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddOpenApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddDbContext<span class="token punctuation">&lt;</span>AppDbContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
    options<span class="token punctuation">.</span><span class="token function">UseSqlServer</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Configure the HTTP request pipeline.</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>app<span class="token punctuation">.</span>Environment<span class="token punctuation">.</span><span class="token function">IsDevelopment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    app<span class="token punctuation">.</span><span class="token function">MapOpenApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

app<span class="token punctuation">.</span><span class="token function">UseHttpsRedirection</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">UseAuthorization</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapControllers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

DatabaseInit<span class="token punctuation">.</span><span class="token function">MigrationInitialization</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Then, in the <code class="inline-code">appsettings.json</code> file, add the SQL Server connection string:</p><pre class=" language-json"><code class="prism  language-json"><span class="token string">"ConnectionStrings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"DefaultConnection"</span><span class="token punctuation">:</span> <span class="token string">"Server=mssql-server,1433;Initial Catalog=FeedbackDb;User ID=SA;Password=8/geTo'7l0f4;TrustServerCertificate=true"</span>
  <span class="token punctuation">}</span>
</code></pre><h3 id="applying-migrations">Applying Migrations</h3><p>You can use the commands below to apply EF Core database migrations.</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token comment"># Create the migrations</span>
dotnet ef migrations add InitialCreate

<span class="token comment"># If you don't already have the EF Core Tools CLI installed globally</span>
dotnet tool <span class="token function">install</span> --global dotnet-ef
</code></pre><h2 id="running-the-microservice-and-database-in-a-docker-container">Running the Microservice and Database in a Docker Container</h2><p>Our microservice is ready to run. For that, we&rsquo;ll use Docker. If you&rsquo;re unfamiliar with Docker, I suggest these two posts that demonstrate how to deploy an ASP.NET Core application from scratch:</p><ol><li><a target="_blank" href="https://www.telerik.com/blogs/deploying-aspnet-core-applications-docker-part-1">Deploying ASP.NET Core Applications with Docker&mdash;Part 1</a>.</li><li><a target="_blank" href="https://www.telerik.com/blogs/deploying-aspnet-core-applications-docker-part-2">Deploying ASP.NET Core Applications with Docker&mdash;Part 2</a>.</li></ol><p>For educational purposes, we will run SQL Server in a Docker container, but in production environments, it is recommended to use cloud resources to maintain the database.</p><p>So, add the following Docker Compose file and Dockerfile to the application root:</p><ul><li>docker-compose.yml</li></ul><pre class=" language-yaml"><code class="prism  language-yaml"><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">"3.9"</span>

<span class="token key atrule">services</span><span class="token punctuation">:</span>
  <span class="token key atrule">customer-insights</span><span class="token punctuation">:</span>
    <span class="token key atrule">build</span><span class="token punctuation">:</span>
      <span class="token key atrule">context</span><span class="token punctuation">:</span> .
      <span class="token key atrule">dockerfile</span><span class="token punctuation">:</span> Dockerfile
    <span class="token key atrule">container_name</span><span class="token punctuation">:</span> customer<span class="token punctuation">-</span>insights
    <span class="token key atrule">ports</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token string">"5050:8080"</span>
    <span class="token key atrule">depends_on</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> mssql<span class="token punctuation">-</span>server
    <span class="token key atrule">environment</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> ASPNETCORE_ENVIRONMENT=Development
      <span class="token punctuation">-</span> ConnectionStrings__DefaultConnection=Server=mssql<span class="token punctuation">-</span>server<span class="token punctuation">,</span>1433;Initial Catalog=FeedbackDb;User ID=SA;Password=8/geTo'7l0f4;TrustServerCertificate=true

  <span class="token key atrule">mssql-server</span><span class="token punctuation">:</span>
    <span class="token key atrule">image</span><span class="token punctuation">:</span> mcr.microsoft.com/mssql/server<span class="token punctuation">:</span>2022<span class="token punctuation">-</span>latest
    <span class="token key atrule">container_name</span><span class="token punctuation">:</span> mssql<span class="token punctuation">-</span>server
    <span class="token key atrule">ports</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token string">"1433:1433"</span>
    <span class="token key atrule">environment</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> ACCEPT_EULA=Y
      <span class="token punctuation">-</span> MSSQL_SA_PASSWORD=8/geTo'7l0f4
    <span class="token key atrule">volumes</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> mssqldata<span class="token punctuation">:</span>/var/opt/mssql

<span class="token key atrule">volumes</span><span class="token punctuation">:</span>
  <span class="token key atrule">mssqldata</span><span class="token punctuation">:</span>
</code></pre><ul><li>Dockerfile</li></ul><pre class=" language-dockerfile"><code class="prism  language-dockerfile"><span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/sdk<span class="token punctuation">:</span>9.0 AS build
<span class="token keyword">WORKDIR</span> /src

<span class="token keyword">COPY</span> *.csproj ./
<span class="token keyword">RUN</span> dotnet restore

<span class="token keyword">COPY</span> . .
<span class="token keyword">RUN</span> dotnet publish <span class="token punctuation">-</span>c Release <span class="token punctuation">-</span>o /app

<span class="token keyword">FROM</span> mcr.microsoft.com/dotnet/aspnet<span class="token punctuation">:</span>9.0 AS runtime
<span class="token keyword">WORKDIR</span> /app
<span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>from=build /app ./

<span class="token keyword">EXPOSE</span> 8080
<span class="token keyword">ENTRYPOINT</span> <span class="token punctuation">[</span><span class="token string">"dotnet"</span><span class="token punctuation">,</span> <span class="token string">"CustomerInsights.dll"</span><span class="token punctuation">]</span>
</code></pre><p>Then run the Docker commands in a terminal:</p><pre class=" language-bash"><code class="prism  language-bash">docker-compose up -d
</code></pre><p>After execution, you should have two Docker containers, one for the microservice and one for SQL Server:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/docker-containers.png?sfvrsn=31c236e2_2" title="docker containers" alt="Docker containers" /></p><p>Now, if you make a POST request to the API using the route: <code class="inline-code">http://localhost:5050/api/v1/feedback/1</code> and pass the following JSON in the body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
  <span class="token string">"customerName"</span><span class="token punctuation">:</span> <span class="token string">"John Smith"</span><span class="token punctuation">,</span>
  <span class="token string">"product"</span><span class="token punctuation">:</span> <span class="token string">"helloPhone 10 Pro"</span><span class="token punctuation">,</span>
  <span class="token string">"rating"</span><span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span>
  <span class="token string">"description"</span><span class="token punctuation">:</span> <span class="token string">"Excellent phone! Super fast, great camera, and battery lasts all day. Worth the upgrade."</span>
<span class="token punctuation">}</span>
</code></pre><p>A new record will be inserted into the database.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/inserting-records.png?sfvrsn=19abde6d_2" title="inserting records" alt="Inserting records" /></p><p>And if you make a GET request, the record will be returned:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/finding-records.png?sfvrsn=c9235850_2" title="finding records" alt="Finding records" /></p><p>So, we have a complete data-driven microservice running in a Docker container.</p><h2 id="-conclusion-and-next-steps"> Conclusion and Next Steps</h2><p>Microservices emerged as an alternative to monolithic systems, reducing the tight coupling between functionalities. Their main advantages include:</p><ul><li>Independent deployment, which allows each service to be updated without impacting the rest of the system</li><li>Selective scalability, where only the most resource-intensive services can be expanded</li><li>Resilience, since failures in one service do not compromise the entire application</li></ul><p>In this post, we built a data-driven CRUD microservice to record and query customer feedback and deployed it in a Docker container.</p><p>I hope the examples demonstrated help you understand how microservices are represented within a modern architecture. Several important features can be explored, such as authentication, monitoring, automated testing, versioning and observability, which make the microservices ecosystem even more robust.</p><img src="https://feeds.telerik.com/link/23052/17227547.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:b8563cd0-8163-4005-8eff-c7feb43480de</id>
    <title type="text">Securing Apps with the Telerik UI for ASP.NET Core OTP Control</title>
    <summary type="text">In this article, we will analyze how to use the Telerik UI for ASP.NET Core OTPInput control, which allows users to enter a one-time password (OTP) during multi-factor authentication in an intuitive manner.</summary>
    <published>2025-11-24T16:04:14Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17215975/securing-apps-telerik-ui-aspnet-core-otp-control"/>
    <content type="text"><![CDATA[<p>In this article, we will analyze how to use the Telerik UI for <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/otp-input">ASP.NET Core OTPInput</a> control, which allows users to enter a one-time password (OTP) during multi-factor authentication in an intuitive manner. Let&rsquo;s get started!</p><h2 id="what-is-the-asp.net-core-otpinput-control">What Is the ASP.NET Core OTPInput Control?</h2><p>The purpose of this control is to provide enhanced security to applications while offering a pleasant user experience when requesting a <strong>one-time password</strong>. It can be used both as a <strong>TagHelper</strong> and with an <strong>HtmlHelper</strong>, which are wrappers for the <strong>Kendo UI OTPInput</strong> widget.</p><p>Some use cases where we could utilize this control include (among many other cases):</p><ul><li>Multi-factor authentication</li><li>Verification during password reset</li><li>Transaction authorization</li><li>Access to restricted areas on websites</li></ul><p>This control can be customized in terms of appearance, input type, number of elements to request, groups in the input and even the type of virtual keyboard that should be displayed if viewed from a mobile device.</p><h2 id="integrating-the-otpinput-control-into-an-asp.net-core-project">Integrating the OTPInput control into an ASP.NET Core project</h2><p>Imagine we are creating a page to validate a six-digit OTP code. One of the first things you might think of to create it would be to use an <code class="inline-code">input</code> tag with some preset values, such as a maximum length of six characters.</p><p>If you want to replicate the demonstrations in this article, you should create a new project using the <strong>ASP.NET Core Web App (Razor Pages)</strong> template. You should also make sure to follow the <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/getting-started/first-steps">Telerik UI for ASP.NET Core controls installation guide</a> to have the environment ready and set up.</p><p>Once that is done, replace the content of the <code class="inline-code">index.cshtml</code> file with the following:</p><pre class=" language-xml"><code class="prism  language-xml">@page

@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">&gt;</span></span><span class="token style language-css">
    <span class="token selector"><span class="token class">.verification-container</span> </span><span class="token punctuation">{</span>
        <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
        <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
        <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
        <span class="token property">margin-top</span><span class="token punctuation">:</span> <span class="token number">5</span>rem<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token selector"><span class="token class">.verification-form</span> </span><span class="token punctuation">{</span>
        <span class="token property">max-width</span><span class="token punctuation">:</span> <span class="token number">320</span>px<span class="token punctuation">;</span>
        <span class="token property">gap</span><span class="token punctuation">:</span> <span class="token number">1.5</span>rem<span class="token punctuation">;</span>
        <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100%</span><span class="token punctuation">;</span>
        <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">2</span>rem<span class="token punctuation">;</span>
        <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#e0e0e0</span><span class="token punctuation">;</span>
        <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token number">8</span>px<span class="token punctuation">;</span>
        <span class="token property">box-shadow</span><span class="token punctuation">:</span> <span class="token number">0</span> <span class="token number">2</span>px <span class="token number">8</span>px <span class="token function">rgba</span><span class="token punctuation">(</span><span class="token number">0</span>,<span class="token number">0</span>,<span class="token number">0</span>,<span class="token number">0.1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
        <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
        <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

        <span class="token selector"><span class="token class">.verification-form</span> input </span><span class="token punctuation">{</span>
            <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100%</span><span class="token punctuation">;</span>
            <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">0.75</span>rem<span class="token punctuation">;</span>
            <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
            <span class="token property">margin-bottom</span><span class="token punctuation">:</span> <span class="token number">1.5</span>rem<span class="token punctuation">;</span>
            <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token hexcode">#ccc</span><span class="token punctuation">;</span>
            <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token number">4</span>px<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token selector"><span class="token class">.verification-form</span> kendo-button </span><span class="token punctuation">{</span>
            <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100%</span><span class="token punctuation">;</span>
            <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-container<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">&gt;</span></span>Two-Factor Verification<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Please enter the 6-digit code sent to your email:<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>otpCode<span class="token punctuation">"</span></span> <span class="token attr-name">maxlength</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Enter the code<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-button</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btnVerify<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span> <span class="token attr-name">theme-color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ThemeColor.Primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            Verify
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The above code specifies a <code class="inline-code">form</code> section within which we add an <code class="inline-code">input</code> and a <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/html-helpers/navigation/button/overview">kendo-button</a> to simulate the submission of the code. The execution of the code looks like this:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/using-an-input-field-to-enter-an-otp-code-which-tends-to-be-unreliable.gif?sfvrsn=206ef795_2" alt="Using an input field to enter an OTP code, which tends to be unreliable" /></p><p>You can see that with this approach we face several issues:</p><ul><li>The user can enter any character.</li><li>If the OTP code were grouped, the interface is not intuitive for the user to recognize them.</li><li>It is likely that on mobile devices, the virtual keyboard that appears is not the correct one.</li></ul><p>In these cases, the <a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/html-helpers/editors/otpinput/overview">ASP.NET Core OTPInput control</a> from Telerik is the best option as it easily addresses all these problems, as we will see next.</p><h2 id="changing-the-input-control-for-an-otpinput">Changing the Input Control for an OTPInput</h2><p>To replace the input element with an OTPInput, we need to consider the type of project we are building to choose between using a <strong>HtmlHelper</strong>, <strong>TagHelper</strong> or even pure <strong>Html</strong>. In my case, since the example is based on Razor pages, I will be using <strong>TagHelper</strong> elements. Given that, the simplest creation of the control would look as follows:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>        
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">items</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-button</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btnVerify<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span> <span class="token attr-name">theme-color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ThemeColor.Primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        Verify
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-button</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The result of the change is as follows:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/replacing-an-input-element-with-telerik-otpinput-control.gif?sfvrsn=a3009a55_4" alt="Replacing an input element with Telerik&#39;s OTPInput control" /></p><p>We can see that the definition of the control is quite straightforward, showcasing the new component in the graphical interface with a modern, aesthetic and functional design. The control automatically centers the focus on the corresponding box, providing an intuitive user experience.</p><p>Moreover, through the use of the <code class="inline-code">items</code> configuration, it has been very easy to indicate the number of characters the user needs to fill in, which results in a control on the UI with the ideal layout so users know at all times which item they are filling in and how many are left.</p><p>Another attribute we have assigned to the control is <code class="inline-code">name</code>, which is mandatory as it sets the HTML attributes <code class="inline-code">id</code> and <code class="inline-code">name</code> behind the scenes.</p><p>One issue we can see in the previous image is that the user can still enter any type of character in the text boxes. Fortunately, the control allows us to change this easily, which we will see next.</p><h2 id="forcing-numeric-input">Forcing Numeric Input</h2><p>We can change the data type entered into the control by using the <code class="inline-code">type</code> configuration, to which we can assign one of the following values:</p><ul><li><code class="inline-code">OTPType.Number</code>: Allows entry only of numeric characters</li><li><code class="inline-code">OTPType.Text</code>: Allows entry of characters in general</li><li><code class="inline-code">OTPType.Password</code>: Allows converting characters to a password format</li></ul><p>In our case, the most convenient method to validate an authentication code will be to limit the input to numeric values, so the definition would look as follows:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>        
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">items</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OTPType.Number<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>When running the application, we obtain the following result:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/restricting-input-to-numeric-characters-in-the-otpinput-control-using-the-type-setting.gif?sfvrsn=a8ef6a6d_2" alt="Restricting input to numeric characters in the OTPInput control using the type setting" /></p><p>In the previous image, you can notice that when trying to enter a non-numeric character, the user receives immediate feedback indicating that the character is invalid.</p><h2 id="creating-groups-in-character-input">Creating Groups in Character Input</h2><p>If the OTP code to be entered requires some sort of visual grouping due to its length, it is very easy to create groups of items and even add separators between them. For example, let&rsquo;s assume that the OTP code is composed of 8 digits, grouped as <strong>3-2-3</strong> items. To achieve the above grouping, the <code class="inline-code">items</code> configuration should be used as follows:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>        
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OTPType.Number<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>otpinput-items</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>otpinput-items</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The result of the above code can be seen in the following image:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/configuring-digit-grouping-in-the-otpinput-control.png?sfvrsn=dd08d4cf_2" alt="Configuring digit grouping in the OTPInput control" /></p><p>In the image, the creation of groups according to the specified configuration is visible.</p><p>Likewise, if you need to show the separation between the groups better, it is possible to add visual separators using the <code class="inline-code">separator</code> configuration as shown below:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>        
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OTPType.Number<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>-<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>otpinput-items</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span><span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">group-length</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>otpinput-items</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>This provides a better representation of the groups, as shown below:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/adding-visual-separators-between-digit-groups-in-the-otpinput-control.png?sfvrsn=dc139539_2" alt="Adding visual separators between digit groups in the OTPInput control" /></p><h2 id="customizing-the-otpinput-control">Customizing the OTPInput Control</h2><p>If you need to customize the OTPInput to fit the visual design of your application, the series of customization settings will allow you to modify the size, color application and border radius of the component.</p><p>To change the size, simply use the <code class="inline-code">size</code> option with one of the following values:</p><ul><li><code class="inline-code">ComponentSize.Small</code></li><li><code class="inline-code">ComponentSize.Medium</code></li><li><code class="inline-code">ComponentSize.Large</code></li><li><code class="inline-code">ComponentSize.None</code></li></ul><p>If you want to change how the color is applied to the component, you can use the <code class="inline-code">fill-mode</code> option with one of these values:</p><ul><li><code class="inline-code">FillMode.Solid</code></li><li><code class="inline-code">FillMode.Outline</code></li><li><code class="inline-code">FillMode.Flat</code></li><li><code class="inline-code">FillMode.None</code></li></ul><p>Lastly, you can modify the <code class="inline-code">rounded</code> option to change the border radius of the component by assigning one of the following values:</p><ul><li><code class="inline-code">Rounded.Small</code></li><li><code class="inline-code">Rounded.Medium</code></li><li><code class="inline-code">Rounded.Large</code></li><li><code class="inline-code">Rounded.Full</code></li><li><code class="inline-code">Rounded.None</code></li></ul><p>In the following example, the usage of all these properties is demonstrated:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OTPType.Number<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>-<span class="token punctuation">"</span></span>
    <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ComponentSize.Large<span class="token punctuation">"</span></span>
    <span class="token attr-name">fill-mode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>FillMode.Solid<span class="token punctuation">"</span></span>
    <span class="token attr-name">rounded</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Rounded.Full<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>    
</code></pre><p>The above code results in the control being displayed as follows:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/personalizing-the-otpinput-control-through-its-customization-settings.png?sfvrsn=56afe1c4_2" alt="Personalizing the OTPInput control through its customization settings" /></p><p>Finally, it is possible to specify which keyboard should appear when using a mobile device. By default, when the application is run, the virtual keyboard that appears is the one shown in the following image:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/the-default-virtual-text-keyboard-shown-on-mobile-devices.jpg?sfvrsn=87d830dc_2" alt="The default virtual text keyboard shown on mobile devices" /></p><p>If you want to change the keyboard, you can use the <code class="inline-code">input-mode</code> option by assigning any valid HTML5 input mode, for example, <code class="inline-code">text</code>, <code class="inline-code">numeric</code>, <code class="inline-code">tel</code>, etc. In the following example, I specified that the keyboard to be displayed is the numeric one:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>optCode<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>OTPType.Number<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>-<span class="token punctuation">"</span></span>
    <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ComponentSize.Large<span class="token punctuation">"</span></span>
    <span class="token attr-name">fill-mode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>FillMode.Solid<span class="token punctuation">"</span></span>
    <span class="token attr-name">rounded</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Rounded.Full<span class="token punctuation">"</span></span>
    <span class="token attr-name">input-mode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>numeric<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The previous change shows the numeric keyboard when starting to fill in the OTPInput control:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/displaying-a-numeric-virtual-keyboard-on-mobile-devices-using-the-inputmode-attribute.jpg?sfvrsn=a99e26f3_2" alt="Displaying a numeric virtual keyboard on mobile devices using the inputmode attribute" /></p><h2 id="events-in-the-otpinput-control">Events in the OTPInput Control</h2><p>The OTPInput control provides the <code class="inline-code">Change</code> event that allows you to control the behavior of the component. For example, you could validate whether all items have been filled before enabling a submit button. You can accomplish this through a Handler Name or a Template Delegate. Below is an example of its usage:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-container<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">&gt;</span></span>Two-Factor Verification<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Please enter the 6-digit code sent to your email:<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">asp-action</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>VerifyCode<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>verification-form<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>        
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-otpinput</span> <span class="token attr-name">...</span> <span class="token attr-name">on-change</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onChange<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            ...
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-otpinput</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-button</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btnVerify<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span> <span class="token attr-name">theme-color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ThemeColor.Primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            Verify
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript">
    <span class="token keyword">function</span> <span class="token function">onChange</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Change :: "</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>When executing the above code, we obtain the following result:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-08/handling-the-onchange-event-to-display-the-current-value-of-the-otpinput-control.gif?sfvrsn=ef32bc56_2" alt="Handling the onChange event to display the current value of the OTPInput control" /></p><p>Undoubtedly, this event can greatly assist us in performing pre-validation before submitting the information.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, you have learned what the Telerik UI for ASP.NET Core OTP control is and some use cases. You have also learned how to configure it to meet your business and design needs through customization settings and item adjustments. Now it&rsquo;s your turn to enhance the security of your applications by implementing it when you need to validate one-time passwords.</p><p>If you haven&rsquo;t already begun using Telerik UI for ASP.NET Core, it comes with a free 30-day trial:</p><p><a target="_blank" href="https://www.telerik.com/try/aspnet-core-ui" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23052/17215975.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:42166ba1-7d43-40cb-9cdc-71435cfe6014</id>
    <title type="text">Real-Time Data Updates with the Telerik UI for .NET MAUI Grid</title>
    <summary type="text">Learn how to make real-time updates in your .NET MAUI applications with SignalR, common in stocks or cryptocurrency scenarios.</summary>
    <published>2025-11-18T18:47:29Z</published>
    <updated>2026-05-28T11:59:39Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17211761/real-time-data-updates-telerik-ui-net-maui-grid"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to make real-time updates in your .NET MAUI applications with SignalR, common in stocks or cryptocurrency scenarios.</span></p><p>In this article, you will learn how to carry out real-time updates in your .NET MAUI-based applications thanks to the power of <a target="_blank" href="https://dotnet.microsoft.com/en-us/apps/aspnet/signalr">SignalR</a>, common in stocks or cryptocurrency scenarios. You will also see why the Progress Telerik <a target="_blank" href="https://www.telerik.com/maui-ui/documentation/controls/datagrid/overview">.NET MAUI DataGrid control</a> is ideal in these scenarios, as it allows functionalities such as rendering custom columns through the use of SkiaSharp. Let&rsquo;s get started!</p><h2 id="creating-a-stock-update-service">Creating a Stock Update Service</h2><p>We will start with a simple API project that simulates the retrieval of stocks from the stock market, with the purpose of showing you how to create a service that exposes real-time information using SignalR. For this demonstration, I created a project using the <strong>ASP.NET Core Web API</strong> template with the following classes:</p><p><strong>Stock.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Stock</span>
<span class="token punctuation">{</span>    
    <span class="token keyword">public</span> <span class="token keyword">string</span> Symbol <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> CompanyName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Price <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Change <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> ChangePercent <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> OpenPrice <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> HighPrice <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> LowPrice <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> PreviousClose <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">long</span> Volume <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">long</span> MarketCap <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateTime LastUpdated <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsMarketOpen <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Sector <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p><strong>StockService.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp">    <span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">IStockService</span>
    <span class="token punctuation">{</span>
        List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> <span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        Stock<span class="token operator">?</span> <span class="token function">GetStock</span><span class="token punctuation">(</span><span class="token keyword">string</span> symbol<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">event</span> Action<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span><span class="token operator">?</span> StockUpdated<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StockService</span> <span class="token punctuation">:</span> IStockService<span class="token punctuation">,</span> IDisposable
    <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> Dictionary<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token punctuation">,</span> Stock<span class="token operator">&gt;</span> _stocks<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> Timer _updateTimer<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> Random _random<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token keyword">int</span> _updateIntervalMs<span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">event</span> Action<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span><span class="token operator">?</span> StockUpdated<span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token function">StockService</span><span class="token punctuation">(</span>IConfiguration configuration<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            _stocks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dictionary</span><span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token punctuation">,</span> Stock<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            _random <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            _updateIntervalMs <span class="token operator">=</span> configuration<span class="token punctuation">.</span><span class="token generic-method function">GetValue<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"StockSimulation:UpdateIntervalMs"</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token function">InitializeStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _updateTimer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span>UpdateStockPrices<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
                TimeSpan<span class="token punctuation">.</span><span class="token function">FromMilliseconds</span><span class="token punctuation">(</span>_updateIntervalMs<span class="token punctuation">)</span><span class="token punctuation">,</span>
                TimeSpan<span class="token punctuation">.</span><span class="token function">FromMilliseconds</span><span class="token punctuation">(</span>_updateIntervalMs<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">InitializeStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> stockData <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">[</span><span class="token punctuation">]</span>
            <span class="token punctuation">{</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"AAPL"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Apple Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Technology"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">175</span><span class="token punctuation">.</span>50m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 2800000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"GOOGL"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Alphabet Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Technology"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">138</span><span class="token punctuation">.</span>25m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 1750000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"MSFT"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Microsoft Corporation"</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Technology"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">378</span><span class="token punctuation">.</span>90m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 2850000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"AMZN"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Amazon.com Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Consumer Discretionary"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">143</span><span class="token punctuation">.</span>75m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 1500000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"TSLA"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Tesla Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Consumer Discretionary"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">248</span><span class="token punctuation">.</span>50m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 790000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"NVDA"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"NVIDIA Corporation"</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Technology"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">455</span><span class="token punctuation">.</span>30m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 1120000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"META"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Meta Platforms Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Technology"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">298</span><span class="token punctuation">.</span>80m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 760000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"JPM"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"JPMorgan Chase &amp; Co."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Financial Services"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">158</span><span class="token punctuation">.</span>45m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 460000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"V"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Visa Inc."</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Financial Services"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">265</span><span class="token punctuation">.</span>90m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 520000000000L <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token punctuation">{</span> Symbol <span class="token operator">=</span> <span class="token string">"JNJ"</span><span class="token punctuation">,</span> Company <span class="token operator">=</span> <span class="token string">"Johnson &amp; Johnson"</span><span class="token punctuation">,</span> Sector <span class="token operator">=</span> <span class="token string">"Healthcare"</span><span class="token punctuation">,</span> Price <span class="token operator">=</span> <span class="token number">162</span><span class="token punctuation">.</span>30m<span class="token punctuation">,</span> MarketCap <span class="token operator">=</span> 425000000000L <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

            <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> data <span class="token keyword">in</span> stockData<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> stock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stock</span>
                <span class="token punctuation">{</span>
                    Symbol <span class="token operator">=</span> data<span class="token punctuation">.</span>Symbol<span class="token punctuation">,</span>
                    CompanyName <span class="token operator">=</span> data<span class="token punctuation">.</span>Company<span class="token punctuation">,</span>
                    Sector <span class="token operator">=</span> data<span class="token punctuation">.</span>Sector<span class="token punctuation">,</span>
                    Price <span class="token operator">=</span> data<span class="token punctuation">.</span>Price<span class="token punctuation">,</span>
                    PreviousClose <span class="token operator">=</span> data<span class="token punctuation">.</span>Price <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token keyword">decimal</span><span class="token punctuation">)</span><span class="token punctuation">(</span>_random<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">10</span> <span class="token operator">-</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    OpenPrice <span class="token operator">=</span> data<span class="token punctuation">.</span>Price <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token keyword">decimal</span><span class="token punctuation">)</span><span class="token punctuation">(</span>_random<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">6</span> <span class="token operator">-</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    MarketCap <span class="token operator">=</span> data<span class="token punctuation">.</span>MarketCap<span class="token punctuation">,</span>
                    Volume <span class="token operator">=</span> _random<span class="token punctuation">.</span><span class="token function">NextInt64</span><span class="token punctuation">(</span><span class="token number">1000000</span><span class="token punctuation">,</span> <span class="token number">50000000</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    IsMarketOpen <span class="token operator">=</span> <span class="token function">IsMarketOpen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    LastUpdated <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow
                <span class="token punctuation">}</span><span class="token punctuation">;</span>
                
                stock<span class="token punctuation">.</span>HighPrice <span class="token operator">=</span> stock<span class="token punctuation">.</span>OpenPrice <span class="token operator">+</span> Math<span class="token punctuation">.</span><span class="token function">Abs</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">decimal</span><span class="token punctuation">)</span><span class="token punctuation">(</span>_random<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>LowPrice <span class="token operator">=</span> stock<span class="token punctuation">.</span>OpenPrice <span class="token operator">-</span> Math<span class="token punctuation">.</span><span class="token function">Abs</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">decimal</span><span class="token punctuation">)</span><span class="token punctuation">(</span>_random<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>Change <span class="token operator">=</span> stock<span class="token punctuation">.</span>Price <span class="token operator">-</span> stock<span class="token punctuation">.</span>PreviousClose<span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>ChangePercent <span class="token operator">=</span> stock<span class="token punctuation">.</span>PreviousClose <span class="token operator">!=</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token punctuation">(</span>stock<span class="token punctuation">.</span>Change <span class="token operator">/</span> stock<span class="token punctuation">.</span>PreviousClose<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">100</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">;</span>

                _stocks<span class="token punctuation">[</span>data<span class="token punctuation">.</span>Symbol<span class="token punctuation">]</span> <span class="token operator">=</span> stock<span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">UpdateStockPrices</span><span class="token punctuation">(</span><span class="token keyword">object</span><span class="token operator">?</span> state<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">IsMarketOpen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

            <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> stock <span class="token keyword">in</span> _stocks<span class="token punctuation">.</span>Values<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>                
                <span class="token keyword">var</span> changePercent <span class="token operator">=</span> <span class="token punctuation">(</span>_random<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">0.5</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">0.04</span><span class="token punctuation">;</span>
                <span class="token keyword">var</span> priceChange <span class="token operator">=</span> stock<span class="token punctuation">.</span>Price <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token keyword">decimal</span><span class="token punctuation">)</span>changePercent<span class="token punctuation">;</span>

                stock<span class="token punctuation">.</span>Price <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">Max</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">.</span>01m<span class="token punctuation">,</span> stock<span class="token punctuation">.</span>Price <span class="token operator">+</span> priceChange<span class="token punctuation">)</span><span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>Change <span class="token operator">=</span> stock<span class="token punctuation">.</span>Price <span class="token operator">-</span> stock<span class="token punctuation">.</span>PreviousClose<span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>ChangePercent <span class="token operator">=</span> stock<span class="token punctuation">.</span>PreviousClose <span class="token operator">!=</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token punctuation">(</span>stock<span class="token punctuation">.</span>Change <span class="token operator">/</span> stock<span class="token punctuation">.</span>PreviousClose<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">100</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">;</span>
                
                <span class="token keyword">if</span> <span class="token punctuation">(</span>stock<span class="token punctuation">.</span>Price <span class="token operator">&gt;</span> stock<span class="token punctuation">.</span>HighPrice<span class="token punctuation">)</span>
                    stock<span class="token punctuation">.</span>HighPrice <span class="token operator">=</span> stock<span class="token punctuation">.</span>Price<span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>stock<span class="token punctuation">.</span>Price <span class="token operator">&lt;</span> stock<span class="token punctuation">.</span>LowPrice<span class="token punctuation">)</span>
                    stock<span class="token punctuation">.</span>LowPrice <span class="token operator">=</span> stock<span class="token punctuation">.</span>Price<span class="token punctuation">;</span>
                
                stock<span class="token punctuation">.</span>Volume <span class="token operator">+</span><span class="token operator">=</span> _random<span class="token punctuation">.</span><span class="token function">NextInt64</span><span class="token punctuation">(</span><span class="token number">10000</span><span class="token punctuation">,</span> <span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                stock<span class="token punctuation">.</span>LastUpdated <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">;</span>
                
                StockUpdated<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">bool</span> <span class="token function">IsMarketOpen</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> now <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>Now<span class="token punctuation">;</span>
            <span class="token keyword">var</span> timeOfDay <span class="token operator">=</span> now<span class="token punctuation">.</span>TimeOfDay<span class="token punctuation">;</span>

            <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">public</span> List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> <span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">return</span> _stocks<span class="token punctuation">.</span>Values<span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">public</span> Stock<span class="token operator">?</span> <span class="token function">GetStock</span><span class="token punctuation">(</span><span class="token keyword">string</span> symbol<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            _stocks<span class="token punctuation">.</span><span class="token function">TryGetValue</span><span class="token punctuation">(</span>symbol<span class="token punctuation">.</span><span class="token function">ToUpper</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">out</span> <span class="token keyword">var</span> stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> stock<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            _updateTimer<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
</code></pre><p>In the code above, we are simulating a Stock service that updates its information every 2 seconds. I have also modified <code class="inline-code">Program.cs</code> to create the endpoint that allows querying all stocks:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>IStockService<span class="token punctuation">,</span> StockService<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">var</span> stocksApi <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/api/stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// GET /api/stocks - Get All Stocks</span>
stocksApi<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>IStockService stockService<span class="token punctuation">,</span> ILogger<span class="token operator">&lt;</span>Program<span class="token operator">&gt;</span> logger<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">try</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> stocks <span class="token operator">=</span> stockService<span class="token punctuation">.</span><span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>stocks<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        logger<span class="token punctuation">.</span><span class="token function">LogError</span><span class="token punctuation">(</span>ex<span class="token punctuation">,</span> <span class="token string">"Failed to retrieve all stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Problem</span><span class="token punctuation">(</span><span class="token string">"Internal Server Error"</span><span class="token punctuation">,</span> statusCode<span class="token punctuation">:</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">WithName</span><span class="token punctuation">(</span><span class="token string">"GetAllStocks"</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">WithOpenApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>When starting the service and testing it, we can see how each execution produces different information for the stocks:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/fetching-stock-data-using-an-api.gif?sfvrsn=9999f32d_2" alt="Fetching stock data using an API" /></p><h2 id="creating-a-hub-for-real-time-data-transmission">Creating a Hub for Real-Time Data Transmission</h2><p>Once we have verified that the API is functioning correctly, the next step is to create a SignalR Hub, which is the communication channel used to send and receive messages. To achieve this, we need to define a class that allows clients to connect to the stocks they want to monitor.</p><p>In our example, to keep things simple, we will define methods for clients to subscribe and unsubscribe to changes in all stocks as follows:</p><p><strong>StockHub.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StockHub</span> <span class="token punctuation">:</span> Hub
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> IStockService _stockService<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">StockHub</span><span class="token punctuation">(</span>IStockService stockService<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _stockService <span class="token operator">=</span> stockService<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">SubscribeToAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> Groups<span class="token punctuation">.</span><span class="token function">AddToGroupAsync</span><span class="token punctuation">(</span>Context<span class="token punctuation">.</span>ConnectionId<span class="token punctuation">,</span> <span class="token string">"all_stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token keyword">var</span> stocks <span class="token operator">=</span> _stockService<span class="token punctuation">.</span><span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> Clients<span class="token punctuation">.</span>Caller<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token string">"AllStocksUpdate"</span><span class="token punctuation">,</span> stocks<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
   
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">UnsubscribeFromAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> Groups<span class="token punctuation">.</span><span class="token function">RemoveFromGroupAsync</span><span class="token punctuation">(</span>Context<span class="token punctuation">.</span>ConnectionId<span class="token punctuation">,</span> <span class="token string">"all_stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> stocks <span class="token operator">=</span> _stockService<span class="token punctuation">.</span><span class="token function">GetAllStocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> Clients<span class="token punctuation">.</span>Caller<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token string">"AllStocksSnapshot"</span><span class="token punctuation">,</span> stocks<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">OnConnectedAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> Clients<span class="token punctuation">.</span>Caller<span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token string">"Connected"</span><span class="token punctuation">,</span> $<span class="token string">"Connected to the Stocks Hub. ID: {Context.ConnectionId}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">OnConnectedAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">OnDisconnectedAsync</span><span class="token punctuation">(</span>Exception<span class="token operator">?</span> exception<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>            
        <span class="token keyword">await</span> <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">OnDisconnectedAsync</span><span class="token punctuation">(</span>exception<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>You may notice that there were no additional installations needed, as SignalR is part of the ASP.NET Core framework. Some important concepts from the previous code that you should know in case you haven&rsquo;t worked with SignalR are as follows:</p><ul><li><strong>Groups</strong>: The group manager class</li><li><strong>Context.ConnectionId</strong>: A temporary unique identifier for a client&rsquo;s connection to the Hub</li><li><strong>all_stocks</strong>: The group label that will receive updates about the stocks</li><li><strong>Clients.Caller.SendAsync</strong>: Sends an event called <code class="inline-code">AllStocksUpdate</code> only to the client that made the call, along with the update data</li></ul><p>After creating the Hub, we need to map it so that clients can connect to it. This should be done in <code class="inline-code">Program.cs</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddSignalR</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
app<span class="token punctuation">.</span><span class="token generic-method function">MapHub<span class="token punctuation">&lt;</span>StockHub<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"/stockHub"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> stocksApi <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">MapGroup</span><span class="token punctuation">(</span><span class="token string">"/api/stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre><p>To test the application, I created a console application that you can see in action below:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/console-application-connected-to-the-signalr-service-but-only-receiving-the-first-update.gif?sfvrsn=b6fb8205_2" alt="Console application connected to the SignalR service, but only receiving the first update" /></p><p>As you can see in the image above, when we subscribe to the Hub to receive real-time notifications, we only receive the first update even though new data is being generated behind the scenes every two seconds. This is happening because we are not sending the updates of the new data through the Hub; we are only doing it internally for the API.</p><p>It is possible to create a <strong>Background Service</strong> responsible for sending broadcasts through the Hub. This will occur when an update is detected through the event <code class="inline-code">StockUpdated</code> defined in <code class="inline-code">StockService</code>:</p><p><strong>StockUpdateBroadcastService.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StockUpdateBroadcastService</span> <span class="token punctuation">:</span> BackgroundService
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> IHubContext<span class="token operator">&lt;</span>StockHub<span class="token operator">&gt;</span> _hubContext<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> IStockService _stockService<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ILogger<span class="token operator">&lt;</span>StockUpdateBroadcastService<span class="token operator">&gt;</span> _logger<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">StockUpdateBroadcastService</span><span class="token punctuation">(</span>
        IHubContext<span class="token operator">&lt;</span>StockHub<span class="token operator">&gt;</span> hubContext<span class="token punctuation">,</span>
        IStockService stockService<span class="token punctuation">,</span>
        ILogger<span class="token operator">&lt;</span>StockUpdateBroadcastService<span class="token operator">&gt;</span> logger<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _hubContext <span class="token operator">=</span> hubContext<span class="token punctuation">;</span>
        _stockService <span class="token operator">=</span> stockService<span class="token punctuation">;</span>
        _logger <span class="token operator">=</span> logger<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">ExecuteAsync</span><span class="token punctuation">(</span>CancellationToken stoppingToken<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        _stockService<span class="token punctuation">.</span>StockUpdated <span class="token operator">+</span><span class="token operator">=</span> OnStockUpdated<span class="token punctuation">;</span>

        _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Stock update broadcast service started"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>stoppingToken<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> stoppingToken<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        
        _stockService<span class="token punctuation">.</span>StockUpdated <span class="token operator">-</span><span class="token operator">=</span> OnStockUpdated<span class="token punctuation">;</span>
        _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Stock update broadcast service stopped"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">async</span> <span class="token keyword">void</span> <span class="token function">OnStockUpdated</span><span class="token punctuation">(</span>Models<span class="token punctuation">.</span>Stock stock<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>            
            <span class="token keyword">await</span> _hubContext<span class="token punctuation">.</span>Clients<span class="token punctuation">.</span><span class="token function">Group</span><span class="token punctuation">(</span>$<span class="token string">"stock_{stock.Symbol}"</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token string">"StockUpdate"</span><span class="token punctuation">,</span> stock<span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">await</span> _hubContext<span class="token punctuation">.</span>Clients<span class="token punctuation">.</span><span class="token function">Group</span><span class="token punctuation">(</span><span class="token string">"all_stocks"</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">SendAsync</span><span class="token punctuation">(</span><span class="token string">"StockUpdate"</span><span class="token punctuation">,</span> stock<span class="token punctuation">)</span><span class="token punctuation">;</span>

            _logger<span class="token punctuation">.</span><span class="token function">LogDebug</span><span class="token punctuation">(</span>$<span class="token string">"Update for {stock.Symbol} broadcasted: ${stock.Price:F2}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            _logger<span class="token punctuation">.</span><span class="token function">LogError</span><span class="token punctuation">(</span>ex<span class="token punctuation">,</span> $<span class="token string">"Error broadcasting update for {stock.Symbol}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>It is essential to register the background service in the dependency container in <code class="inline-code">Program.cs</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddHostedService<span class="token punctuation">&lt;</span>StockUpdateBroadcastService<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>When running the application with this change, we will see that the console application is now receiving updates every 2 seconds:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/console-application-displaying-real-time-updates-powered-by-signalr.gif?sfvrsn=8f7d940d_2" alt="Console application displaying real-time updates powered by SignalR" /></p><p>Once we have verified that everything is working correctly, let&rsquo;s move on to see how to connect a .NET MAUI app to the service that communicates real-time information.</p><h2 id="creating-a-.net-maui-app-to-receive-real-time-notifications">Creating a .NET MAUI App to Receive Real-Time Notifications</h2><p>The next step is to create the .NET MAUI application to receive the stock information and display it in the UI. The framework does not include a DataGrid control to display stock data in a straightforward manner. Fortunately, the Telerik suite for .NET MAUI has a quite robust and flexible implementation of the <a target="_blank" href="https://www.telerik.com/maui-ui/documentation/controls/datagrid/overview">.NET MAUI DataGrid control</a>, which will allow us to display data quickly.</p><p>To achieve this, first, you need to create or open a .NET MAUI project, and then install the Telerik controls as per the <a target="_blank" href="https://docs.telerik.com/devtools/maui/installation/installation-methods">installation guide</a>. Next, install the following NuGet packages:</p><ul><li><code class="inline-code">CommunityToolkit.Mvvm</code></li><li><code class="inline-code">Microsoft.AspNetCore.SignalR.Client</code></li></ul><p>Similarly, it is important to have a model that represents the stock information as well as a simulated history of actions and methods that simulate initializations, updates, etc.:</p><p><strong>Stock.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">partial</span> <span class="token keyword">class</span> <span class="token class-name">Stock</span> <span class="token punctuation">:</span> ObservableObject
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">string</span> symbol <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">string</span> companyName <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> price<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> change<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> changePercent<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> openPrice<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> highPrice<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> lowPrice<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">decimal</span> previousClose<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">long</span> volume<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">long</span> marketCap<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> DateTime lastUpdated<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> isMarketOpen<span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">string</span> sector <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    
    <span class="token keyword">public</span> List<span class="token operator">&lt;</span><span class="token keyword">double</span><span class="token operator">&gt;</span> PriceHistory <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span><span class="token keyword">double</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">public</span> <span class="token keyword">string</span> PriceText <span class="token operator">=</span><span class="token operator">&gt;</span> $<span class="token string">"${Price:F2}"</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> ChangeText <span class="token operator">=</span><span class="token operator">&gt;</span> Change <span class="token operator">&gt;=</span> <span class="token number">0</span> <span class="token operator">?</span> $<span class="token string">"+${Change:F2}"</span> <span class="token punctuation">:</span> $<span class="token string">"-${Math.Abs(Change):F2}"</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> ChangePercentText <span class="token operator">=</span><span class="token operator">&gt;</span> $<span class="token string">"({(Change &gt;= 0 ? "</span><span class="token operator">+</span><span class="token string">" : "</span><span class="token string">")}{ChangePercent:F2}%)"</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> Color ChangeColor <span class="token operator">=</span><span class="token operator">&gt;</span> Change <span class="token operator">&gt;=</span> <span class="token number">0</span> <span class="token operator">?</span> Colors<span class="token punctuation">.</span>Green <span class="token punctuation">:</span> Colors<span class="token punctuation">.</span>Red<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> VolumeText <span class="token operator">=</span><span class="token operator">&gt;</span> Volume<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"N0"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> MarketCapText <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">FormatMarketCap</span><span class="token punctuation">(</span>MarketCap<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> LastUpdatedText <span class="token operator">=</span><span class="token operator">&gt;</span> LastUpdated<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"HH:mm:ss"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">string</span> <span class="token function">FormatMarketCap</span><span class="token punctuation">(</span><span class="token keyword">long</span> marketCap<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>marketCap <span class="token operator">&gt;=</span> 1_000_000_000_000<span class="token punctuation">)</span>
            <span class="token keyword">return</span> $<span class="token string">"${marketCap / 1_000_000_000_000.0:F1}T"</span><span class="token punctuation">;</span>
        <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>marketCap <span class="token operator">&gt;=</span> 1_000_000_000<span class="token punctuation">)</span>
            <span class="token keyword">return</span> $<span class="token string">"${marketCap / 1_000_000_000.0:F1}B"</span><span class="token punctuation">;</span>
        <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>marketCap <span class="token operator">&gt;=</span> 1_000_000<span class="token punctuation">)</span>
            <span class="token keyword">return</span> $<span class="token string">"${marketCap / 1_000_000.0:F1}M"</span><span class="token punctuation">;</span>
        <span class="token keyword">else</span>
            <span class="token keyword">return</span> $<span class="token string">"${marketCap:N0}"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">UpdateFrom</span><span class="token punctuation">(</span>Stock newStock<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Symbol <span class="token operator">=</span> newStock<span class="token punctuation">.</span>Symbol<span class="token punctuation">;</span>
        CompanyName <span class="token operator">=</span> newStock<span class="token punctuation">.</span>CompanyName<span class="token punctuation">;</span>
        
        <span class="token function">AddPriceToHistory</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span>newStock<span class="token punctuation">.</span>Price<span class="token punctuation">)</span><span class="token punctuation">;</span>

        Price <span class="token operator">=</span> newStock<span class="token punctuation">.</span>Price<span class="token punctuation">;</span>
        Change <span class="token operator">=</span> newStock<span class="token punctuation">.</span>Change<span class="token punctuation">;</span>
        ChangePercent <span class="token operator">=</span> newStock<span class="token punctuation">.</span>ChangePercent<span class="token punctuation">;</span>
        OpenPrice <span class="token operator">=</span> newStock<span class="token punctuation">.</span>OpenPrice<span class="token punctuation">;</span>
        HighPrice <span class="token operator">=</span> newStock<span class="token punctuation">.</span>HighPrice<span class="token punctuation">;</span>
        LowPrice <span class="token operator">=</span> newStock<span class="token punctuation">.</span>LowPrice<span class="token punctuation">;</span>
        PreviousClose <span class="token operator">=</span> newStock<span class="token punctuation">.</span>PreviousClose<span class="token punctuation">;</span>
        Volume <span class="token operator">=</span> newStock<span class="token punctuation">.</span>Volume<span class="token punctuation">;</span>
        MarketCap <span class="token operator">=</span> newStock<span class="token punctuation">.</span>MarketCap<span class="token punctuation">;</span>
        LastUpdated <span class="token operator">=</span> newStock<span class="token punctuation">.</span>LastUpdated<span class="token punctuation">;</span>
        IsMarketOpen <span class="token operator">=</span> newStock<span class="token punctuation">.</span>IsMarketOpen<span class="token punctuation">;</span>
        Sector <span class="token operator">=</span> newStock<span class="token punctuation">.</span>Sector<span class="token punctuation">;</span>
        
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>PriceText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>ChangeText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>ChangePercentText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>ChangeColor<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>VolumeText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>MarketCapText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">OnPropertyChanged</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>LastUpdatedText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">AddPriceToHistory</span><span class="token punctuation">(</span><span class="token keyword">double</span> price<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        PriceHistory<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>price<span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token keyword">if</span> <span class="token punctuation">(</span>PriceHistory<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> <span class="token number">20</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            PriceHistory<span class="token punctuation">.</span><span class="token function">RemoveAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">InitializePriceHistory</span><span class="token punctuation">(</span><span class="token keyword">double</span> initialPrice<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>PriceHistory<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>                
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">double</span> variation <span class="token operator">=</span> <span class="token punctuation">(</span>Random<span class="token punctuation">.</span>Shared<span class="token punctuation">.</span><span class="token function">NextDouble</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">0.5</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">0.1</span><span class="token punctuation">;</span>
                PriceHistory<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>initialPrice <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">+</span> variation<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>With the model ready in the .NET MAUI application, we can continue with the connection service.</p><h2 id="creating-the-service-to-get-signalr-notifications">Creating the Service to Get SignalR Notifications</h2><p>In order to connect a client to a SignalR Hub, it is necessary to create a <code class="inline-code">HubConnection</code>. In our .NET MAUI app, we will create a class that will handle this connection, as well as properties and events that will allow the application to make the necessary changes when it receives an update to the data.</p><p>The class looks as follows:</p><p><strong>StockSignalRService.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">partial</span> <span class="token keyword">class</span> <span class="token class-name">StockSignalRService</span> <span class="token punctuation">:</span> ObservableObject<span class="token punctuation">,</span> IDisposable
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> HubConnection _connection<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> _isConnected <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">string</span> connectionStatus <span class="token operator">=</span> <span class="token string">"Disconnected"</span><span class="token punctuation">;</span>

    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> isConnecting <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> ObservableCollection<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> Stocks <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsConnected <span class="token operator">=</span><span class="token operator">&gt;</span> _isConnected<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">event</span> Action<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span><span class="token operator">?</span> MessageReceived<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">event</span> Action<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span><span class="token operator">?</span> StockUpdated<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">StockSignalRService</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> hubUrl <span class="token operator">=</span> DeviceInfo<span class="token punctuation">.</span>Platform <span class="token operator">==</span> DevicePlatform<span class="token punctuation">.</span>Android
            <span class="token operator">?</span> <span class="token string">"http://10.0.2.2:5031/stockHub"</span>
            <span class="token punctuation">:</span> <span class="token string">"http://localhost:5134/stockHub"</span><span class="token punctuation">;</span>

        _connection <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HubConnectionBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithUrl</span><span class="token punctuation">(</span>hubUrl<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithAutomaticReconnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">ConfigureEventHandlers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">ConfigureEventHandlers</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        _connection<span class="token punctuation">.</span>Closed <span class="token operator">+</span><span class="token operator">=</span> OnDisconnected<span class="token punctuation">;</span>
        _connection<span class="token punctuation">.</span>Reconnected <span class="token operator">+</span><span class="token operator">=</span> OnReconnected<span class="token punctuation">;</span>
        _connection<span class="token punctuation">.</span>Reconnecting <span class="token operator">+</span><span class="token operator">=</span> OnReconnecting<span class="token punctuation">;</span>
        
        _connection<span class="token punctuation">.</span><span class="token generic-method function">On<span class="token punctuation">&lt;</span>Stock<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"StockUpdate"</span><span class="token punctuation">,</span> OnStockUpdate<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _connection<span class="token punctuation">.</span>On<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token string">"AllStocksUpdate"</span><span class="token punctuation">,</span> OnAllStocksUpdate<span class="token punctuation">)</span><span class="token punctuation">;</span>            
        _connection<span class="token punctuation">.</span>On<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token string">"AllStocksSnapshot"</span><span class="token punctuation">,</span> OnAllStocksSnapshot<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _connection<span class="token punctuation">.</span><span class="token generic-method function">On<span class="token punctuation">&lt;</span><span class="token keyword">string</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"Connected"</span><span class="token punctuation">,</span> OnConnectedMessage<span class="token punctuation">)</span><span class="token punctuation">;</span>            
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">ConnectAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>_isConnected<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            IsConnecting <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> <span class="token string">"Connecting..."</span><span class="token punctuation">;</span>

            <span class="token keyword">await</span> _connection<span class="token punctuation">.</span><span class="token function">StartAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            _isConnected <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> <span class="token string">"Connected"</span><span class="token punctuation">;</span>
            
            <span class="token keyword">await</span> <span class="token function">SubscribeToAllStocksAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            ConnectionStatus <span class="token operator">=</span> $<span class="token string">"Error: {ex.Message}"</span><span class="token punctuation">;</span>
            MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>$<span class="token string">"Connection error: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">finally</span>
        <span class="token punctuation">{</span>
            IsConnecting <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">DisconnectAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>_isConnected<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> _connection<span class="token punctuation">.</span><span class="token function">StopAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            _isConnected <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> <span class="token string">"Disconnected"</span><span class="token punctuation">;</span>
            
            MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            <span class="token punctuation">{</span>
                Stocks<span class="token punctuation">.</span><span class="token function">Clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>$<span class="token string">"Error while disconnecting: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">SubscribeToAllStocksAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>_isConnected<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> _connection<span class="token punctuation">.</span><span class="token function">InvokeAsync</span><span class="token punctuation">(</span><span class="token string">"SubscribeToAllStocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span><span class="token string">"Subscribed to all stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>$<span class="token string">"Subscription error: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">GetAllStocksAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>_isConnected<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> _connection<span class="token punctuation">.</span><span class="token function">InvokeAsync</span><span class="token punctuation">(</span><span class="token string">"GetAllStocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>$<span class="token string">"Error retrieving stocks: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token preprocessor property">#<span class="token directive keyword">region</span> Event Handlers</span>

    <span class="token keyword">private</span> Task <span class="token function">OnDisconnected</span><span class="token punctuation">(</span>Exception<span class="token operator">?</span> exception<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            _isConnected <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> exception <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">?</span> $<span class="token string">"Disconnected: {exception.Message}"</span> <span class="token punctuation">:</span> <span class="token string">"Disconnected"</span><span class="token punctuation">;</span>
            Stocks<span class="token punctuation">.</span><span class="token function">Clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> Task <span class="token function">OnReconnected</span><span class="token punctuation">(</span><span class="token keyword">string</span><span class="token operator">?</span> connectionId<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            _isConnected <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> <span class="token string">"Reconnected"</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        _ <span class="token operator">=</span> Task<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">await</span> <span class="token function">SubscribeToAllStocksAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> Task <span class="token function">OnReconnecting</span><span class="token punctuation">(</span>Exception<span class="token operator">?</span> exception<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            _isConnected <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
            ConnectionStatus <span class="token operator">=</span> <span class="token string">"Reconnecting..."</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> Task<span class="token punctuation">.</span>CompletedTask<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">OnStockUpdate</span><span class="token punctuation">(</span>Stock stock<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> existingStock <span class="token operator">=</span> Stocks<span class="token punctuation">.</span><span class="token function">FirstOrDefault</span><span class="token punctuation">(</span>s <span class="token operator">=</span><span class="token operator">&gt;</span> s<span class="token punctuation">.</span>Symbol <span class="token operator">==</span> stock<span class="token punctuation">.</span>Symbol<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>existingStock <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                existingStock<span class="token punctuation">.</span><span class="token function">UpdateFrom</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">else</span>
            <span class="token punctuation">{</span>
                Stocks<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>

            StockUpdated<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">OnAllStocksUpdate</span><span class="token punctuation">(</span>List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> stocks<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> stock <span class="token keyword">in</span> stocks<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> existingStock <span class="token operator">=</span> Stocks<span class="token punctuation">.</span><span class="token function">FirstOrDefault</span><span class="token punctuation">(</span>s <span class="token operator">=</span><span class="token operator">&gt;</span> s<span class="token punctuation">.</span>Symbol <span class="token operator">==</span> stock<span class="token punctuation">.</span>Symbol<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>existingStock <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    existingStock<span class="token punctuation">.</span><span class="token function">UpdateFrom</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                <span class="token keyword">else</span>
                <span class="token punctuation">{</span>
                    Stocks<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">OnAllStocksSnapshot</span><span class="token punctuation">(</span>List<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> stocks<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MainThread<span class="token punctuation">.</span><span class="token function">BeginInvokeOnMainThread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            Stocks<span class="token punctuation">.</span><span class="token function">Clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> stock <span class="token keyword">in</span> stocks<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>                
                stock<span class="token punctuation">.</span><span class="token function">InitializePriceHistory</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span>stock<span class="token punctuation">.</span>Price<span class="token punctuation">)</span><span class="token punctuation">;</span>
                Stocks<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>stock<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>$<span class="token string">"Snapshot received for {stocks.Count} stocks"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">OnConnectedMessage</span><span class="token punctuation">(</span><span class="token keyword">string</span> message<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        MessageReceived<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token preprocessor property">#<span class="token directive keyword">endregion</span></span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _connection<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">DisposeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the above code, we should highlight a few points:</p><ul><li>If you follow the exercise, you need to change the port according to your service.</li><li>Events <code class="inline-code">MessageReceived</code> and <code class="inline-code">StockUpdated</code> are created so that an external class can know when a message has been generated and when there is an update to the stocks.</li><li><code class="inline-code">HubConnectionBuilder</code> is used to create the HubConnection.</li><li>The events to which the client will subscribe from the Hub are registered through <code class="inline-code">_connection.On</code>. You will notice that the names are the same as those defined in <code class="inline-code">StockHub.cs</code> of the Web API project.</li><li>When a client connects through <code class="inline-code">ConnectAsync</code>, the subscription is initiated through <code class="inline-code">_connection.StartAsync()</code>, and it automatically subscribes to listen to all stocks via <code class="inline-code">SubscribeToAllStocksAsync()</code>.</li></ul><p>With the SignalR message subscription service, the next step is to create the page&rsquo;s view model.</p><h2 id="creating-the-applications-viewmodel">Creating the Application&rsquo;s ViewModel</h2><p>Creating the viewmodel for the project is very similar to any .NET MAUI viewmodel using the MVVM Toolkit, with the difference being the use of the service <code class="inline-code">StockSignalRService</code>:</p><p><strong>MainViewModel.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">partial</span> <span class="token keyword">class</span> <span class="token class-name">MainViewModel</span> <span class="token punctuation">:</span> ObservableObject
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> isLoading <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
    <span class="token punctuation">[</span>ObservableProperty<span class="token punctuation">]</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> isConnected <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">readonly</span> StockSignalRService _stockService<span class="token punctuation">;</span>
    
    <span class="token keyword">public</span> ObservableCollection<span class="token operator">&lt;</span>Stock<span class="token operator">&gt;</span> Stocks <span class="token operator">=</span><span class="token operator">&gt;</span> _stockService<span class="token punctuation">.</span>Stocks<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>StockSignalRService stockService<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _stockService <span class="token operator">=</span> stockService<span class="token punctuation">;</span>            
        
        _ <span class="token operator">=</span> Task<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">await</span> <span class="token function">InitializeConnectionAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">async</span> Task <span class="token function">InitializeConnectionAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>                
            <span class="token keyword">await</span> _stockService<span class="token punctuation">.</span><span class="token function">ConnectAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            Debug<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Connection Error: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>                           
<span class="token punctuation">}</span>
</code></pre><p>In the above code, let&rsquo;s highlight a few things:</p><ul><li>The service <code class="inline-code">StockSignalRService</code> is used to connect to the SignalR Hub.</li><li>A collection of type <code class="inline-code">Stock</code> is defined, which is linked to the <code class="inline-code">Stocks</code> property of the service, meaning that when there is a change notification from the service, the collection will automatically have these new values, updating the UI automatically.</li><li>Once the page is displayed, the method <code class="inline-code">ConnectAsync</code> from the service is invoked, which, as a reminder, subscribes to updates of all stocks.</li></ul><h2 id="creating-the-page-with-the-datagrid-in-the-application">Creating the Page with the DataGrid in the Application</h2><p>The last part of the project is creating the ContentPage that will show real-time updated information.</p><p>Using a component like a CollectionView to display data, in addition to implementing functionalities like sorting, grouping, filtering, etc., can be quite a headache. Fortunately, in the Telerik control suite, we have the DataGrid control, which includes the aforementioned functionalities and many more natively.</p><p>For our example, we will use a <code class="inline-code">RadDataGrid</code> that will take up the entire page, although you could modify this page to show information such as the time of the last update, the number of updates, choosing which stocks to display, etc.</p><p>The page in its first version will look like this:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Grid</span><span class="token punctuation">&gt;</span></span>
    
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>RadDataGrid</span>
        <span class="token attr-name"><span class="token namespace">x:</span>Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>StocksDataGrid<span class="token punctuation">"</span></span>
        <span class="token attr-name">AutoGenerateColumns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
        <span class="token attr-name">Background</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>White<span class="token punctuation">"</span></span>
        <span class="token attr-name">GridLinesColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>LightGray<span class="token punctuation">"</span></span>
        <span class="token attr-name">ItemsSource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding Stocks}<span class="token punctuation">"</span></span>
        <span class="token attr-name">RowHeight</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>RadDataGrid</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Grid</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>In the code behind the page, we need to modify the code to receive the viewmodel and bind it to the <code class="inline-code">BindingContext</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">partial</span> <span class="token keyword">class</span> <span class="token class-name">MainPage</span> <span class="token punctuation">:</span> ContentPage
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> MainViewModel _viewModel<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">MainPage</span><span class="token punctuation">(</span>MainViewModel viewModel<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">InitializeComponent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        _viewModel <span class="token operator">=</span> viewModel<span class="token punctuation">;</span>
        BindingContext <span class="token operator">=</span> _viewModel<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnDisappearing</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">OnDisappearing</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Finally, you need to modify <code class="inline-code">MauiProgram.cs</code> to allow the dependency container to resolve the dependency injections:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MauiProgram</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> MauiApp <span class="token function">CreateMauiApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> builder <span class="token operator">=</span> MauiApp<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
       <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

        builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>StockSignalRService<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>MainViewModel<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            
        builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>MainPage<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            

<span class="token preprocessor property">#<span class="token directive keyword">if</span> DEBUG</span>
        builder<span class="token punctuation">.</span>Logging<span class="token punctuation">.</span><span class="token function">AddDebug</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token preprocessor property">#<span class="token directive keyword">endif</span></span>

        <span class="token keyword">return</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Once we have done the above, we can see the page in action:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/a-cross-platform-application-built-with-net-maui-delivering-real-time-updates-powered-by-signalr.gif?sfvrsn=3d86ed7e_2" alt="A cross-platform application built with .NET MAUI, delivering real-time updates powered by SignalR" /></p><p>Although the Grid shows some data oddly, we have verified that the update occurs every 2 seconds correctly, as you can see in the <strong>PriceText</strong>, <strong>ChangeText</strong> and <strong>ChangePercentText</strong> columns.</p><h2 id="improving-the-datagrid-for-better-information-display">Improving the DataGrid for Better Information Display</h2><p>One of the things I love about the Telerik DataGrid control is its customization options. This translates to being able to decide what columns to include in the DataGrid and format them according to what we need. To do this, we must follow these steps:</p><ol><li>Place the property <code class="inline-code">AutoGenerateColumns</code> in <code class="inline-code">False</code>.</li><li>Add a section <code class="inline-code">&lt;telerik:RadDataGrid.Columns&gt;</code> inside the RadDataGrid definition.</li><li>Create the columns according to the type of data you want to display (in our case, they will be of the type <code class="inline-code">DataGridTextColumn</code>).</li><li>Configure the properties of each column according to the operations we want to allow (sorting, filtering, etc.).</li></ol><p>The code for our example will look like this:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ContentPage.Resources</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ResourceDictionary</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name"><span class="token namespace">x:</span>Key</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>HeaderStyle<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token style language-css">
            &lt;Setter Property=<span class="token string">"BackgroundColor"</span> Value=<span class="token string">"DarkBlue"</span> /&gt;
            &lt;Setter Property=<span class="token string">"TextColor"</span> Value=<span class="token string">"White"</span> /&gt;
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Style</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ResourceDictionary</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ContentPage.Resources</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Grid</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>RadDataGrid</span>
        <span class="token attr-name"><span class="token namespace">x:</span>Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>StocksDataGrid<span class="token punctuation">"</span></span>
        <span class="token attr-name">AutoGenerateColumns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>False<span class="token punctuation">"</span></span>
        <span class="token attr-name">Background</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>White<span class="token punctuation">"</span></span>
        <span class="token attr-name">GridLinesColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>LightGray<span class="token punctuation">"</span></span>
        <span class="token attr-name">ItemsSource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding Stocks}<span class="token punctuation">"</span></span>
        <span class="token attr-name">RowHeight</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>RadDataGrid.Columns</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserGroup</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Symbol<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Symbol<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserGroup</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Company Name<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>CompanyName<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Price<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Price<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span>
                            <span class="token attr-name">HorizontalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>End<span class="token punctuation">"</span></span>
                            <span class="token attr-name">Text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding PriceText}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">VerticalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Center<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Change<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Change<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span>
                            <span class="token attr-name">HorizontalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>End<span class="token punctuation">"</span></span>
                            <span class="token attr-name">Text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding ChangeText}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">TextColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding ChangeColor}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">VerticalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Center<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>% Change<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ChangePercent<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span>
                            <span class="token attr-name">HorizontalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>End<span class="token punctuation">"</span></span>
                            <span class="token attr-name">Text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding ChangePercentText}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">TextColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding ChangeColor}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">VerticalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Center<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Volume<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Volume<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span>
                            <span class="token attr-name">HorizontalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>End<span class="token punctuation">"</span></span>
                            <span class="token attr-name">Text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding VolumeText}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">VerticalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Center<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Market Cap<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MarketCap<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span>
                            <span class="token attr-name">HorizontalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>End<span class="token punctuation">"</span></span>
                            <span class="token attr-name">Text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{Binding MarketCapText}<span class="token punctuation">"</span></span>
                            <span class="token attr-name">VerticalOptions</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Center<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DataTemplate</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.CellContentTemplate</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>

            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
                <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserGroup</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>True<span class="token punctuation">"</span></span>
                <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Sector<span class="token punctuation">"</span></span>
                <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Sector<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>RadDataGrid.Columns</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>RadDataGrid</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Grid</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Once the previous modifications are done, you can see that the DataGrid looks much better:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/datagrid-with-columns-optimized-for-clear-information-display.gif?sfvrsn=9bac2622_2" alt="DataGrid with columns optimized for clear information display" /></p><h2 id="creating-a-custom-column-to-display-a-different-format">Creating a Custom Column to Display a Different Format</h2><p>It is very likely that at some point you want to display a custom chart in your DataGrid. For example, in stock applications, it is common to show price trends through charts. The Telerik DataGrid control is rendered using the SkiaSharp library, which allows us to create charts as complex as we want. There are multiple <a target="_blank" href="https://www.telerik.com/blogs/your-first-steps-skiasharp-net-maui">tutorials on using SkiaSharp in .NET MAUI</a> available online.</p><p>For the case of the DataGrid, we must use as a base the class <code class="inline-code">DataGridCellRenderer</code>, which contains a method called <code class="inline-code">RenderContainer</code> that we can use to render the custom content for the current element. In our example, I created a class called <code class="inline-code">SparklineRenderer</code> that looks like this:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SparklineRenderer</span> <span class="token punctuation">:</span> DataGridCellRenderer
<span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">RenderContainer</span><span class="token punctuation">(</span>DataGridCellRendererRenderContext renderContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>renderContext<span class="token punctuation">.</span>Item <span class="token keyword">is</span> Stock stock <span class="token operator">&amp;&amp;</span> 
            renderContext <span class="token keyword">is</span> DataGridSkiaSharpCellRendererRenderContext skRenderContext<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token function">DrawSparkline</span><span class="token punctuation">(</span>stock<span class="token punctuation">,</span> skRenderContext<span class="token punctuation">,</span> skRenderContext<span class="token punctuation">.</span>Bounds<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">DrawSparkline</span><span class="token punctuation">(</span>Stock stock<span class="token punctuation">,</span> DataGridSkiaSharpCellRendererRenderContext context<span class="token punctuation">,</span> Microsoft<span class="token punctuation">.</span>Maui<span class="token punctuation">.</span>Graphics<span class="token punctuation">.</span>Rect bounds<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        <span class="token keyword">var</span> priceHistory <span class="token operator">=</span> stock<span class="token punctuation">.</span>PriceHistory<span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>priceHistory <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> priceHistory<span class="token punctuation">.</span>Count <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>

        <span class="token keyword">double</span> padding <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span>
        <span class="token keyword">double</span> chartWidth <span class="token operator">=</span> bounds<span class="token punctuation">.</span>Width <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> padding<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">double</span> chartHeight <span class="token operator">=</span> bounds<span class="token punctuation">.</span>Height <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> padding<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">double</span> chartX <span class="token operator">=</span> bounds<span class="token punctuation">.</span>X <span class="token operator">+</span> padding<span class="token punctuation">;</span>
        <span class="token keyword">double</span> chartY <span class="token operator">=</span> bounds<span class="token punctuation">.</span>Y <span class="token operator">+</span> padding<span class="token punctuation">;</span>

        <span class="token keyword">double</span> displayScale <span class="token operator">=</span> context<span class="token punctuation">.</span>DisplayScale<span class="token punctuation">;</span>
        
        <span class="token keyword">double</span> minPrice <span class="token operator">=</span> priceHistory<span class="token punctuation">.</span><span class="token function">Min</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">double</span> maxPrice <span class="token operator">=</span> priceHistory<span class="token punctuation">.</span><span class="token function">Max</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">double</span> priceRange <span class="token operator">=</span> maxPrice <span class="token operator">-</span> minPrice<span class="token punctuation">;</span>
                
        <span class="token keyword">if</span> <span class="token punctuation">(</span>priceRange <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
            priceRange <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
        
        <span class="token keyword">var</span> points <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>SKPoint<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> priceHistory<span class="token punctuation">.</span>Count<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">double</span> x <span class="token operator">=</span> chartX <span class="token operator">+</span> <span class="token punctuation">(</span>i <span class="token operator">*</span> chartWidth <span class="token operator">/</span> <span class="token punctuation">(</span>priceHistory<span class="token punctuation">.</span>Count <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">double</span> normalizedValue <span class="token operator">=</span> <span class="token punctuation">(</span>priceHistory<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">-</span> minPrice<span class="token punctuation">)</span> <span class="token operator">/</span> priceRange<span class="token punctuation">;</span>
            <span class="token keyword">double</span> y <span class="token operator">=</span> chartY <span class="token operator">+</span> chartHeight <span class="token operator">-</span> <span class="token punctuation">(</span>normalizedValue <span class="token operator">*</span> chartHeight<span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            points<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">SKPoint</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span><span class="token punctuation">(</span>x <span class="token operator">*</span> displayScale<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span><span class="token punctuation">(</span>y <span class="token operator">*</span> displayScale<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        
        <span class="token keyword">double</span> totalChange <span class="token operator">=</span> priceHistory<span class="token punctuation">.</span><span class="token function">Last</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> priceHistory<span class="token punctuation">.</span><span class="token function">First</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        SKColor lineColor <span class="token operator">=</span> totalChange <span class="token operator">&gt;=</span> <span class="token number">0</span> <span class="token operator">?</span> 
            <span class="token keyword">new</span> <span class="token class-name">SKColor</span><span class="token punctuation">(</span><span class="token number">34</span><span class="token punctuation">,</span> <span class="token number">197</span><span class="token punctuation">,</span> <span class="token number">94</span><span class="token punctuation">)</span> <span class="token punctuation">:</span>
            <span class="token keyword">new</span> <span class="token class-name">SKColor</span><span class="token punctuation">(</span><span class="token number">239</span><span class="token punctuation">,</span> <span class="token number">68</span><span class="token punctuation">,</span> <span class="token number">68</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SKPaint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            paint<span class="token punctuation">.</span>Color <span class="token operator">=</span> lineColor<span class="token punctuation">;</span>
            paint<span class="token punctuation">.</span>StrokeWidth <span class="token operator">=</span> <span class="token number">1.5f</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>displayScale<span class="token punctuation">;</span>
            paint<span class="token punctuation">.</span>Style <span class="token operator">=</span> SKPaintStyle<span class="token punctuation">.</span>Stroke<span class="token punctuation">;</span>
            paint<span class="token punctuation">.</span>IsAntialias <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>

            <span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SKPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>points<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    path<span class="token punctuation">.</span><span class="token function">MoveTo</span><span class="token punctuation">(</span>points<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> points<span class="token punctuation">.</span>Count<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
                    <span class="token punctuation">{</span>
                        path<span class="token punctuation">.</span><span class="token function">LineTo</span><span class="token punctuation">(</span>points<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">}</span>
                    context<span class="token punctuation">.</span>Canvas<span class="token punctuation">.</span><span class="token function">DrawPath</span><span class="token punctuation">(</span>path<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        
        <span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SKPaint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            paint<span class="token punctuation">.</span>Color <span class="token operator">=</span> lineColor<span class="token punctuation">;</span>
            paint<span class="token punctuation">.</span>Style <span class="token operator">=</span> SKPaintStyle<span class="token punctuation">.</span>Fill<span class="token punctuation">;</span>
            paint<span class="token punctuation">.</span>IsAntialias <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>

            <span class="token keyword">float</span> dotRadius <span class="token operator">=</span> <span class="token number">1f</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>displayScale<span class="token punctuation">;</span>
            <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> point <span class="token keyword">in</span> points<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                context<span class="token punctuation">.</span>Canvas<span class="token punctuation">.</span><span class="token function">DrawCircle</span><span class="token punctuation">(</span>point<span class="token punctuation">.</span>X<span class="token punctuation">,</span> point<span class="token punctuation">.</span>Y<span class="token punctuation">,</span> dotRadius<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        
        <span class="token keyword">if</span> <span class="token punctuation">(</span>points<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SKPaint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                paint<span class="token punctuation">.</span>Color <span class="token operator">=</span> lineColor<span class="token punctuation">;</span>
                paint<span class="token punctuation">.</span>Style <span class="token operator">=</span> SKPaintStyle<span class="token punctuation">.</span>Fill<span class="token punctuation">;</span>
                paint<span class="token punctuation">.</span>IsAntialias <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>

                <span class="token keyword">var</span> lastPoint <span class="token operator">=</span> points<span class="token punctuation">.</span><span class="token function">Last</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">float</span> highlightRadius <span class="token operator">=</span> <span class="token number">2f</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>displayScale<span class="token punctuation">;</span>
                context<span class="token punctuation">.</span>Canvas<span class="token punctuation">.</span><span class="token function">DrawCircle</span><span class="token punctuation">(</span>lastPoint<span class="token punctuation">.</span>X<span class="token punctuation">,</span> lastPoint<span class="token punctuation">.</span>Y<span class="token punctuation">,</span> highlightRadius<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s examine some important points from the previous code:</p><ol><li>It inherits from <code class="inline-code">DataGridCellRenderer</code>, a class essential for displaying content in the DataGrid.</li><li>The method <code class="inline-code">RenderContainer</code> is used to draw the content.</li><li>The stock history is obtained, which is a list of simulated previous values.</li><li>An area to draw the chart is defined.</li><li>In the first loop <code class="inline-code">for</code>, the history is transformed into normalized <code class="inline-code">X</code> and <code class="inline-code">Y</code> coordinates according to the chart area.</li><li>Through <code class="inline-code">totalChange</code> and <code class="inline-code">lineColor</code>, it is determined whether the color should be green or red.</li><li>A <code class="inline-code">SKPath</code> is created with the calculated points, and the chart is drawn.</li><li>Small circles are added using <code class="inline-code">DrawCircle</code> to highlight each individual point.</li><li>A larger circle is drawn to represent the most recent value.</li></ol><p>Once we have the new renderer, we will use it in the DataGrid as follows:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
    <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>False<span class="token punctuation">"</span></span>
    <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>False<span class="token punctuation">"</span></span>
    <span class="token attr-name">CellRenderer</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource SparklineRenderer}<span class="token punctuation">"</span></span>
    <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Trend<span class="token punctuation">"</span></span>
    <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Symbol<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>With the new cell added to the DataGrid, we will see that the custom chart appears in the UI:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/a-datagrid-control-with-a-custom-column-to-display-a-stock-trend.gif?sfvrsn=cc5b43ef_2" alt="A DataGrid control with a custom column to display a stock trend" /></p><p>Undoubtedly, being able to customize the content of the cells opens a new door of opportunity to present quick and accurate information to app users.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this post, you have learned how to create SignalR-based applications to achieve real-time communication, something crucial in stock, cryptocurrency or similar applications.</p><p>You have also seen how the Telerik UI for .NET MAUI DataGrid control is an excellent option for displaying this content, not only for its properties to work with data but also for its flexibility in customization. It is your time to provide your clients with unique data-driven experiences.</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-maui" class="Btn">Try UI for .NET MAUI</a></p><img src="https://feeds.telerik.com/link/23052/17211761.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
