<?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:0cf71322-37c4-4118-adf7-37d60ae4a24e;id=922</id>
  <updated>2026-09-09T10:43:31Z</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:bd100e25-ddba-4ff3-b841-0f92766ab6d4</id>
    <title type="text">Understanding Race Conditions in ASP.NET Core</title>
    <summary type="text">Explore the concept of race conditions in ASP.NET Core, understand how to identify them in practice and learn strategies to protect applications against this type of issue.</summary>
    <published>2026-08-11T18:04:18Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17414975/understanding-race-conditions-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Explore the concept of race conditions in ASP.NET Core, understand how to identify them in practice and learn strategies to protect applications against this type of issue.</span></p><p>Two requests arrive at your application at the exact same moment. Both read the same data, both attempt to update it, and everything appears to work correctly. Yet one update silently overwrites the other. This is a classic race condition. In this post, you&rsquo;ll see how these problems arise and how to prevent them using common concurrency control techniques.</p><p>Working with systems that execute multiple operations in milliseconds is not a distant reality. On the contrary, in medium- and large-scale applications, this is a common scenario.</p><p>But along with this level of concurrency comes a problem that is often overlooked: race conditions. When not handled properly, they can lead to inconsistencies and even silently corrupt data.</p><p>In this post, we will explore the concept of race conditions, understand how to identify them in practice, and discuss strategies to protect our applications against this type of issue.</p><h2 id="what-is-a-race-condition">What Is a Race Condition?</h2><p>According to Microsoft documentation on <a target="_blank" href="https://learn.microsoft.com/en-us/troubleshoot/developer/visualstudio/visual-basic/language-compilers/race-conditions-deadlocks">race conditions and deadlocks</a>, a race condition occurs when two threads access and modify a shared resource at the same time. The final result depends on the order in which these operations are executed.</p><p>The problem is that this order is not guaranteed; it can vary with each execution. This leads to unpredictable behavior, such as inconsistent data, lost updates or invalid states.</p><p>Imagine two requests trying to update an account balance at the same time. Both read the same initial value, perform separate calculations and save the result. Depending on which one saves last, one update may overwrite the other, even if both were performed correctly in isolation.</p><h2 id="when-do-race-conditions-occur">When Do Race Conditions Occur?</h2><p>Race conditions don&rsquo;t appear &ldquo;out of nowhere.&rdquo; They usually arise from some very common code and architectural patterns. Below, we&rsquo;ll look at two of the best-known: Read-Modify-Write and Check-Then-Act.</p><h3 id="read-modify-write">Read-Modify-Write</h3><p>The idea here is simple: you read a value, make some modification to it and then write it back. The problem starts when two or more executions do this at the same time. Consider the image below:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/read-modify-write-problem.png?sfvrsn=c7122620_2" title="read modify write problem" alt="Read modify write problem" /></p><p>Note that both readings happen simultaneously, returning the value of 50. However, Thread A adds 20 to the initial value, while Thread B adds 10. The problem occurs when Thread A updates the initial value (50) to 70, while Thread B updates it 1 second later with the value of 60, completely disregarding the value previously added by Thread A.</p><p>The result is an incorrectly calculated value, something that would certainly cause losses in a production environment.</p><h3 id="check-then-act">Check-Then-Act</h3><p>The Check-Then-Act pattern is often even more subtle than the previous pattern. The problem with this pattern is not in updating a value, but in making a decision based on a state that may change before the action takes place. Consider the image below:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/check-then-act-problem.png?sfvrsn=bb7cec48_2" title="check then act problem" alt="Check Then Act problem" /></p><p>In this case we have two threads that check the stock quantity of a product. In both queries, the quantity is 1, and even though there is a check, both purchases were executed based on an invalid state. After all, when Thread A executed the purchase, there was no longer any quantity available for Thread B. If this problem had occurred in a real environment, the client of Thread B would have been left without the product.</p><h2 id="avoiding-race-conditions">Avoiding Race Conditions</h2><p>Now that we&rsquo;ve learned how to identify a race condition, let&rsquo;s understand how to prevent it from happening. There are different strategies to protect ASP.NET Core applications against concurrency issues, and choosing the most appropriate approach depends on the scenario.</p><p>The main point is to understand that any concurrent operation involving shared state needs to be handled with care.</p><h2 id="understanding-the-concept-of-thread-safety">Understanding the Concept of Thread Safety</h2><p>Thread safety is the ability of code or a resource to function correctly even when accessed simultaneously by multiple threads.</p><p>We can say that thread-safe code means that data is not corrupted, the state remains valid, and the behavior does not depend on the order of execution of the threads.</p><h3 id="using-locks">1. Using Locks</h3><p>A lock is a mechanism used to make an operation thread-safe. It allows only one thread to execute a given piece of code at a time.</p><p>Consider the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<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">public</span> <span class="token keyword">class</span> <span class="token class-name">ProductService</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">object</span> _lock <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 comment">// Simulating a stock quantity</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> _stock <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token keyword">string</span> customer<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">"{customer} is waiting to enter the critical section..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">lock</span> <span class="token punctuation">(</span>_lock<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">"{customer} entered the critical section."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span>_stock <span class="token operator">&lt;=</span> <span class="token number">0</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">"{customer} could not complete the purchase. Product out of stock."</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 punctuation">}</span>

            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"{customer} is processing the purchase..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Simulating a slow operation</span>
            Thread<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _stock<span class="token operator">--</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">"{customer} completed the purchase."</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">"Remaining stock: {_stock}"</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">"{customer} left the critical section."</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">Program</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 function">Main</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">ProductService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> task1 <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 punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token string">"Customer A"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> task2 <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 punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token string">"Customer B"</span><span class="token punctuation">)</span><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">WhenAll</span><span class="token punctuation">(</span>task1<span class="token punctuation">,</span> task2<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>You can <a target="_blank" href="https://dotnetfiddle.net/XLYifS">run this code in Fiddle</a> and get the following result:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/lock-result.png?sfvrsn=f599ef0b_2" title="lock result" alt="Lock result" /></p><p>The idea here is to simulate a scenario where only one unit of stock is available for two customers attempting to make a purchase simultaneously. Note that we declare a static object <code>_lock</code>, which acts as a guardian for the critical section of the code, so only one thread at a time can execute the logic block that checks and decrements the stock.</p><p>When the request is triggered, two distinct tasks are initiated in parallel for customers A and B. If we didn&rsquo;t use the locking structure, both could read the stock value as available at the same time, resulting in a duplicate sale of an item that only exists once, which constitutes a race condition.</p><p>However, the use of the <code>lock</code> instruction forces a waiting queue. While the first customer processes their purchase and the system waits (<code>Thread.Sleep</code>), the second customer remains held at the entrance of the critical section.</p><p>Only after the first customer&rsquo;s transaction is completed and the stock is updated to zero is the lock released for the next customer. Upon entering the critical section, the second client performs a logical security check, realizes that the stock has been depleted by the previous processing and ends the purchase attempt without causing data inconsistencies.</p><p>The end result is a predictable execution flow, where we guarantee data integrity in a critical scenario under concurrent demand.</p><h3 id="using-semaphoreslim">2. Using SemaphoreSlim</h3><p>As we saw above, a lock blocks the current thread until the critical region is released. This may not always be a good strategy, as in some cases it can reduce the scalability of the application. In these cases, SemaphoreSlim may be a more suitable alternative.</p><p>In ASP.NET Core, SemaphoreSlim is a built-in class used to limit the number of threads that can access a resource or a section of code simultaneously. When set to 1, it works similarly to a lock, allowing only one execution at a time. Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<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">public</span> <span class="token keyword">class</span> <span class="token class-name">Program</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 function">Main</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">ProductService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> task1 <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 punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token string">"Customer A"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> task2 <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 punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token string">"Customer B"</span><span class="token punctuation">)</span><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">WhenAll</span><span class="token punctuation">(</span>task1<span class="token punctuation">,</span> task2<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">"Finished."</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">ProductService</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> SemaphoreSlim _semaphore <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">1</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 keyword">private</span> <span class="token keyword">int</span> _stock <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> customer<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">"{customer} is waiting to enter the critical section..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _semaphore<span class="token punctuation">.</span><span class="token function">WaitAsync</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>
            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"{customer} entered the critical section."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span>_stock <span class="token operator">&lt;=</span> <span class="token number">0</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">"{customer} could not complete the purchase. Product out of stock."</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 punctuation">}</span>

            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"{customer} is processing the purchase..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Simulates an asynchronous operation</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">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _stock<span class="token operator">--</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">"{customer} completed the purchase."</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">"Remaining stock: {_stock}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">finally</span>
        <span class="token punctuation">{</span>
            _semaphore<span class="token punctuation">.</span><span class="token function">Release</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">"{customer} left the critical section."</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>You can <a target="_blank" href="https://dotnetfiddle.net/R6CNsN">run this code in Fiddle</a> and get the following result:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/semaphore-result.png?sfvrsn=c64f1819_2" title="semaphore result" alt="Semaphore result" /></p><p>In this code, we create an instance of SemaphoreSlim, passing an initial and maximum value of 1 as a parameter. Thus, when two threads attempt to execute <code>PurchaseAsync</code> simultaneously, the first thread enters SemaphoreSlim while the second waits.<br />After the first operation is completed, the semaphore is released and the second thread can finally continue.</p><p>As in the example with lock, this prevents two operations from altering the stock at the same time. Tthe difference here is that the threads are asynchronous, and we also have the possibility of configuring an initial and maximum value for the number of simultaneous threads.</p><h3 id="using-thread-safe-collections">3. Using Thread-Safe Collections</h3><p>Another scenario prone to race condition problems is using collections shared between multiple threads. Structures such as <code>List&lt;T&gt;</code>, <code>Dictionary&lt;TKey, TValue&gt;</code> and <code>HashSet&lt;T&gt;</code> were not designed for concurrent access. This means that collections of these types allow multiple threads to read and modify their data, generating inconsistent data and unpredictable behavior.</p><p>Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">private</span> <span class="token keyword">readonly</span> Dictionary<span class="token operator">&lt;</span>Guid<span class="token punctuation">,</span> Product<span class="token operator">&gt;</span> _products <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">void</span> <span class="token function">AddProduct</span><span class="token punctuation">(</span>Product product<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _products<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> product<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>If multiple requests attempt to add or update items at the same time, the application may throw exceptions or even corrupt the internal state of the collection. The problem occurs because <code>Dictionary&lt;TKey, TValue&gt;</code> is not prepared for synchronization.</p><p>For concurrent scenarios, .NET provides thread-safe collections through the namespace <code>System.Collections.Concurrent</code>, one of the most commonly used being <code>ConcurrentDictionary&lt;TKey, TValue&gt;</code>. Thus, we can use <code>ConcurrentDictionary</code> with safe concurrent access between multiple threads:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">private</span> <span class="token keyword">readonly</span> ConcurrentDictionary<span class="token operator">&lt;</span>Guid<span class="token punctuation">,</span> Product<span class="token operator">&gt;</span> _concurrentProducts <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">void</span> <span class="token function">AddConcurrentProduct</span><span class="token punctuation">(</span>Product product<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _concurrentProducts<span class="token punctuation">.</span><span class="token function">TryAdd</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>Id<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">public</span> Product<span class="token operator">?</span> <span class="token function">GetProduct</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _concurrentProducts<span class="token punctuation">.</span><span class="token function">TryGetValue</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> <span class="token keyword">out</span> <span class="token keyword">var</span> product<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> product<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>Now multiple threads can read <code>_concurrentProducts</code> at the same time, and concurrent operations are handled internally.</p><h3 id="using-optimistic-concurrency">4. Using Optimistic Concurrency</h3><p>Optimistic concurrency is another option for avoiding race conditions, especially in modern applications.</p><p>Unlike previously seen approaches such as locking or SemaphoreSlim, it does not attempt to prevent simultaneous accesses. Instead, it assumes that conflicts are rare and only detected when they occur.</p><p>Imagine that multiple operations can read the same data simultaneously. The first update happens normally, but subsequent updates fail if the data has been changed in the middle of the process. This prevents silent overwrites and provides consistency.</p><p>Consider the image below:<br /><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/optimistic-concurrency-example.png?sfvrsn=24e00cb9_2" title="optimistic concurrency example" alt="Optimistic Concurrency example" /></p><p>Note that both threads read the value at the same time (stock = 10, version 1), but Thread A was faster and updated the value first (stock = 9, version 2). When Thread B tries to update the value, an exception is generated because version 2 already exists. This verifies the consistency of the object&rsquo;s initial state; in this case, the stock quantity does not receive an invalid state.</p><h4 id="implementing-optimistic-concurrency-with-ef-core">Implementing Optimistic Concurrency with EF Core</h4><p>Entity Framework Core has a mechanism for using Optimistic Concurrency. To implement it, in an entity class we define a Version column as follows:</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">namespace</span> PracticingRaceConditions<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">Product</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">internal</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">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> Stock <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>Timestamp<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Version <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>This Version column informs EF Core that it will be used by the Optimistic Concurrency mechanism, and when two threads attempt to update the same record with the same version, an exception will be thrown.</p><p>To simulate the error, we can do the following:</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 function">SimulatingPurchaseAsync</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>ProductDbContext<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><span class="token string">"Data Source=productsDb"</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span>Options<span class="token punctuation">;</span>

    <span class="token comment">// Request A</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> contextA <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductDbContext</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Request B</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> contextB <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductDbContext</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> productA <span class="token operator">=</span> <span class="token keyword">await</span> contextB<span class="token punctuation">.</span>Products<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> productB <span class="token operator">=</span> <span class="token keyword">await</span> contextB<span class="token punctuation">.</span>Products<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    productA<span class="token punctuation">.</span>Stock<span class="token operator">--</span><span class="token punctuation">;</span>
    productB<span class="token punctuation">.</span>Stock<span class="token operator">--</span><span class="token punctuation">;</span>

    <span class="token comment">// Request A saves first</span>
    <span class="token keyword">await</span> contextA<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">try</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// Request B attempts to save using an outdated version</span>
        <span class="token keyword">await</span> contextB<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">catch</span> <span class="token punctuation">(</span><span class="token class-name">DbUpdateConcurrencyException</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">"Concurrency conflict detected!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>If we execute the <code>SimulatingPurchaseAsync()</code> method, we will get the following output in the console:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/concurrency-conflict-error.png?sfvrsn=bc8f8963_2" title="concurrency conflict error" alt="Concurrency conflict error" /></p><p>Note that when executing the <code>SimulatingPurchaseAsync()</code> method, we simulate two stock changes at the same time. When saving the result of Thread A, execution occurs normally because a version 2 of the product did not yet exist. But when trying to save the result of Thread B, a <code>DbUpdateConcurrencyException</code> exception is thrown, because EF Core detected that a version 2 was again trying to update the record.</p><p>In this way, we can use the EF Core&rsquo;s Optimistic Concurrency mechanism to prevent the race condition problem.</p><h2 id="conclusion">Conclusion</h2><p>The race condition problem occurs when two threads access and modify the same resource at the same time, which can result in an invalid state depending on the order in which the operations are performed.</p><p>In this post, we&rsquo;ve seen common examples where race conditions can occur, and learned how protect our code from these problems through approaches like Locks, SemaphoreSlim, Thread-Safe Collections and Optimistic Concurrency with EF Core. I hope this post has helped you understand what race conditions are and how to protect your applications from errors resulting from this type of problem.</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">Best Practices for Exceptions in ASP.NET Core</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let&rsquo;s see some <a target="_blank" href="https://www.telerik.com/blogs/best-practices-exceptions-aspnet-core"> best practices for using exceptions in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17414975.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:320185f1-cb3f-4b3a-86b3-d6b5a64a70f7</id>
    <title type="text">Creating a Production-Ready CRUD Application in ASP.NET Core</title>
    <summary type="text">Are you a beginner looking to create a portfolio to impress recruiters? Or perhaps an experienced developer looking to build better basic apps ready to evolve? Learn how to create a CRUD application in ASP.NET Core that goes beyond the basics and has everything you need to stand out in the real world.</summary>
    <published>2026-08-05T13:24:07Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17403835/creating-production-ready-crud-application-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Are you a beginner looking to create a portfolio to impress recruiters? Or perhaps an experienced developer looking to build better basic apps ready to evolve? Learn how to create a CRUD application in ASP.NET Core that goes beyond the basics and has everything you need to stand out in the real world.</span></p><p>In this post, you&rsquo;ll learn how to structure an ASP.NET Core project following CRUD best practices, even when the premise is simple. The idea here is to build something you can actually use as a portfolio.</p><p>In addition, we&rsquo;ll focus on decisions that experienced developers make daily: how to avoid unnecessary coupling, how to better model the domain, and how to prepare your application to grow without becoming chaotic.</p><p>By the end of the post, you&rsquo;ll have a solid foundation on how to create a CRUD application ready to evolve into something bigger, maybe with authentication, messaging, caching, or even an event-driven architecture.</p><h2 id="-the-problem-with-generic-cruds"> The Problem with Generic CRUDs</h2><p>You&rsquo;ve probably faced a requirement like this at some point: Create a simple, straightforward CRUD that only performs the basic operations of Create, Read, Update and Delete, with entities representing tables, and everything working in a few minutes. And that&rsquo;s not wrong. The problem starts when this same model, designed for prototyping, is taken to production without being prepared for future evolution.</p><p>One of the clearest signs of a basic CRUD is the use of anemic entities, classes that only have properties, without any behavior:</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">User</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</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 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>
</code></pre><p>Here, the entity does not protect its own state. Therefore, it can be created with an empty name, it can have an invalid email, it can be changed from anywhere. In other words, there is no business rule, just a simple data structure.</p><p>Another classic symptom of a prematurely scaled CRUD is having rules and validations scattered throughout the code. In basic CRUDs, when rules appear, they are scattered a bit in the Controller class, a bit in the Service class, and in the worst cases they don&rsquo;t even exist.</p><p>Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <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>user<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 punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This solves the immediate problem, but creates a bigger one: the rule doesn&rsquo;t belong to the domain, it&rsquo;s loose in the system. Therefore, as the system grows, validations can be duplicated, lose consistency and become harder to find everywhere because they are scattered.</p><p>The biggest problem here is that you&rsquo;ve only modeled the data, not the system. Basic CRUDs focus on basic operations: creating, reading, updating and deleting data. But real systems are about behavior: A user can register, they can deactivate their account, they can change their email based on internal validations, they can have a preferred name, they can change their address &hellip;</p><p>When you only model data, the system loses meaning and becomes just a data handling tool, which can easily be replaced by anything else, like a simple Excel spreadsheet for example.</p><p>The opposite of this are applications that, despite using only basic operations, are prepared for new features that will likely be needed. The image below illustrates the main points of both approaches:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/generic-crud-vs-complete-crud.png?sfvrsn=e82eb177_2" title="generic crud vs complete crud" alt="Generic CRUD VS Complete CRUD" /></p><h2 id="-creating-a-crud-ready-for-production"> Creating a CRUD Ready for Production</h2><p>Now that we&rsquo;ve reviewed some examples to avoid, let&rsquo;s see how to create a complete CRUD application, organizing each part of the code according to best practices and preparing the project for evolution. You can access the complete project code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/campus-hub-crud-base">Campus Hub source code</a>.</p><h2 id="architecture-and-basic-structure">Architecture and Basic Structure</h2><p>The application will be a CRUD to manage university courses, which can be registered, updated and deactivated. We will use the principles of Clean Architecture combined with tactical Domain-Driven Design (DDD) to create the basic structure of the project. Thus, the application will have the following project organization:</p><p><strong>src/</strong></p><ul><li><strong>Presentation</strong> -&gt; <code>CampusHub.Api</code></li><li><strong>Application</strong> -&gt; <code>CampusHub.Application</code></li><li><strong>Domain</strong> -&gt; <code>CampusHub.Domain</code></li><li><strong>Infrastructure</strong> -&gt; <code>CampusHub.Infrastructure</code></li></ul><p>Let&rsquo;s run the .NET commands to create the projects. In a terminal, execute the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new sln -n CampusHub
</code></pre><p>This will create a new solution (CampusHub.sln). Then, to create the projects within the src directory, run the following command:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new webapi -n CampusHub.Api -o src/CampusHub.Api
dotnet new classlib -n CampusHub.Application -o src/CampusHub.Application
dotnet new classlib -n CampusHub.Domain -o src/CampusHub.Domain
dotnet new classlib -n CampusHub.Infrastructure -o src/CampusHub.Infrastructure
</code></pre><p>Now, to add the projects to the solution, run the following command:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet sln add src/CampusHub.Api/CampusHub.Api.csproj
dotnet sln add src/CampusHub.Application/CampusHub.Application.csproj
dotnet sln add src/CampusHub.Domain/CampusHub.Domain.csproj
dotnet sln add src/CampusHub.Infrastructure/CampusHub.Infrastructure.csproj
</code></pre><p>Finally, run the following commands to add the dependencies between the projects:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add src/CampusHub.Application reference src/CampusHub.Domain
dotnet add src/CampusHub.Infrastructure reference src/CampusHub.Domain
dotnet add src/CampusHub.Infrastructure reference src/CampusHub.Application
dotnet add src/CampusHub.Api reference src/CampusHub.Application
dotnet add src/CampusHub.Api reference src/CampusHub.Infrastructure
</code></pre><p>Then, in the CampusHub.Infrastructure.cs add the following NuGet packages:</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">"8.0.0"</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.Relational"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</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.Tools"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span><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>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><span class="token operator">/</span>PackageReference<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Pomelo.EntityFrameworkCore.MySql"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</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">"8.0.0"</span> <span class="token operator">/</span><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><h3 id="domain-layer">Domain Layer</h3><p>In the domain layer, we will place the project&rsquo;s entities and enums. Therefore, we will have a class to represent the Course entity and an enum to represent the course statuses. So, within the <code>CampusHub.Domain</code> project, create a folder called <code>Entities</code> and add the following class inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Enums<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Domain<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">Course</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MinNameLength <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MaxNameLength <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MinWorkload <span class="token operator">=</span> <span class="token number">1</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> <span class="token keyword">string</span> Code <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">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> Name <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">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> WorkloadHours <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> MaxStudents <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> CourseStatus Status <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 comment">// EF Core requirement</span>
    <span class="token keyword">private</span> <span class="token function">Course</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 function">Course</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">,</span> <span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<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>
        <span class="token function">SetCode</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetName</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetWorkload</span><span class="token punctuation">(</span>workloadHours<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Draft<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> Course <span class="token function">Create</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">,</span> <span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<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">Course</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> name<span class="token punctuation">,</span> workloadHours<span class="token punctuation">,</span> maxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// Behavior methods</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">UpdateDetails</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">EnsureNotArchived</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">SetName</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetWorkload</span><span class="token punctuation">(</span>workloadHours<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<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">ChangeCapacity</span><span class="token punctuation">(</span><span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">EnsureNotArchived</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<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">Activate</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Active<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">"Course is already active."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Active<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">Archive</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Archived<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">"Course is already archived."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Archived<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>


    <span class="token comment">// Private validation logic</span>
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">SetCode</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<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>code<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">"Course code cannot be empty."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Code <span class="token operator">=</span> code<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">ToUpper</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">SetName</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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>name<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">"Course name cannot be empty."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>name<span class="token punctuation">.</span>Length <span class="token operator">&lt;</span> MinNameLength <span class="token operator">||</span> name<span class="token punctuation">.</span>Length <span class="token operator">&gt;</span> MaxNameLength<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">"Course name must be between {MinNameLength} and {MaxNameLength} characters."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Name <span class="token operator">=</span> name<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> <span class="token keyword">void</span> <span class="token function">SetWorkload</span><span class="token punctuation">(</span><span class="token keyword">int</span> workloadHours<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>workloadHours <span class="token operator">&lt;</span> MinWorkload<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">"Workload must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        WorkloadHours <span class="token operator">=</span> workloadHours<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">SetMaxStudents</span><span class="token punctuation">(</span><span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>maxStudents <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">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Max students must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        MaxStudents <span class="token operator">=</span> maxStudents<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">EnsureNotArchived</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Archived<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">"Archived courses cannot be modified."</span><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 this entity has an excellent structure. It is not anemic, meaning it has behaviors such as the <code>Activate()</code>, <code>Archive()</code> and <code>UpdateDetails()</code> methods.</p><p>Furthermore, you can never create an invalid Course because the verification methods protect against creating an invalid state. For example, an archived course cannot be modified, and an active course cannot be reactivated.</p><p>Now, create a new folder called <code>Enums</code> and add the following enum to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Enums<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">enum</span> CourseStatus
<span class="token punctuation">{</span>
    Draft <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span>
    Active <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
    Archived <span class="token operator">=</span> <span class="token number">2</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="application-layer">Application Layer</h3><p>The application layer is where we will configure the service classes with CRUD methods and Data Transfer Objects (DTOs). So, within the CampusHub.Application project, create a new folder called <code>DTOs</code> and create the following records inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CourseResponseDto</span><span class="token punctuation">(</span>
    Guid Id<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Code<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Status
<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> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CreateCourseDto</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> Code<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents
<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> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">UpdateCourseDto</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Then, create a new folder called <code>Interfaces</code> and add the following interfaces to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ICourseRepository</span>
<span class="token punctuation">{</span>
    Task <span class="token function">AddAsync</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>Course<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">Update</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">ExistsByCodeAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<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> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ICourseService</span>
<span class="token punctuation">{</span>
    Task<span class="token operator">&lt;</span>Guid<span class="token operator">&gt;</span> <span class="token function">CreateAsync</span><span class="token punctuation">(</span>CreateCourseDto dto<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">UpdateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">ActivateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">ArchiveAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</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, create a new folder called <code>Services</code> and add the class below to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<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">CourseService</span> <span class="token punctuation">:</span> ICourseService
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ICourseRepository _repository<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CourseService</span><span class="token punctuation">(</span>ICourseRepository repository<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _repository <span class="token operator">=</span> repository<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>Guid<span class="token operator">&gt;</span> <span class="token function">CreateAsync</span><span class="token punctuation">(</span>CreateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> exists <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">ExistsByCodeAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Code<span class="token punctuation">)</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">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course code already exists."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> course <span class="token operator">=</span> Course<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span>
            dto<span class="token punctuation">.</span>Code<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>MaxStudents
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>course<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> course<span class="token punctuation">.</span>Id<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">UpdateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</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">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">UpdateDetails</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span> dto<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span> dto<span class="token punctuation">.</span>MaxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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">ActivateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</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">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">Activate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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">ArchiveAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</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">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">Archive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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>CourseResponseDto<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</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>course <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 keyword">null</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">MapToResponse</span><span class="token punctuation">(</span>course<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>CourseResponseDto<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> courses <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> courses<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>MapToResponse<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> CourseResponseDto <span class="token function">MapToResponse</span><span class="token punctuation">(</span>Course course<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">CourseResponseDto</span><span class="token punctuation">(</span>
            course<span class="token punctuation">.</span>Id<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Code<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>MaxStudents<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Status<span class="token punctuation">.</span><span class="token function">ToString</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 have all the methods we need to execute the CRUD functions. Note that to modify the state of the objects we use the methods created in the domain class.</p><h3 id="infrastructure-layer">Infrastructure Layer</h3><p>Now let&rsquo;s create the infrastructure layer, used for communication with external parts of the application such as external APIs and databases. Within the CampusHub.Infrastructure project, create a new folder called <code>Data</code> and add the following classes to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<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> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<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> DbSet<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span> Courses <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Course<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 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>
        modelBuilder<span class="token punctuation">.</span><span class="token function">ApplyConfigurationsFromAssembly</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>AppDbContext<span class="token punctuation">)</span><span class="token punctuation">.</span>Assembly<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>
    <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> CampusHub<span class="token punctuation">.</span>Domain<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">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">.</span>Metadata<span class="token punctuation">.</span>Builders<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<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">CourseConfiguration</span> <span class="token punctuation">:</span> IEntityTypeConfiguration<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Configure</span><span class="token punctuation">(</span>EntityTypeBuilder<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span> builder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        builder<span class="token punctuation">.</span><span class="token function">ToTable</span><span class="token punctuation">(</span><span class="token string">"Courses"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

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

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code<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">20</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">HasIndex</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code<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>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Name<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">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>WorkloadHours<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>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>MaxStudents<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>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Status<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 generic-method function">HasConversion<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 punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Enum -&gt; int</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ValueGeneratedNever</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 made the necessary configurations for the EF Core implementation, defining database details such as the table name, maximum number of characters for the Name property and others.</p><p>Next, create a new folder called <code>Repositories</code> and add the following class to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<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">namespace</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Repositories<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CourseRepository</span> <span class="token punctuation">:</span> ICourseRepository
<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">CourseRepository</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>Course course<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>course<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>Course<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<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>Courses<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<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 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>Course<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</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">await</span> _context<span class="token punctuation">.</span>Courses<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 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">ExistsByCodeAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> normalizedCode <span class="token operator">=</span> code<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">ToUpper</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">await</span> _context<span class="token punctuation">.</span>Courses
            <span class="token punctuation">.</span><span class="token function">AnyAsync</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code <span class="token operator">==</span> normalizedCode<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">Update</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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 punctuation">}</span>
</code></pre><p>In the repository class, we create the implementation of the CRUD methods that execute the operations on the database.</p><p>The last class in the infrastructure layer will be used to configure the dependency injection and define the connection string. So, inside the project add the class below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Repositories<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<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">using</span> Microsoft<span class="token punctuation">.</span>Extensions<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>Extensions<span class="token punctuation">.</span>DependencyInjection<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Infrastructure<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">DependencyInjection</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 configuration<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">UseMySql</span><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>
            ServerVersion<span class="token punctuation">.</span><span class="token function">AutoDetect</span><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 punctuation">;</span>

        services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>ICourseRepository<span class="token punctuation">,</span> CourseRepository<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 punctuation">}</span>
</code></pre><h3 id="api-layer">API Layer</h3><p>The API is the final layer of the application, here we will define the controller classes and other settings such as the database connection string. Inside the Controllers folder, add the following controller:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<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">namespace</span> CampusHub<span class="token punctuation">.</span>Api<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/[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">CoursesController</span> <span class="token punctuation">:</span> ControllerBase
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ICourseService _service<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CoursesController</span><span class="token punctuation">(</span>ICourseService service<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _service <span class="token operator">=</span> service<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> <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>CreateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> id <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">CreateAsync</span><span class="token punctuation">(</span>dto<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 punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">null</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">HttpGet</span><span class="token punctuation">(</span><span class="token string">"{id:guid}"</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>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">GetByIdAsync</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>course <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>course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</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>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> courses <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">GetAllAsync</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>courses<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">HttpPut</span><span class="token punctuation">(</span><span class="token string">"{id:guid}"</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">Update</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">UpdateAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> dto<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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">HttpPost</span><span class="token punctuation">(</span><span class="token string">"{id:guid}/activate"</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">Activate</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">ActivateAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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">HttpPost</span><span class="token punctuation">(</span><span class="token string">"{id:guid}/archive"</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">Archive</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">ArchiveAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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>Then, in the Program class, replace the existing code with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Services<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Infrastructure<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">// Services</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>

<span class="token comment">// Application</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>ICourseService<span class="token punctuation">,</span> CourseService<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">// Infrastructure</span>

builder<span class="token punctuation">.</span>Services<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 comment">// Build</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">// Middleware</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>

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>Finally, in the file appsettings.json, add the following configuration. (Don&rsquo;t forget to add your credentials!):</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=localhost;port=3306;database=campushub;user=YOUR_USER;password=YOUR_PASSWORD;"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">,</span>
</code></pre><h2 id="running-ef-core-commands">Running EF Core Commands</h2><p>To apply the migration commands, in the project root, open a new terminal and run the commands below.</p><p><strong>Creating the Migration Files</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet ef migrations add InitialCreate --project src/CampusHub.Infrastructure --startup-project src/CampusHub.Api --output-dir Data/Migrations
</code></pre><p><strong>Applying the Migration Commands</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet ef database update --project src/CampusHub.Infrastructure --startup-project src/CampusHub.Api
</code></pre><h2 id="what-does-the-final-structure-look-like">What Does the Final Structure Look Like?</h2><p>After implementing all the steps above, the project will have the following structure:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/final-project-structure.png?sfvrsn=5ab25a90_2" title="final project structure" alt="Final project structure" /></p><h2 id="-when-does-a-complete-crud-not-pay-off"> When Does a Complete CRUD Not Pay Off?</h2><p>In this post, we created a complete CRUD, with all the necessary elements for its evolution. However, this complexity can exponentially increase development time and the level of knowledge required to maintain the project.</p><p>With this in mind, in some scenarios, simplicity is preferable. For example, when the system basically stores and retrieves data without relevant business rules. In scenarios such as basic registrations, auxiliary tables or internal administrative panels, adding layers such as services, rich entities and DDD patterns tends to generate more complexity than value, making the code more difficult to maintain without a justified need.</p><p>It is also not worthwhile to invest in a sophisticated architecture in the initial phases of a product, such as MVPs or systems still in validation. In these cases, the priority is speed and adaptation to frequent changes, and a simple structure allows for faster evolution without the burden of unnecessary abstractions.</p><p>On the other hand, a more elaborate CRUD makes sense when the domain has important rules, states, complex validations or when errors can have a significant impact. In these scenarios, a more structured architecture, as demonstrated in the post, helps protect the domain, maintain consistency and facilitate the system&rsquo;s evolution over time (all parts are in their proper places).</p><p>The final decision of whether or not to use a complete and evolve-ready CRUD should therefore always take into account the level of complexity of the problem.</p><h2 id="conclusion-and-next-steps">Conclusion and Next Steps</h2><p>Knowing the best approach when creating new web applications is always a challenge, but we must keep in mind that if the project requires complex business rules, validations and errors that can have a significant impact, this is a strong indication that the application should not be a simple and generic CRUD.</p><p>In this post, we learned how to create a complete CRUD, with each layer representing a part of Clean Architecture, and using DDD development principles to implement a well-structured domain with private properties and behaviors.</p><p>But far beyond the basic structure presented in this post, a complete CRUD also involves unit tests, complex validations (FluentValidation), authentication and authorization with modern methods (JW Tokens for example), and many other elements. I hope this post serves as a starting point and helps you create CRUD applications that are not only ready to function, but also to evolve with quality over time.</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">Best Practices for Exceptions in ASP.NET Core</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let&rsquo;s see some <a target="_blank" href="https://www.telerik.com/blogs/best-practices-exceptions-aspnet-core"> best practices for using exceptions in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17403835.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:9fd87dc0-365e-47d0-8606-b091b8bd9ccb</id>
    <title type="text">Best Practices for Exceptions in ASP.NET Core</title>
    <summary type="text">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let’s see some best practices for using them in ASP.NET Core.</summary>
    <published>2026-06-29T13:01:51Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17369707/best-practices-exceptions-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let&rsquo;s see some best practices for using them in ASP.NET Core.</span></p><p>Exceptions have been part of the .NET ecosystem since the earliest versions of the .NET Framework. Today, with the platform&rsquo;s continuous evolution, their use is simpler and more integrated than ever. The problem, however, arises when they are used inappropriately or imprecisely.</p><p>In this post, we will explore in which situations the use of exceptions is truly recommended and when more appropriate alternatives should be considered. Furthermore, we will see in practice some of the most modern features available in ASP.NET Core for implementing exception handling aligned with best practices.</p><h2 id="-what-are-exceptions-in-the-context-of-asp.net-core"> What Are Exceptions in the Context of ASP.NET Core?</h2><p>In ASP.NET Core, exceptions are objects that represent unexpected events that occur during program execution and interrupt its normal flow. Common examples of flow interruption include null reference errors, invalid user input, and database connection problems.</p><p>In practice, when an exception is thrown, the .NET runtime starts looking for somewhere that knows how to handle it, typically a <code>try/catch</code> statement, as shown in the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">try</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token number">10</span> <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">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>
    <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">"Error: "</span> <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>
</code></pre><p>If this exception is not handled, it returns to the beginning of the execution and eventually reaches the ASP.NET Core pipeline, resulting in an HTTP error, usually a <code>500 - Internal Server Error</code>.</p><h2 id="-when-should-exceptions-be-avoided"> When Should Exceptions Be Avoided?</h2><p>One of the most common mistakes in backend applications is using exceptions for expected situations. If code already expects something to happen, then it&rsquo;s not exceptional, it&rsquo;s predictable.</p><p>The principle of exceptions is to <strong>reserve exceptions for unpredictable situations</strong>.</p><p>A common example of a situation where exceptions should be avoided is for input data validation. Data validation should never use exceptions as a rule. Note the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><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">IsNullOrEmpty</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>BankAccountNumber<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">"BankAccountNumber is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The problem here is that the system expects the client to send a user with invalid data. In other words, this is part of the normal flow, not an exception. In this case, the correct approach would be to treat this as data validation and simply return a <code>400 Bad Request</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><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">IsNullOrEmpty</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>BankAccountNumber<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">BadRequest</span><span class="token punctuation">(</span><span class="token string">"BankAccountNumber is required"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="-when-does-using-exceptions-make-sense"> When Does Using Exceptions Make Sense?</h2><p>If exceptions shouldn&rsquo;t be used in the normal flow, then when do they make sense? In backend applications like ASP.NET Core, <strong>exceptions should be used when something truly unexpected happens and the application cannot continue safely or should not continue at all</strong>.</p><p>Below, we&rsquo;ll look at practical scenarios where exceptions actually make sense.</p><h3 id="-infrastructure-failures"> Infrastructure Failures</h3><p>This is a very common scenario. If the database fails here, it&rsquo;s not possible to proceed normally, meaning it&rsquo;s an exception:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">try</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> user <span class="token operator">=</span> <span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>Users<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> Results<span class="token punctuation">.</span><span class="token function">Ok</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 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>
    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">InternalServerError</span><span class="token punctuation">(</span>$<span class="token string">"Error retrieving user. Error details: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="⚠️-impossible-or-inconsistent-states">⚠️ Impossible or Inconsistent States</h3><p>Despite frontend and backend validations, inconsistent states can reach the application core. If this happens, something is certainly wrong, and this corrupted state cannot be saved to the database or continue its path. In this case, an exception must be generated:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">if</span> <span class="token punctuation">(</span>BankAccountNumber<span class="token punctuation">.</span>Length <span class="token operator">&lt;</span> <span class="token number">8</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">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"The bank account number must have at least 8 digits at this point"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>If the system logic guarantees that <code>BankAccountNumber</code> will always have at least 8 characters at this point, then this indicates a bug, data inconsistency or even a flow error, and the exception is the last resort to prevent an inconsistent state at this stage of the process.</p><h3 id="-dependency-on-external-services"> Dependency on External Services</h3><p>If the external service is mandatory for the flow and the absence of data completely prevents correct processing, then you are dealing with a real error and the exception represents a technical problem.</p><p>In this case, it is correct to throw an exception because the process cannot continue with inconsistent data. Furthermore, it is important to clarify that the error occurred in the integration between the services, as demonstrated in the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">var</span> customer <span class="token operator">=</span> <span class="token keyword">await</span> externalService<span class="token punctuation">.</span><span class="token function">GetCustomerAsync</span><span class="token punctuation">(</span>customerId<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 operator">==</span> <span class="token keyword">null</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">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Unable to retrieve customer data from the external service: CustomerService"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><h2 id="result-pattern-as-an-alternative">Result Pattern as an Alternative</h2><p>If exceptions shouldn&rsquo;t be used for normal flow, the question arises: what would be a good alternative to represent expected errors?</p><p>One of the most widely used alternatives today is the Result Pattern. The Result Pattern is a design pattern used to manage execution flow and errors, returning a structured object instead of throwing exceptions for expected failures.</p><p>Instead of throwing exceptions, the method returns an object indicating whether it succeeded or failed and why.</p><h3 id="implementing-the-result-pattern">Implementing the Result Pattern</h3><p>To use the Result Pattern, we will create a simple application and then create the classes and methods. The complete source code with examples is available in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/practicing-exception-handling">PracticingExceptionHandling source code</a>.</p><p>To create the base, you can use the command below in your terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o PracticingExceptionHandling
</code></pre><p>Open the application and create the following class within it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> PracticingExceptionHandling<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Result</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> IsSuccess <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Error <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">protected</span> <span class="token function">Result</span><span class="token punctuation">(</span><span class="token keyword">bool</span> isSuccess<span class="token punctuation">,</span> <span class="token keyword">string</span> error<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        IsSuccess <span class="token operator">=</span> isSuccess<span class="token punctuation">;</span>
        Error <span class="token operator">=</span> error<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> Result <span class="token function">Ok</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">new</span> <span class="token class-name">Result</span><span class="token punctuation">(</span><span class="token keyword">true</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> Result <span class="token function">Fail</span><span class="token punctuation">(</span><span class="token keyword">string</span> error<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">Result</span><span class="token punctuation">(</span><span class="token keyword">false</span><span class="token punctuation">,</span> error<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">Result</span><span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token punctuation">:</span> Result
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> T Value <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token function">Result</span><span class="token punctuation">(</span>T <span class="token keyword">value</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 keyword">true</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Value <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token function">Result</span><span class="token punctuation">(</span><span class="token keyword">string</span> error<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span><span class="token keyword">false</span><span class="token punctuation">,</span> error<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> Result<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token function">Ok</span><span class="token punctuation">(</span>T <span class="token keyword">value</span><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">Result</span><span class="token operator">&lt;</span>T<span class="token operator">&gt;</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">public</span> <span class="token keyword">static</span> <span class="token keyword">new</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token function">Fail</span><span class="token punctuation">(</span><span class="token keyword">string</span> error<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">Result</span><span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that two <code>Result</code> classes are implemented, both aiming to explicitly represent the success or failure of an operation as well as the error details. The base class defines the common structure for any result. It has the <code>IsSuccess</code> property, which indicates whether the operation was successful, and the <code>Error</code> property, which contains the error message in case of failure.</p><p>The generic <code>Result&lt;T&gt;</code> class extends this idea to operations that return a value. When the operation is successful, in addition to <code>IsSuccess</code> being true, the result also carries a <code>Value</code> of type <code>T</code>. For this, there are two distinct private constructors: one for success, which receives the value, and another for failure, which receives only the error message. This avoids the creation of invalid states within the class itself.</p><p>The static methods <code>Ok</code> and <code>Fail</code> in the generic version follow the same pattern as the base class, but now allow data to be returned along with the result. This is useful in scenarios such as searches, validations or external integrations, where you want to return a value on success or a clear explanation on error, without throwing exceptions for expected situations.</p><p>An important point is that the consumer of the result must always check <code>IsSuccess</code> before accessing <code>Value</code>. This is a desirable behavior, as it forces the API user to explicitly handle failures, preventing silent errors or unexpected flows.</p><p>Now create a new class called <code>UserService</code> and add the following code to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> PracticingExceptionHandling<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserService</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Result<span class="token operator">&lt;</span>User<span class="token operator">&gt;</span> <span class="token function">CreateUser</span><span class="token punctuation">(</span>User newUser<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>newUser<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Result<span class="token operator">&lt;</span>User<span class="token operator">&gt;</span><span class="token punctuation">.</span><span class="token function">Fail</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">var</span> user <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            Id <span class="token operator">=</span> newUser<span class="token punctuation">.</span>Id<span class="token punctuation">,</span>
            Name <span class="token operator">=</span> newUser<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            BankAccountNumber <span class="token operator">=</span> newUser<span class="token punctuation">.</span>BankAccountNumber<span class="token punctuation">,</span>
            Status <span class="token operator">=</span> <span class="token number">1</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> Result<span class="token operator">&lt;</span>User<span class="token operator">&gt;</span><span class="token punctuation">.</span><span class="token function">Ok</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 punctuation">}</span>
</code></pre><p>Here we have the <code>CreateUser</code> method which validates whether the <code>Name</code> property is null or contains only spaces. If either of these conditions is true, the method immediately returns a failure result <code>Result&lt;User&gt;.Fail</code>, preventing the creation of an invalid user and explicitly stating the reason for the error.</p><p>If the validation passes, a new <code>User</code> object is instantiated with the data provided in the request, also applying a default value for <code>Status</code>. Finally, the method returns a success result <code>Result&lt;User&gt;.Ok</code>, encapsulating the created object.</p><p>The next step is to call the <code>CreateUser</code> method, so in the <code>Program</code> class, add the following endpoint:</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/create"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>User newUser<span class="token punctuation">,</span> UserService userService<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> result <span class="token operator">=</span> userService<span class="token punctuation">.</span><span class="token function">CreateUser</span><span class="token punctuation">(</span>newUser<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>result<span class="token punctuation">.</span>IsSuccess<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 keyword">new</span> <span class="token punctuation">{</span> error <span class="token operator">=</span> result<span class="token punctuation">.</span>Error <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>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>In this endpoint, we define a POST route <code>/users/create</code> that receives a User object and a <code>UserService</code> instance. Upon receiving the request, the endpoint calls the <code>CreateUser</code> method, which returns a <code>Result&lt;User&gt;</code>. This return encapsulates both success and potential failure.</p><p>Next, the endpoint checks the <code>IsSuccess</code> property. If the result indicates failure, it returns a <code>400 Bad Request</code>, including the error message (<code>result.Error</code>) in the response body, clearly explaining the reason for the rejection to the client. If the operation is successful, the endpoint returns a <code>200 OK</code> with the created object (<code>result.Value</code>).</p><p>In this way, we use the Result Pattern as a clear contract between the layers, which improves the readability and predictability of the API&rsquo;s behavior, reserving exceptions only for unpredictable scenarios.</p><h2 id="✨-expressive-exceptions">✨ Expressive Exceptions</h2><p>A common mistake when using exceptions is creating weak or generic exceptions. We&rsquo;ve already seen that exceptions should be reserved for unpredictable errors; in this case, when an error occurs, it&rsquo;s important to obtain as much information as possible about why that exception happened. Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><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">"User not found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The problem here is that the system knows something went wrong but doesn&rsquo;t know exactly what went wrong (that is, the reason for the error).</p><p>To solve this, we can create a custom exception. Custom exceptions are classes that represent domain or application specific errors. Consider the following example:</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">UserNotFoundException</span> <span class="token punctuation">:</span> Exception
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">UserNotFoundException</span><span class="token punctuation">(</span><span class="token keyword">string</span> id<span class="token punctuation">)</span>
        <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>$<span class="token string">"User with id {id} was not found"</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 the error has an identity. It provides user details and makes it explicit that this is a domain exception and is important to the application, much more explicit than a generic exception.</p><p>Now see how it looks in the call:</p><p><strong>❌ Generic</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><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">"User not found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><strong>✅ Specific (Custom Exception)</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">UserNotFoundException</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>With custom exceptions, we obtain better tracking (logs), the possibility of specific handling and more expressive code.</p><h2 id="-creating-exceptions-with-helper-methods"> Creating Exceptions with Helper Methods</h2><p>When using custom exceptions, it&rsquo;s common to have duplicate exceptions, for example, when the same exception is thrown in multiple parts of the class. Note the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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>name<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">InvalidUserException</span><span class="token punctuation">(</span><span class="token string">"Name cannot be empty"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Name <span class="token operator">=</span> name<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">UpdateName</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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>name<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">InvalidUserException</span><span class="token punctuation">(</span><span class="token string">"Name cannot be empty"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    Name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In both cases, the same exception is being thrown, using the same details. To reuse exceptions, we can create helper methods and even generic methods depending on the context in which they are used. In the case above, we can create a method that expects the name of a property and returns a custom exception and use it everywhere it is useful:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">public</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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>name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token function">NewInvalidPropertyException</span><span class="token punctuation">(</span><span class="token string">"Name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Name <span class="token operator">=</span> name<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">UpdateName</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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>name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token function">NewInvalidPropertyException</span><span class="token punctuation">(</span><span class="token string">"Name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Name <span class="token operator">=</span> name<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> InvalidUserException <span class="token function">NewInvalidPropertyException</span><span class="token punctuation">(</span><span class="token keyword">string</span> property<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">InvalidUserException</span><span class="token punctuation">(</span>$<span class="token string">"{property} cannot be empty"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><h2 id="-conclusion"> Conclusion</h2><p>Exceptions are an excellent feature of ASP.NET Core and are especially useful in unpredictable scenarios or as a last line of defense to prevent invalid states from progressing through the flow or persisting in the database. The problem arises when they are used to handle expected errors or in a generic way, without providing sufficient details.</p><p>In this post, we explore best practices in the use of exceptions and examine the Result Pattern as a more suitable alternative for predictable scenarios.</p><p>I hope this post helps you make better decisions when using exceptions, when to avoid them and how to apply them effectively in your applications.</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">Exploring the SLNX Solution File Format</h4></div><div class="col-8"><p class="u-fs16 u-mb0">If you&rsquo;ve worked with .NET for any length of time, you&rsquo;ve made peace with the .sln file. But as of .NET 10, we&rsquo;ve got .slnx. <a target="_blank" href="https://www.telerik.com/blogs/exploring-slnx-solution-file-format">Let&rsquo;s talk about what .slnx is</a>, why it exists, what it gets right and where you might get tripped up.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17369707.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:fc4f18d8-9f5a-4741-bfdd-7844b4bdd7e0</id>
    <title type="text">Exploring the SLNX Solution File Format</title>
    <summary type="text">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</summary>
    <published>2026-06-11T19:09:21Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17359639/exploring-slnx-solution-file-format"/>
    <content type="text"><![CDATA[<p><span class="featured">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</span></p><p>If you&rsquo;ve worked with .NET for any length of time, you&rsquo;ve made peace with the <code>.sln</code> file. Not because it&rsquo;s good (it isn&rsquo;t) but because it&rsquo;s the format we have. It&rsquo;s verbose, GUID-laden and a reliable source of merge conflicts on Friday afternoons. It&rsquo;s the format we tolerate, not the one we&rsquo;d choose.</p><p>The good news is there&rsquo;s a new format in town. The <code>.slnx</code> format is Microsoft&rsquo;s XML-based replacement for the venerable <code>.sln</code> file, and it has been steadily gaining first-class support across Visual Studio, the .NET CLI, MSBuild and Rider over the last year. As of <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">.NET 9.0.200</a> and <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">Visual Studio 17.13+</a>, you can use it for real projects without crossing your fingers. And <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">as of .NET 10</a>, <code>dotnet new sln</code> defaults to <code>.slnx</code>.</p><p>In this post, let&rsquo;s talk about what <code>.slnx</code> is, why it exists, what it gets right and where you might get tripped up.</p><h2 id="a-quick-sln-retrospective">A Quick SLN Retrospective</h2><p>To appreciate why we&rsquo;re getting a new format, let&rsquo;s remember what we&rsquo;ve been working with. Here&rsquo;s a typical fragment of an <code>.sln</code> file.</p><pre><code>Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34804.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApi", "MyApi\MyApi.csproj", "{F95781B3-A973-4D19-9585-974DA143E6A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8FC526EA-218B-4615-8410-4E1850611F38}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
</code></pre><p>Oof. The SLN format is a custom Microsoft format that has some weird quirks. Every project gets a GUID. Solution folders are themselves projects with their own GUIDs. Configuration combinations even get cross-multiplied so a four-project solution with two configurations and two platforms produces <em>16 lines</em> of nearly identical setup.</p><p>The pain points are well known to us all. If even two developers add a project at the same time, both edit <code>GlobalSection(ProjectConfigurationPlatforms)</code>, and Git throws its hands up.</p><p>GUIDs, bless their hearts, are not for human eyes. It&rsquo;s a nightmare trying to figure out which project owns which configuration block. And outside of Visual Studio, generating or modifying <code>.sln</code> files reliably often requires <a target="_blank" href="https://microsoft.github.io/slngen/">a tool like <code>slngen</code></a> because no one wants to write the parser themselves.</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">Visual Studio 2026, 6 Months Later</h4></div><div class="col-8"><p class="u-fs16 u-mb0">See what&rsquo;s really behind the <a target="_blank" href="https://www.telerik.com/blogs/visual-studio-2026-6-months-later">Visual Studio 2026 updates</a> and improvements, such as performance, GitHub Copilot, Hot Reload and more.
.</p></div></div><hr class="u-mb3" /></aside><p>This is what the <code>.slnx</code> tries to fix.</p><h2 id="looking-inside-the-.slnx">Looking Inside the .slnx</h2><p>Here&rsquo;s the same project, but expressed as <code>.slnx</code>.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<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>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<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>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MyApi/MyApi.csproj<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>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Notice how we have no GUIDs, no cross-multiplied configuration table and no <code>EndGlobalSection</code> markers. Even a developer who is foreign to the <code>.slnx</code> format can read it and immediately understand it.</p><p>Expanding a little, here&rsquo;s an example from a layered solution.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<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>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>.editorconfig<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>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<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>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Packages.props<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>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/src/<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Application/Application.csproj<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Domain/Domain.csproj<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Infrastructure/Infrastructure.csproj<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Web.Api/Web.Api.csproj<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>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/tests/<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/UnitTests/UnitTests.csproj<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>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/IntegrationTests/IntegrationTests.csproj<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>Folder</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>If you compare this to the equivalent <code>.sln</code>, you&rsquo;re looking at maybe a third of the lines, with none of the GUID overhead. If you need to override configurations for a specific project (like, say your test project shouldn&rsquo;t build in Release mode) you can express that with a <code>Configurations</code> element on the project. In reality, most projects won&rsquo;t need it at all. If you do, you can review examples in <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a> and its <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/wiki/Samples">samples wiki</a>.</p><p>If the <code>.slnx</code> looks similar to a <code>.csproj</code>, that&rsquo;s intentional. Microsoft&rsquo;s reasoning is that XML is already the format the rest of MSBuild speaks, the team already had a parser for it, and features like comments and attributes are first-class citizens. A JSON or YAML format might have been trendier, but it would have forced the team to invent semantics for things <code>.csproj</code> already has solved problems for.</p><h2 id="migrating-existing-projects">Migrating Existing Projects</h2><p>Existing projects are a different conversation. Here&rsquo;s what you get by migrating, and how to do it cleanly.</p><ul><li><strong>Merge conflicts are more manageable.</strong> Because there are no GUIDs that change every time you touch a project, the diffs you see in PRs are meaningful. If you add a project, you see one line added. Move a project to a different folder, and you see the path change. Nobody has to play GUID detective.</li><li><strong>You can reasonably edit by hand.</strong> You&rsquo;ll never want to hand-edit a <code>.sln</code> unless it&rsquo;s an emergency. With <code>.slnx</code>, opening it in your editor of choice is perfectly reasonable. I&rsquo;ve fixed broken solution layouts in a text editor faster than Visual Studio would have let me click through the dialogs.</li><li><strong>Tooling has standardized what a solution is.</strong> Microsoft has open-sourced the parser as the <a target="_blank" href="https://www.nuget.org/packages/Microsoft.VisualStudio.SolutionPersistence/">Microsoft.VisualStudio.SolutionPersistence NuGet package</a>. This means MSBuild, the .NET CLI and third-party tools are all reading the same file the same way.</li></ul><p>Based on the official <code>dotnet sln</code> reference, the <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln">actual conversion is one command</a>.</p><pre><code>dotnet sln migrate
</code></pre><p>If you run this in a directory with an <code>.sln</code> file, you&rsquo;ll get an <code>.slnx</code> file beside it. From Visual Studio, you can also use <strong>File -&gt; Save Solution As&hellip;</strong> and pick <strong>XML Solution File (.slnx)</strong> from the dropdown. Rider supports a similar flow.</p><p>You don&rsquo;t want to keep both files in the repository. The <code>dotnet sln</code> commands don&rsquo;t reliably pick the right one when you have both, and it&rsquo;s easy for the two to get out of sync as people add or remove projects. Pick when to cut over, delete the old file and move on. While there is <a target="_blank" href="https://github.com/edvilme/dotnet-sln-sync">a community tool</a> called <code>dotnet-sln-sync</code> that helps, that should only be used temporarily and not long term.</p><p>As you convert over, here&rsquo;s a quick checklist to avoid any headaches:</p><ul><li><strong>Verify your <code>global.json</code> allows the right SDK.</strong> You need at least .NET 9.0.200 for full CLI support. If your <code>global.json</code> pins to something older, the migrate command won&rsquo;t work.</li><li><strong>Check your CI build agents.</strong> If you&rsquo;re on self-hosted Azure DevOps agents, for example, confirm they&rsquo;re running an SDK new enough to handle <code>.slnx</code>.</li><li><strong>Audit your pipelines.</strong> Anywhere you have references directly to <code>.sln</code>, update the path. Wildcard references like <code>**/*.sln</code> will miss the new file. If you are using templates where you have a mix of <code>.sln</code> and <code>.slnx</code>, a pattern like <code>**/*.sln*</code> will be useful.</li><li><strong>Look out for dependent tools.</strong> Tools like <code>slngen</code> don&rsquo;t yet support <code>.slnx</code> at the time of this post (you can <a target="_blank" href="https://github.com/microsoft/slngen/issues/643">track the issue here</a>). If you&rsquo;re reliant on a tool outside of the Microsoft umbrella, check before you migrate.</li></ul><h2 id="some-rough-edges">Some Rough Edges</h2><p>The <code>.slnx</code> format is no longer &ldquo;preview&rdquo; in any practical sense, but the ecosystem around it is still catching up.</p><ul><li><strong>Visual Studio file association.</strong> Double-clicking a <code>.slnx</code> file doesn&rsquo;t open Visual Studio by default. You can fix this with a file association or just open it from inside the IDE.</li><li><strong>C# Dev Kit in VS Code.</strong> It works, but you may need to set <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/"><code>dotnet.defaultSolution</code></a> to the path of your <code>.slnx</code> if it isn&rsquo;t auto-detected.</li><li><strong>Globbing.</strong> You can&rsquo;t write <code>&lt;Project Path="src/**/*.csproj" /&gt;</code> and have it discover projects automatically. The team&rsquo;s reasoning, captured in the <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/issues/61">globbing feature request</a>, is that globbing slows down solution loading on large repos because Visual Studio has to scan the file system before it can render anything. That makes sense, <em>but</em> I think plenty of us would happily trade a slower cold load for never having to add another project entry by hand.</li><li><strong>Third-party tools.</strong> Anything that parses <code>.sln</code> directly needs to update. Hopefully the open-source parser library accelerates that, but you&rsquo;ll find holdouts.</li></ul><p>None of these are dealbreakers for new projects. For older codebases with a lot of tooling baked in, it&rsquo;s worth doing a small pilot before flipping the whole repo.</p><h2 id="wrapping-up">Wrapping Up</h2><p>The <code>.slnx</code> format isn&rsquo;t a revolutionary feature. It doesn&rsquo;t change how you write code, it doesn&rsquo;t add new build capabilities, and it doesn&rsquo;t speed up your application. It is, however, one of those quality-of-life upgrades that quietly results in fewer merge conflicts, less time staring at GUIDs, less friction in your CI pipelines once they&rsquo;re set up.</p><p>If you&rsquo;re starting a new .NET solution, just use <code>.slnx</code> from day one. And with .NET 10, you&rsquo;ll get it without lifting a finger. If you&rsquo;re maintaining an existing <code>.sln</code> file, plan a migration when you have a slow week, audit your tooling and cut over cleanly. The format isn&rsquo;t going anywhere, and getting ahead of the transition is easier than waiting for some downstream tool to force your hand.</p><p>Thanks for reading, and happy coding!</p><h2 id="further-reading">Further Reading</h2><ul><li><a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">New, simpler solution file format</a></li><li><a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">Introducing support for SLNX in the .NET CLI</a></li><li><a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln"><code>dotnet sln</code> command reference</a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">.NET 10 breaking change: <code>dotnet new sln</code> defaults to SLNX</a></li></ul><img src="https://feeds.telerik.com/link/23052/17359639.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e793d386-96e8-4722-9af0-282b11768a2d</id>
    <title type="text">Practicing Vertical Slice Architecture in ASP.NET Core</title>
    <summary type="text">Tired of organizing code through technical layers? How about going beyond the basics and “slicing” the system? Vertical Slice Architecture structures the application not by the type of code to be written, but by the problem to be solved. Let’s learn how to implement it in ASP.NET Core.</summary>
    <published>2026-06-09T20:10:26Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17357140/practicing-vertical-slice-architecture-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Tired of organizing code through technical layers? How about going beyond the basics and &ldquo;slicing&rdquo; the system? Vertical Slice Architecture structures the application not by the type of code to be written, but by the problem to be solved. Let&rsquo;s learn how to implement it in ASP.NET Core.</span></p><p>If you&rsquo;ve ever created an API in ASP.NET Core using the classic approach of dividing it into Controllers, Services and Repositories, you&rsquo;ve probably faced some difficulties in creating the necessary structure. With each new feature, more files are scattered, more cross-dependencies appear, and it becomes more difficult to understand what truly belongs to each use case.</p><p>But what if, instead of organizing the code by technical layers, you organized it by system functionalities? That&rsquo;s exactly what Vertical Slice Architecture proposes.</p><p>Instead of a giant folder for Services and another for Repositories, each functionality becomes a vertical &ldquo;slice&rdquo; of the system, containing everything it needs: endpoint, business rules, validations, DTOs and data access. Each request then has its own isolated flow.</p><p>In this post, we&rsquo;ll explore Vertical Slice Architecture and how to apply it in a practical ASP.NET Core project.</p><h2 id="layered-architecture-and-its-trade-offs">Layered Architecture and Its Trade-offs</h2><p>Layered Architecture is one of the best-known and most widely used architectural styles in enterprise software development. It&rsquo;s commonly found in ASP.NET Core, Java/Spring and other enterprise platforms. The main point of this style is that it organizes the system into horizontal layers, each with well-defined responsibilities.</p><p>The classic architecture typically divides the system into four main layers (despite variations):</p><ol><li>Presentation (UI / API): Controllers, endpoints, user interfaces</li><li>Application / Service: Orchestrates use cases</li><li>Domain / Business: Business rules</li><li>Infrastructure / Data Access: Database, external integrations, files, etc.</li></ol><p>Each layer depends only on the layer immediately below it (or on abstractions), creating a predictable flow of dependencies.</p><p>Despite its popularity, this approach has some drawbacks, such as flow coupling. Although the layers are separate, use cases traverse all of them: Controller -&gt; Service -&gt; Domain -&gt; Repository -&gt; Database. This creates structural flow coupling.</p><p>A simple change in one use case can require changes in multiple layers. This results in scattered files and constant navigation between projects/folders to find important parts of the code.</p><p>Another negative point is that this architecture organizes code by technical type, not by functionality. For example, if you want to understand the &ldquo;Create Order&rdquo; use case, you will need to open multiple files in different folders (Controllers, Services folder, Repositories folder, etc.).</p><p>When can Layered Architecture start to become a problem? In large systems with many use cases, a high volume of changes per feature, a complex domain that requires strong modeling, and large teams working on multiple fronts. In scenarios like this, an excellent alternative is Vertical Slice Architecture.</p><h2 id="vertical-slice-architecture-as-an-alternative">Vertical Slice Architecture as an Alternative</h2><p>Vertical Slice Architecture is an architectural style where the system is designed and organized by features (functionalities), rather than by technical layers as in traditional layer-based approaches.</p><p>Vertical Slice emerged as an alternative to problems with traditional approaches, such as the difficulty in maintenance due to classes being scattered throughout the program.</p><p>The proposal of Vertical Slice is simple: organize the code by functionality (feature) and not by technical type. Imagine you need to create functionalities for creating and canceling an order.</p><p>In a layered approach, we would have the standard structure:</p><pre><code>Controller 
- OrderController 
Service 
- OrderService
Repository 
- OrderRepository
Dtos 
- OrderDto
</code></pre><p>In a Vertical Slice Architecture, you would organize things by use case:</p><pre><code>Features/
CreateOrder/
CreateOrderEndpoint.cs
CreateOrderHandler.cs
CreateOrderRequest.cs
CreateOrderValidator.cs

CancelOrder/
CancelOrderEndpoint.cs
CancelOrderHandler.cs
CancelOrderRequest.cs
CancelOrderValidator.cs
</code></pre><p>Much clearer now, isn&rsquo;t it? The image below shows a simple comparison between Vertical Slice Architecture and Layered Architecture:</p><p><img title="vertical slice vs layered" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/vertical-slice-vs-layered.png?sfvrsn=743f49ce_2" alt="Vertical Slice VS Layered" /></p><h2 id="implementing-vertical-slice-architecture">Implementing Vertical Slice Architecture</h2><p>Now we&rsquo;re going to create an API using Vertical Slice Architecture. Our API will be used to manage a subscription service. You can access the complete code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/vertical-slice-subs">Vertical Slice Subs Source Code</a>.</p><p>Our project will follow this structure:</p><p><img title="project structure" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/project-structure.png?sfvrsn=10deb58f_2" alt="Project structure" /></p><h3 id="creating-the-domain-model">Creating the Domain Model</h3><p>To create the base application, you can use the following command in the terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o SubscriptionManagement
</code></pre><p>Then, inside the project, create a new folder called &ldquo;Features,&rdquo; and inside it another folder called &ldquo;Subscriptions&rdquo;. Add the following class and enum to the folder Subscriptions:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Subscription</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 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>
    <span class="token keyword">public</span> <span class="token keyword">string</span> ServiceName <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">default</span><span class="token operator">!</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> DateOnly StartDate <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> DateOnly<span class="token operator">?</span> TrialEndsAt <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> SubscriptionStatus Status <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">private</span> <span class="token function">Subscription</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">Subscription</span><span class="token punctuation">(</span><span class="token keyword">string</span> serviceName<span class="token punctuation">,</span> <span class="token keyword">decimal</span> price<span class="token punctuation">,</span> DateOnly startDate<span class="token punctuation">,</span> DateOnly<span class="token operator">?</span> trialEndsAt<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        ServiceName <span class="token operator">=</span> serviceName<span class="token punctuation">;</span>
        Price <span class="token operator">=</span> price<span class="token punctuation">;</span>
        StartDate <span class="token operator">=</span> startDate<span class="token punctuation">;</span>
        TrialEndsAt <span class="token operator">=</span> trialEndsAt<span class="token punctuation">;</span>
        Status <span class="token operator">=</span> SubscriptionStatus<span class="token punctuation">.</span>Active<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">Cancel</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>Status <span class="token operator">==</span> SubscriptionStatus<span class="token punctuation">.</span>Canceled<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">"Subscription already canceled."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> SubscriptionStatus<span class="token punctuation">.</span>Canceled<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> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">enum</span> SubscriptionStatus
<span class="token punctuation">{</span>
    Active <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
    Canceled <span class="token operator">=</span> <span class="token number">2</span>
<span class="token punctuation">}</span>
</code></pre><p>The next step is to create the database related files. In the root of the project, create a new folder called &ldquo;Infrastructure&rdquo; and add the following class to it:</p><ul><li>AppDbContext</li></ul><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> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<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>Subscription<span class="token operator">&gt;</span> Subscriptions <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Subscription<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 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 punctuation">}</span>
</code></pre><h2 id="-feature-1-createsubscription"> Feature 1: CreateSubscription</h2><h3 id="command">Command</h3><p>Our first feature will be used to create a new subscription. Inside the <code>Features/Subscriptions/</code> folder, create a new folder called &ldquo;CreateSubscriptions&rdquo; (all the classes below should be created within this structure: <code>Features/Subscriptions/CreateSubscriptions</code>), and inside it, add the following record:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> record <span class="token function">CreateSubscriptionCommand</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> ServiceName<span class="token punctuation">,</span>
    <span class="token keyword">decimal</span> Price<span class="token punctuation">,</span>
    DateOnly StartDate<span class="token punctuation">,</span>
    DateOnly<span class="token operator">?</span> TrialEndsAt
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="validator">Validator</h3><p>Next, let&rsquo;s create a validator. Create the class below:</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">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateSubscriptionValidator</span> <span class="token punctuation">:</span> AbstractValidator<span class="token operator">&lt;</span>CreateSubscriptionCommand<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">CreateSubscriptionValidator</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>ServiceName<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">MaximumLength</span><span class="token punctuation">(</span><span class="token number">100</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">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>TrialEndsAt<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GreaterThan</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>StartDate<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">When</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>TrialEndsAt<span class="token punctuation">.</span>HasValue<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="handler">Handler</h3><p>The concept of a Handler is common in projects that use Vertical Slice Architecture. A Handler is the component responsible for executing the application&rsquo;s use case&mdash;that is, it contains the logic of the functionality.</p><p>Typically, the Handler is the class or function that receives a command or query and executes the corresponding operation, coordinating business rules, database access, calls to other services and response return.</p><p>Handlers work well in architectures that use Command Query Responsibility Segregation (CQRS), especially when using MediatR. Here, the concept doesn&rsquo;t depend on MediatR; it already exists simply with the organization by slices.</p><p>Now, let&rsquo;s create our first Handler. To do this, create the classes below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CreateSubscriptionCommand</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> ServiceName<span class="token punctuation">,</span>
    <span class="token keyword">decimal</span> Price<span class="token punctuation">,</span>
    DateOnly StartDate<span class="token punctuation">,</span>
    DateOnly<span class="token operator">?</span> TrialEndsAt
<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> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<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">CreateSubscriptionHandler</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">Handle</span><span class="token punctuation">(</span>CreateSubscriptionCommand command<span class="token punctuation">,</span> AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> subscription <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Subscription</span><span class="token punctuation">(</span>
            command<span class="token punctuation">.</span>ServiceName<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>Price<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>StartDate<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>TrialEndsAt
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        dbContext<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>subscription<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> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/subscriptions/{subscription.Id}"</span><span class="token punctuation">,</span> subscription<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 handler class has a method to create a new subscription, and returns an HTTP <code>Created</code> status.</p><h3 id="endpoint">Endpoint</h3><p>In Vertical Slice Architecture, it&rsquo;s also common to declare separate endpoints. In this case, we&rsquo;ll have one endpoint for each use case. Create the following class:</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">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateSubscriptionEndpoint</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">Map</span><span class="token punctuation">(</span>WebApplication app<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">"/subscriptions"</span><span class="token punctuation">,</span> 
            <span class="token keyword">async</span> <span class="token punctuation">(</span>CreateSubscriptionCommand command<span class="token punctuation">,</span> 
                AppDbContext dbContext<span class="token punctuation">,</span> 
                IValidator<span class="token operator">&lt;</span>CreateSubscriptionCommand<span class="token operator">&gt;</span> validator<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> validation <span class="token operator">=</span> <span class="token keyword">await</span> validator<span class="token punctuation">.</span><span class="token function">ValidateAsync</span><span class="token punctuation">(</span>command<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>validation<span class="token punctuation">.</span>IsValid<span class="token punctuation">)</span>
                    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">ValidationProblem</span><span class="token punctuation">(</span>validation<span class="token punctuation">.</span><span class="token function">ToDictionary</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> <span class="token keyword">await</span> CreateSubscriptionHandler<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> dbContext<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>Here we are defining an endpoint for creating a new subscription. It starts by validating the data, and if it&rsquo;s valid, it uses the handler to create the new record and return the expected status.</p><h2 id="-feature-2-cancelsubscription"> Feature 2: CancelSubscription</h2><p>The subscription creation flow is ready. Now let&rsquo;s consider the cancellation use case. Following the same logic as before, within the <code>Features/Subscriptions</code> structure, create a new folder called <code>CancelSubscriptions</code> and add the following classes to that folder:</p><h3 id="command-1">Command</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CancelSubscriptionCommand</span><span class="token punctuation">(</span>Guid Id<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="handler-1">Handler</h3><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> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<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">CancelSubscriptionHandler</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">Handle</span><span class="token punctuation">(</span>
        CancelSubscriptionCommand command<span class="token punctuation">,</span>
        AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> subscription <span class="token operator">=</span> <span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>Subscriptions
            <span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Id <span class="token operator">==</span> command<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>subscription <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">try</span>
        <span class="token punctuation">{</span>
            subscription<span class="token punctuation">.</span><span class="token function">Cancel</span><span class="token punctuation">(</span><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 punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InvalidOperationException</span> ex<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 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">return</span> Results<span class="token punctuation">.</span><span class="token function">NoContent</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="endpoint-1">Endpoint</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<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">CancelSubscriptionEndpoint</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">Map</span><span class="token punctuation">(</span>WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token function">MapDelete</span><span class="token punctuation">(</span><span class="token string">"/subscriptions/{id:guid}"</span><span class="token punctuation">,</span>
            <span class="token keyword">async</span> <span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> AppDbContext dbContext<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> command <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CancelSubscriptionCommand</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">return</span> <span class="token keyword">await</span> CancelSubscriptionHandler<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> dbContext<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><h3 id="program-class">Program Class</h3><p>The final step is to configure the Program class to use the endpoints we created above, to save time, we&rsquo;ll use an in-memory database. So add the following code to the Program class:</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">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<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>opt <span class="token operator">=</span><span class="token operator">&gt;</span>
    opt<span class="token punctuation">.</span><span class="token function">UseInMemoryDatabase</span><span class="token punctuation">(</span><span class="token string">"subscriptions-db"</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">AddValidatorsFromAssemblyContaining<span class="token punctuation">&lt;</span>CreateSubscriptionValidator<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>

CreateSubscriptionEndpoint<span class="token punctuation">.</span><span class="token function">Map</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span>
CancelSubscriptionEndpoint<span class="token punctuation">.</span><span class="token function">Map</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>Note that the Program class is quite simple, only creating configurations such as the use of the in-memory database, validations and the calling of the endpoint methods: <code>CreateSubscriptionEndpoint.Map(app);</code> and <code>CancelSubscriptionEndpoint.Map(app);</code>.</p><h2 id="-testing-the-application"> Testing the Application</h2><p>Now, let&rsquo;s run the endpoints and verify that the application is functional.</p><p>Run the application and make a POST request to the route <code>http://localhost:5127/subscriptions</code> using the JSON below as the body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
  <span class="token string">"serviceName"</span><span class="token punctuation">:</span> <span class="token string">"Aspflix"</span><span class="token punctuation">,</span>
  <span class="token string">"price"</span><span class="token punctuation">:</span> <span class="token number">39.90</span><span class="token punctuation">,</span>
  <span class="token string">"startDate"</span><span class="token punctuation">:</span> <span class="token string">"2026-03-01"</span><span class="token punctuation">,</span>
  <span class="token string">"trialEndsAt"</span><span class="token punctuation">:</span> <span class="token string">"2026-03-15"</span>
<span class="token punctuation">}</span>
</code></pre><p>If everything goes well, you will receive the following response in a debugger like Progress Telerik <a target="_blank" href="https://www.telerik.com/fiddler/fiddler-everywhere">Fiddler Everywhere</a>:</p><p><img title="create a subscription" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/create-a-subscription.png?sfvrsn=29b0495f_2" alt="Create a subscription" /></p><p>And to test the cancellation, simply execute a DELETE request to the route: <code>http://localhost:5127/subscriptions/ID_HERE</code>.</p><p>And you will receive the following response:</p><p><img title="cancel a subscription" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/cancel-a-subscription.png?sfvrsn=7b047aec_2" alt="Cancel a subscription" /></p><p>If you try to cancel again, you will receive a validation error.</p><p><img title="cancel a subscription error" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/cancel-a-subscription-error.png?sfvrsn=197162f9_2" alt="Cancel a subscription error" /></p><h2 id="conclusion">Conclusion</h2><p>Vertical Slice Architecture offers an alternative approach focused on system functionalities, rather than predefined structures.</p><p>As we saw in the article, this brings advantages such as ease of maintenance, due to the low cognitive requirements for understanding the project. It also offers agility in finding functions, as business rules are separated by use case. Furthermore, we implemented a subscription project in ASP.NET Core using Slice Architecture.</p><p>I hope this post helps you decide which architectural approach to use when creating or refactoring 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">Creating More Realistic Tests with In-Memory Databases in ASP.NET Core
</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/creating-more-realistic-tests-memory-databases-aspnet-core">Testing ASP.NET Core APIs with in-memory</a> SQLite and JustMock enables validation of real-world scenarios like pagination, keys and rules without a real database.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17357140.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:f5808969-92f6-4e38-b801-f36a47627abf</id>
    <title type="text">Visual Studio 2026, 6 Months Later</title>
    <summary type="text">Throughout this post, we’ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</summary>
    <published>2026-06-04T21:42:24Z</published>
    <updated>2026-09-09T10:43:31Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17354314/visual-studio-2026-6-months-later"/>
    <content type="text"><![CDATA[<p><span class="featured">Throughout this post, we&rsquo;ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</span></p><p>I remember exactly where I was when <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">Visual Studio 2026 dropped</a> back in November: at my desk, watching the Microsoft launch event with one eye and refactoring an <code>IDataReader</code> implementation with the other.</p><p>The keynote was typical Microsoft. There was a lot of &ldquo;reimagining how developers work.&rdquo; There was, of course, a slide with the word &ldquo;intelligent&rdquo; on it at least eight times. And the word &ldquo;Copilot&rdquo; appeared on screen so many times I started to wonder if Microsoft&rsquo;s marketing team had been replaced by an infinite loop that just prints <code>Copilot</code> forever.</p><p>Then I installed the thing.</p><p>The first week with VS 2026 was the most disoriented I&rsquo;ve felt in a Visual Studio release since <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/a-design-with-all-caps/">the menu bar went all-caps in 2012</a> and I spent a month reading angry forum threads about it.</p><p>In 2026, I was tripped up by the just-slightly different Solution Explorer, the Copilot panel that lives where my Test Explorer used to live, the general Copilot onslaught and the interesting design choices. I felt like everything on my desk had been moved slightly to the left.</p><p>That was temporary. As with most new technology, eventually things felt normal again. I was finding workflows that genuinely beat my old ones and I stopped noticing the cosmetic changes. So here&rsquo;s where I am six months later &hellip; same machine, same kind of solution I work in every day, just a different IDE around it.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/vs2026-overview.png?sfvrsn=4ef376fb_2" alt="" /><br /><span style="font-size:14px;">We meet again, old friend. At least the menu bar isn't in caps.</span></p><p>Six months in, I feel comfortable. Six months is long enough to live with the rough edges, and find what&rsquo;s genuinely useful. So I&rsquo;m here to share them with you.</p><p>As an aside: to keep my sanity intact, I&rsquo;m going to use the word &ldquo;Copilot&rdquo; only when I absolutely have to. Microsoft has decided that every feature in Visual Studio is now Copilot-something &hellip; Copilot Chat, Copilot Edits, Copilot Agent, Copilot Workspaces, GitHub Copilot, Copilot for Azure, Copilot for Pull Requests and so on. So when I say &ldquo;the agent&rdquo; or &ldquo;the AI thing in the sidebar,&rdquo; you&rsquo;ll know what I mean.</p><h2 id="the-performance-story-is-real">The Performance Story Is Real</h2><p>I&rsquo;ll lead with this, mostly because it was the thing I was most skeptical about. Every Visual Studio release for the past decade has touted &ldquo;X% faster solution load!&rdquo; and, to me, every Visual Studio release for the last decade has felt (roughly) the same speed once you test it with your beefy codebase.</p><p>VS 2026 is genuinely faster. Not &ldquo;marketing-faster&rdquo; &hellip; actually faster. The <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2022/">64-bit-everywhere story that started with VS 2022</a> has matured into something that handles large solutions without the periodic &ldquo;please hold&rdquo; pauses we&rsquo;re all familiar with. In Microsoft&rsquo;s VS2026 launch post, they say they <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">cut hangs by over 50%</a>.</p><p>The IntelliSense responsiveness is the part I notice most. Typing a <code>.</code> after an object now produces suggestions roughly when I expect them to appear. A low bar? Definitely, but previous versions frequently limboed under it.</p><h2 id="agent-mode-is-useful-when-you-pick-the-right-job">Agent Mode Is Useful, When You Pick the Right Job</h2><p>GitHub Copilot&rsquo;s <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-agent-mode?view=visualstudio">agent mode in Visual Studio 2026</a>&mdash;or, as Microsoft prefers, <em>GitHub Copilot Agent Mode powered by Copilot in Visual Studio with Copilot</em>&mdash;has improved tremendously since Visual Studio 2022. Six months in, I think it&rsquo;s genuinely useful, but for a specific set of tasks.</p><p>In my experience, it works well with scoped, well-defined refactors. If I want to ask: <em>Hey Copilot, add a new health check for this Cosmos container and follow the pattern in my existing health checks.</em> (Yes, I sometimes talk to Copilot like a home assistant.) Or: <em>Take my new controller and add the same logging pattern as the others in this project.</em> And, after discovering some methods in a codebase with 11 nested if statements: <em>Convert this <code>for</code> loop to a LINQ expression but keep it readable.</em> It&rsquo;s effective enough that I&rsquo;ve stopped writing this kind of code by hand.</p><p>In my experience, things get harder when the agent needs to understand why something exists. The agent will happily refactor a piece of code that only exists because of, say, a weird third-party integration constraint from 2018, and the resulting code will be cleaner, more maintainable &hellip; and broken in production. Did I actually break something in production? Not this time. But I would have.</p><p>To be fair to the agent, this is exactly where it needs context. It doesn&rsquo;t read minds (although it sure does try). Here&rsquo;s a pattern I&rsquo;ve leaned on.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// AGENT NOTES:</span>
<span class="token comment">// - This service must remain synchronous; the upstream caller cannot</span>
<span class="token comment">//   be made async without a contract change.</span>
<span class="token comment">// - The string "PRIME" is required by the third-party integration. </span>
<span class="token comment">//   I don't like it but they won't change it. Do not "improve" it </span>
<span class="token comment">//   to PersonType.Prime.ToString().</span>
<span class="token comment">// - Cancellation tokens must be forwarded; do NOT pass `default`.</span>
</code></pre><p>When the agent has clear context like this, it really delivers. Below is the agent producing two new health check files plus a registration update in <code>Program.cs</code>, all from one prompt asking it to mirror an existing pattern. The result is a careful refactoring with the right substitutions swapped in. That&rsquo;s exactly the kind of work I&rsquo;m happy to hand off.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/gh-copilot-agent.png?sfvrsn=a2b5eefe_2" alt="" /><br /><span style="font-size:14px;">The agent panel and one of the two generated files. Three files changed, zero hand-written by me. (My team is thankful for the latter.)</span></p><p>This sounds like babysitting, and it is. Welcome to AI-driven development, my friends. The 10 seconds of setup saves me from a 10-minute cleanup later. When the prep is solid, the result is solid.</p><p>For more information on <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-chat-context?view=visualstudio#use-custom-instructions">custom instructions, check out the Microsoft documentation</a>.</p><h2 id="test-explorer-isnt-frustrating">Test Explorer Isn&rsquo;t Frustrating</h2><p>In my experience, the Test Explorer seems to be much improved in Visual Studio 2026. If I had to sum it up in previous versions, I&rsquo;d say &ldquo;it&rsquo;s fine&rdquo; &hellip; after all, do we ever get jazzed about tests? In previous releases, I kept saying &ldquo;it&rsquo;s fine&rdquo; until I had a few thousand tests, at which point it started getting opinionated about which tests to run.</p><p>The new version is faster, the filtering predictably works, and it doesn&rsquo;t lose track of which tests are running when you switch branches mid-execution. Try it yourself. Run your full test suite and watch the Test Explorer not embarrass itself. It&rsquo;s a small joy.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/test-explorer.png?sfvrsn=222a8ba_2" alt="" /><br /><span style="font-size:14px;">One of those things you don't notice until it stops getting in your way.</span></p><h2 id="better-git-integration">Better Git Integration</h2><p>Visual Studio&rsquo;s Git tooling has historically been one of the IDE&rsquo;s more underwhelming features. Until recently, my workflow was:</p><ol><li>Make code changes in Visual Studio.</li><li>Run Git commands in my terminal (or a third-party tool like GitHub Desktop)</li><li>Ignore the IDE&rsquo;s Git panel entirely.</li></ol><p>With VS 2026, I&rsquo;m finally doing real Git work inside the tool. The new staging interface is better. The conflict resolution UI is also better than it used to be. The &ldquo;please show me an actual diff and not a vague summary&rdquo; feature is good.</p><p>Now, it&rsquo;s no GitKraken and it doesn&rsquo;t try to be. But it&rsquo;s good enough that I no longer switch over to a terminal for routine operations, and that works for me.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/git-conflict.png?sfvrsn=62f8884a_2" alt="" /><br /><span style="font-size:14px;">Three-way merge view, in the IDE I'm already in. Rejoice!</span></p><h2 id="hot-reload-that-reloads-more-often">Hot Reload That Reloads More Often</h2><p>Hot Reload has been one of those features that worked on Friday afternoons if I said &ldquo;please work&rdquo; (especially for ASP.NET Core). Any other time, I&rsquo;d change a method body, save, watch the IDE think very hard about it, and then get: <em>&ldquo;Hot Reload was unable to apply your changes. Restart the application?&rdquo;</em></p><p>The &ldquo;hot&rdquo; in &ldquo;Hot Reload&rdquo; was <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/will_microsoft_ever_fix_hot_reload_in_net/">stretching the truth</a> at times (with the understanding that getting it right <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/comment/naac88s/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">isn&rsquo;t as simple in .NET</a> as it is for languages like JavaScript).</p><p>With VS 2026, I now reach for it instinctively instead of cynically. Method-body edits in controllers, services and Razor pages just apply. You change a string in a controller, hit Save and see the change. You know, the way it was supposed to work in 2021. The way it kind of worked in 2023. The way it now actually works in 2026.</p><p>When Hot Reload can&rsquo;t apply a change (because the change requires a metadata update or you&rsquo;ve added a field or whatever the actual reason is), the IDE now tells me clearly why instead of vaguely throwing up its hands. That alone goes a long way. I know whether to keep iterating or to just restart the host.</p><h3 id="debugger-improvements-especially-with-async-traces">Debugger Improvements (Especially with Async Traces)</h3><p>Have you ever stared at an async stack trace and felt your soul leave your body? Just me?</p><p>Historically, when an exception bubbled up from somewhere deep in an <code>async</code>/<code>await</code> chain, the stack trace was a wall of <code>MoveNext</code> calls and state machine internals. It was tremendously useful if you wanted to learn about the C# compiler, but not great if you wanted to know which of your methods actually threw.</p><p>Things are looking better in Visual Studio 2026. The debugger now stitches together async call chains into something that reads like the call graph you actually wrote. The <code>MoveNext</code> noise is collapsed by default. The original calling method is shown as the parent frame and not buried six levels down. When an exception is thrown inside a <code>Task.WhenAll</code>, you can see which task threw without reverse-engineering the parallel structure.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-06/debugger.png?sfvrsn=53980e30_2" alt="" /><br /><span style="font-size:14px;">An async exception, with the actual call chain visible. No <code>MoveNext</code> translation required.</span></p><p>On top of that, conditional breakpoints feel faster to evaluate, and the tracepoints (where you can log a message without breaking) is a one-click affair instead of a buried right-click option.</p><p>Small wins. But debugging is the thing I do every day, and small wins on the thing you do every day add up fast.</p><h2 id="the-verdict">The Verdict</h2><p>Look, the Copilot stuff is genuinely impressive. The agent demos at the keynote were the kind of thing that makes you want to fire up the installer immediately. And after six months, I&rsquo;ll admit that I use it every day.</p><p>But Visual Studio 2026 is a genuine step forward for the boring reasons. The performance is real. The Test Explorer doesn&rsquo;t stress me out. The debugger provides stack traces a human can read. The agent mode handles the kind of work I&rsquo;d previously do by hand. Those are the wins that show up in my daily work, not the ones that show up in keynote demos.</p><p>If you&rsquo;re on Visual Studio 2022 and wondering whether to upgrade: yes. Ignore the marketing, and let the unglamorous improvements do their job. Six months later, you won&rsquo;t be excited about Visual Studio 2026 &hellip; and that&rsquo;s exactly the point. You&rsquo;ll just be working faster.</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">Extension Properties: C# 14&rsquo;s Game-Changing Feature for Cleaner Code</h4></div><div class="col-8"><p class="u-fs16 u-mb0">This post introduces <a target="_blank" href="https://www.telerik.com/blogs/extension-properties-csharp-14-game-changing-feature-cleaner-code">extension properties</a>, a new feature of C# 14 that allows you to use properties in your extension methods.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17354314.gif" height="1" width="1"/>]]></content>
  </entry>
  <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 locally with Microsoft Agent Framework and DevUI. The article also explores how DevUI shortens the agent development loop by providing a visual interface for running workflows and inspecting agent interactions. This can now be addressed with the Progress AI Observability Platform.</summary>
    <published>2026-05-26T19:41:09Z</published>
    <updated>2026-09-09T10:43:31Z</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><span class="featured">This article shows .NET developers how to get started building and debugging agent-based applications locally with Microsoft Agent Framework and DevUI. The article also explores how DevUI shortens the agent development loop by providing a visual interface for running workflows and inspecting agent interactions. This can now be addressed with the Progress AI Observability Platform.</span></p><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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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-09-09T10:43:31Z</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-09-09T10:43:31Z</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://www.telerik.com/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://www.telerik.com/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-09-09T10:43:31Z</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://www.telerik.com/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-09-09T10:43:31Z</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-09-09T10:43:31Z</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://www.telerik.com/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-09-09T10:43:31Z</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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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-09-09T10:43:31Z</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-09-09T10:43:31Z</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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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://www.telerik.com/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>
</feed>
