<?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:33e6abd0-8e8f-4a11-bb6a-1f5a845368d8;id=4744</id>
  <updated>2026-04-11T21:14:59Z</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: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-04-11T21:14:59Z</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-04-11T21:14:59Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17297665/how-to-organize-minimal-apis"/>
    <content type="text"><![CDATA[<p><span class="featured">Minimal APIs arrived with .NET 6 and now, in Version 10, are already part of the daily routine for many developers. In this post, we'll explore best practices for organizing your Minimal APIs and see how Carter can help make them even cleaner, more modular and more elegant.</span></p><p>In contrast to traditional Controller classes, minimal APIs offer a more compact way to create web APIs. The downside of this approach is that the Program class can quickly become bloated. To overcome this problem, we can use some approaches and libraries that help organize the mess and bring order to things.</p><p>In this post, we&rsquo;ll create a Minimal API with a disorganized Program class and then refactor it into a clean and elegant version. We&rsquo;ll also explore a very versatile approach using the Carter library.</p><h2 id="one-class-to-rule-them-all-">One Class to Rule Them All </h2><p>.NET 6 introduced a new format for creating web APIs, eliminating the need for the Startup class and even the Controller classes, responsible for HTTP input and output methods.</p><p>In the Minimal APIs model, the Program class, previously responsible only for bootstrapping the application, now also concentrates everything that was previously in Startup and Controller: service configuration, middleware definition and endpoint mapping.</p><p>This unification considerably reduced the number of classes in the project, but also opened the door to a common problem: the tendency to transform Program.cs into an inflated, confusing and difficult-to-maintain file.</p><p>Without a structured approach to organizing these responsibilities, what should be minimalist can quickly become something far from it. Therefore, adopting strategies and tools that help modularize routes, configurations and business rules is not only a good practice but also essential for maintaining the scalability and clarity of the project.</p><h2 id="organization-is-the-key-️">Organization Is the Key ️</h2><p>When working with Minimal APIs, the tendency is to put everything in the Program class, but the truth is that this class wasn&rsquo;t designed to be a repository, but rather to orchestrate the application. Keeping this class clean, small and focused on composition is the secret to preserving readability and facilitating code evolution.</p><p>And this is only possible when we use consistent organization: separating modules, extracting configurations, delegating routes and preventing implementation details from leaking to the highest level of the application.</p><p>In this section, we&rsquo;ll first create a messy example and then transform Program.cs into a truly minimalist entry point, taking in only what it should take in, while moving the rest to the most appropriate places.</p><h2 id="creating-the-project">Creating the Project</h2><p>The complete application code is available in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/customer-admin">Customer Admin source code</a>.</p><p>To create the base application, you can use the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -n CustomerAdmin
</code></pre><p>Then, open the application and add the following dependencies to the .csproj file:</p><pre class=" language-csharp"><code class="prism  language-csharp"> <span class="token operator">&lt;</span>ItemGroup<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore .SqlServer"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft. EntityFrameworkCore .Design"</span> Version<span class="token operator">=</span><span class="token string">"10.0.1"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>IncludeAssets<span class="token operator">&gt;</span>runtime<span class="token punctuation">;</span> build<span class="token punctuation">;</span> native<span class="token punctuation">;</span> contentfiles<span class="token punctuation">;</span> analyzers<span class="token punctuation">;</span> buildtransitive<span class="token operator">&lt;</span><span class="token operator">/</span>IncludeAssets<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>PrivateAssets<span class="token operator">&gt;</span>all<span class="token operator">&lt;</span><span class="token operator">/</span>PrivateAssets<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>PackageReference<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>ItemGroup<span class="token operator">&gt;</span>
</code></pre><p>Now let&rsquo;s move on to the grand finale, the monstrous Program.cs class. Replace the code in the Program class with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

To be eligible for a return:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h2 id="running-the-application-and-testing-the-rag">Running the Application and Testing the RAG</h2><p>Now that everything is configured, we can run the application and test the endpoint that will generate the response. In this post, we&rsquo;ll use Progress Telerik <a href="https://www.telerik.com/fiddler/fiddler-everywhere" target="_blank">Fiddler Everywhere</a> for this. Run the application and make the following request:</p><p>Route: POST - <code>https://localhost:PORT/api/policy/ask</code></p><p>Body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
    <span class="token string">"question"</span><span class="token punctuation">:</span> <span class="token string">"Can I return an opened product?"</span>
<span class="token punctuation">}</span>
</code></pre><p>So the generated response will be something like this:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/rag-response.png?sfvrsn=8fa57c8b_2" title="policy response" alt="Policy response" /></p><p>The complete response returned by the model was:</p><pre class=" language-text"><code class="prism  language-text">According to the return policy, most new, unopened items can be returned within 30 days for a full refund. If you have an opened product that is defective or damaged, it can be returned or exchanged at any time. If you need further assistance, please contact our support team.
</code></pre><p>Note that the answer provided by the model is aligned with the policy, which states that new and unused items can be returned within 30 days. Furthermore, it demonstrates clarity and objectivity by directly addressing the user&rsquo;s question, conveying confidence and concern for the customer, which contributes to a good support experience.</p><h2 id="conclusion">Conclusion</h2><p>The RAG technique allows for the generation of contextualized and intelligent responses, integrating the retrieval of relevant information with advanced synthesis capabilities. The responses produced by RAGs reduce ambiguities, improve the user experience and increase the reliability of interactions, especially in scenarios where document-based accuracy is essential.</p><p>In this post, we created a complete RAG system in ASP,NET Core, integrating it with OpenAI services for generating embeddings and producing contextualized responses. I hope this content serves as a practical reference, facilitating the adoption of the RAG technique whenever you have the opportunity to apply it in your projects.</p><p>If this all seems like a lot of work, there are professional RAG platforms you can explore, such as Progress Agentic RAG.</p><h3 id="progress-agentic-rag">Progress Agentic RAG</h3><p><a target="_blank" href="https://www.progress.com/agentic-rag">Progress Agentic RAG</a> is a RAG-as-a-Service platform that simplifies the creation of augmented reality (AR) retrieval and generation solutions. Instead of requiring proprietary infrastructure or multiple separate tools, it offers a ready-to-use environment for indexing documents, files and even videos, along with integrated metrics to evaluate RAG quality.</p><p>In practice, Progress Agentic RAG stands out for:</p><ul><li>Generative search for websites, which can interpret user intent and build responses using existing content on the page itself.</li><li>Use in sensitive areas, such as the financial sector, supporting decision-making without compromising privacy and security.</li><li>The ability to handle unstructured data, covering more than 60 formats: PDFs, videos, spreadsheets, texts, among others. This greatly helps teams like legal departments, who need quick and accurate answers.</li><li>Intelligent video indexing, allowing the location of specific segments and generating responses based on audiovisual content.</li></ul><p>For developers, Progress Agentic RAG functions as an intelligence layer that can be integrated with minimal effort. This reduces the need to build a RAG pipeline from scratch and accelerates the development of generative AI-based solutions.</p><p><strong>Keep reading:</strong> <a target="_blank" href="https://www.telerik.com/blogs/understanding-rag-retrieval-augmented-generation">Understand more about RAG</a> and get a <a target="_blank" href="https://www.telerik.com/blogs/introductory-walk-through-progress-agentic-rag-dashboard">walk-through of Progress Agentic RAG</a>.</p><img src="https://feeds.telerik.com/link/23052/17268545.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:a2228ce0-ff8c-42cd-a991-cae03f6490b0</id>
    <title type="text">Getting the Right Row on the Screen in the Kendo Grid</title>
    <summary type="text">You can move any row into the user’s view screen (and save the user scrolling to find it) using the KendoGrid scrollToItem method.</summary>
    <published>2026-01-22T17:22:33Z</published>
    <updated>2026-04-11T21:14:59Z</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-04-11T21:14:59Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17253334/building-resilient-apis-retry-pattern"/>
    <content type="text"><![CDATA[<p><span class="featured">Sending and receiving data is the essence of any web API. But what happens when something goes wrong and communication fails? To avoid unexpected issues and enable greater resilience, we can use the Retry Pattern. Learn how to implement this pattern in ASP.NET Core to make your APIs more reliable.</span></p><p>The Retry Pattern helps developers create APIs that are prepared for adverse situations when communicating with external services, such as databases and even other APIs.</p><p>In this post, we&rsquo;ll explore the main challenges faced when working with distributed applications that communicate with each other and discover how the Retry Pattern can be used as a resilience strategy to deal with common failures in these scenarios.</p><p>We&rsquo;ll also see how to implement the Retry Pattern in an ASP.NET Core application using a retry pipeline through the Polly library.</p><h2 id="common-integration-issues">Common Integration Issues</h2><p>Any application that communicates with APIs or external services is susceptible to the risk of communication failures, including timeouts, temporary unavailability, network errors or request limits. While often underestimated, these issues are common and can compromise the user experience and system reliability.</p><p>In this context, we can highlight the following issues that modern systems face when handling integrations:</p><h3 id="temporary-network-errors">Temporary Network Errors</h3><p>When accessing the network, 100% availability is not always guaranteed. Packets can be lost, connections can drop and providers can experience momentary instability. For example, a payment API takes longer than expected to respond, and its call times out (<code>TimeoutException</code>). In practice, the transaction may have been processed, but the application did not receive confirmation.</p><h3 id="temporary-unavailability-of-external-services">Temporary Unavailability of External Services</h3><p>Services may undergo maintenance, experience overload or be offline for a few minutes. For example, an ecommerce website queries a pricing API to update a product&rsquo;s price. If the API is unavailable, the system may fail and display the outdated price to the customer, causing harm and stress for both the consumer and the company selling the product.</p><h3 id="inconsistent-or-invalid-data">Inconsistent or Invalid Data</h3><p>There is always a risk that external APIs may return incomplete data, in an unexpected format or with business errors. For example, a registration API returns addresses without a ZIP code or with invalid characters, breaking the system&rsquo;s internal validations.</p><p>These are just some of the many scenarios in which an API can fail to perform its intended task, resulting in significant problems and losses for the development team and the company. Fortunately, some solutions help mitigate these and other risks by automatically retrying a failed operation in the hope that it will succeed on a second attempt. Next, we&rsquo;ll explore the Retry Pattern, which stands out as a solution for dealing with these temporary failures.</p><h2 id="understanding-the-retry-pattern">Understanding the Retry Pattern</h2><p>The Retry Pattern is a resilience pattern whose central idea is to retry a failed operation, rather than simply giving up on the first attempt. This is useful and, in many cases, indispensable, as distributed systems tend to experience temporary problems, such as momentary network instability or even server overload. Therefore, when a communication or processing failure occurs between two services, it is advisable to at least try again.</p><p>These failures are usually corrected after a period of time. If the action that triggered the failure is retried after a reasonable delay, it is very likely to be successful. For example, imagine a payment service is momentarily overloaded and returns a &ldquo;Service Unavailable (503)&rdquo; error. In this case, it does not mean that the service is permanently down, but rather that it was unable to process the request at that time. If the application retries after a few seconds, it is very likely that the payment will be completed successfully.</p><p>Scenarios like this show that the Retry Pattern can be extremely useful, as it helps prevent temporary glitches from resulting in significant losses, maintaining a stable user experience and reducing the need for potential manual intervention.</p><p>The image below demonstrates two scenarios, with and without the use of the Retry Pattern.</p><p><img title="using retry pattern" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/using-retry-pattern.png?sfvrsn=39007a84_2" alt="Using retry pattern" /></p><p><img title="without using retry pattern" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/without-using-retry-pattern.png?sfvrsn=1463e64e_2" alt="Without using retry pattern" /></p><h2 id="implementing-retry-pattern-in-asp.net-core">Implementing Retry Pattern in ASP.NET Core</h2><p>To implement the Retry Pattern, we&rsquo;ll consider a scenario where a product catalog API needs to access another API to retrieve product image data. The premise is that we can&rsquo;t leave the client without the product image. So, even if the first request fails, we have to try again.</p><p>So, first, we&rsquo;ll create a simple product catalog API and implement a retry policy and a secondary API to return product images. Finally, we&rsquo;ll force an error on the first two attempts and a success on the third, to validate that the policy is working.</p><p>You can access the complete source code in this GitHub repository: <a href="https://github.com/zangassis/PollyProducts">PollyProducts source code</a>.</p><h2 id="-polly-for-resilience"> Polly for Resilience</h2><p><a href="https://www.pollydocs.org/index.html">Polly</a> is a library focused on resilience and fault tolerance for .NET applications.</p><p>It is widely known and helps systems handle temporary failures when calling external APIs, databases, network services and more, allowing developers to implement resilience policies to anticipate temporary unavailability scenarios.</p><p>In addition to basic features like automatic operation retry, Polly offers advanced features like circuit breaker, which temporarily stops new calls after a consecutive number of failures, avoiding overloading unavailable services. Another important feature is fallback, which defines an alternative action when the main operation fails, for example, returning cached data if the API is down.</p><h2 id="creating-the-product-catalog-api">Creating the Product Catalog API</h2><p>So, first, let&rsquo;s create the API that will make requests to the secondary API to return data from the product catalog. This is where we&rsquo;ll create a pipeline with a retry policy.</p><p>To create the base application, you can run the following commands in your terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o PollyProducts
</code></pre><p>Then, run the following commands to download and install the Polly NuGet Packages:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Polly

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

dotnet new webapi -n CustomerInsights

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

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

dotnet add package Microsoft.EntityFrameworkCore.Tools

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                <span class="token keyword">var</span> lastPoint <span class="token operator">=</span> points<span class="token punctuation">.</span><span class="token function">Last</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">float</span> highlightRadius <span class="token operator">=</span> <span class="token number">2f</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>displayScale<span class="token punctuation">;</span>
                context<span class="token punctuation">.</span>Canvas<span class="token punctuation">.</span><span class="token function">DrawCircle</span><span class="token punctuation">(</span>lastPoint<span class="token punctuation">.</span>X<span class="token punctuation">,</span> lastPoint<span class="token punctuation">.</span>Y<span class="token punctuation">,</span> highlightRadius<span class="token punctuation">,</span> paint<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s examine some important points from the previous code:</p><ol><li>It inherits from <code class="inline-code">DataGridCellRenderer</code>, a class essential for displaying content in the DataGrid.</li><li>The method <code class="inline-code">RenderContainer</code> is used to draw the content.</li><li>The stock history is obtained, which is a list of simulated previous values.</li><li>An area to draw the chart is defined.</li><li>In the first loop <code class="inline-code">for</code>, the history is transformed into normalized <code class="inline-code">X</code> and <code class="inline-code">Y</code> coordinates according to the chart area.</li><li>Through <code class="inline-code">totalChange</code> and <code class="inline-code">lineColor</code>, it is determined whether the color should be green or red.</li><li>A <code class="inline-code">SKPath</code> is created with the calculated points, and the chart is drawn.</li><li>Small circles are added using <code class="inline-code">DrawCircle</code> to highlight each individual point.</li><li>A larger circle is drawn to represent the most recent value.</li></ol><p>Once we have the new renderer, we will use it in the DataGrid as follows:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn</span>
    <span class="token attr-name">CanUserFilter</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>False<span class="token punctuation">"</span></span>
    <span class="token attr-name">CanUserSort</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>False<span class="token punctuation">"</span></span>
    <span class="token attr-name">CellRenderer</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource SparklineRenderer}<span class="token punctuation">"</span></span>
    <span class="token attr-name">HeaderText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Trend<span class="token punctuation">"</span></span>
    <span class="token attr-name">PropertyName</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Symbol<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Style</span> <span class="token attr-name">BasedOn</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{StaticResource HeaderStyle}<span class="token punctuation">"</span></span> <span class="token attr-name">TargetType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>telerik:DataGridColumnHeaderAppearance<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn.HeaderStyle</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">telerik:</span>DataGridTextColumn</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>With the new cell added to the DataGrid, we will see that the custom chart appears in the UI:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/a-datagrid-control-with-a-custom-column-to-display-a-stock-trend.gif?sfvrsn=cc5b43ef_2" alt="A DataGrid control with a custom column to display a stock trend" /></p><p>Undoubtedly, being able to customize the content of the cells opens a new door of opportunity to present quick and accurate information to app users.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this post, you have learned how to create SignalR-based applications to achieve real-time communication, something crucial in stock, cryptocurrency or similar applications.</p><p>You have also seen how the Telerik UI for .NET MAUI DataGrid control is an excellent option for displaying this content, not only for its properties to work with data but also for its flexibility in customization. It is your time to provide your clients with unique data-driven experiences.</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-maui" class="Btn">Try UI for .NET MAUI</a></p><img src="https://feeds.telerik.com/link/23052/17211761.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e31b81fd-9ca6-4351-84b4-b658c403475a</id>
    <title type="text">Automating Tasks with Quartz.NET</title>
    <summary type="text">Quartz enables flexible task scheduling in .NET applications, making it ideal for systems that require automation, such as notifications, report generation and periodic synchronization. In this post, we'll build a job for periodic history-cleaning with Quartz.</summary>
    <published>2025-10-13T15:54:13Z</published>
    <updated>2026-04-11T21:14:59Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17184760/automating-tasks-quartz-net"/>
    <content type="text"><![CDATA[<p><span class="featured">Quartz enables flexible task scheduling in .NET applications, making it ideal for systems that require automation, such as notifications, report generation and periodic synchronization. In this post, we'll build a job for periodic history-cleaning with Quartz.</span></p><p>Running repetitive routines is just one of a backend developer&rsquo;s many tasks, and, to be honest, writing a <code class="inline-code">while(true)</code> loop with Thread.Sleep isn&rsquo;t exactly the most elegant solution and certainly not the most scalable. This is where Quartz.NET shines as a great option for scheduling tasks for .NET applications.</p><p>In this post, we&rsquo;ll learn how to automate recurring tasks using Quartz.NET by implementing a job to clear process history. Additionally, we&rsquo;ll look at how to set up a schedule for automated job service execution.</p><h2 id="what-is-quartz.net">⏰ What Is Quartz.NET?</h2><p><a target="_blank" href="https://www.quartz-scheduler.net">Quartz.NET</a> is an open-source job scheduling system (under the Apache License, Version 2.0) designed for .NET applications that need to execute automated tasks accurately and reliably.</p><p>It is designed to handle complex scheduling scenarios, such as cron-based executions, custom intervals, task dependencies and distributed execution, making it a great choice for enterprise systems and backend applications.</p><h2 id="why-choose-quartz"> Why Choose Quartz?</h2><p>Quartz.NET stands out when it comes to task scheduling in .NET applications due to its simplicity and available features. It allows you to create everything from simple tasks with fixed intervals to complex schedules with cron expressions, supporting recurring executions, delays, job dependencies and database persistence.</p><p>Furthermore, it is open source (Apache 2.0), has good documentation, wide community adoption and easy integration with dependency injection and ASP.NET Core applications.</p><p>While there are alternative solutions, Quartz.NET stands out as an excellent choice for automations such as email delivery, report generation, data synchronization and other critical processes that demand reliability and runtime control.</p><h2 id="creating-a-history-cleaning-job-with-quartz"> Creating a History Cleaning Job with Quartz</h2><p>As a use case, we will build a job to clean up the process history. The application will have the following functionalities:</p><ul><li>A job called <code class="inline-code">TaskMonitor</code> that searches for processes that occurred before the last 30 days and deletes them; it then inserts a record into a notification table</li><li>Integration with SQL Server and EF Core</li><li>Sample data to verify the job&rsquo;s operation</li><li>Configuring the execution interval with a cron expression</li></ul><p>The complete source code for the application built in the post is available in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/task-monitor">TaskMonitor Source Code</a>.</p><h2 id="creating-the-application"> Creating the Application</h2><p>To create the sample application, you can use the following command:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -n TaskMonitor
</code></pre><h3 id="installing-the-packages">Installing the Packages</h3><p>Then, install the packages required to create the job.</p><p><strong>Quartz packages:</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Quartz
dotnet add package Quartz.AspNetCore
</code></pre><p><strong>Other packages:</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
</code></pre><h3 id="model-classes">Model Classes</h3><p>To create the model classes, first create a new folder called &ldquo;Models&rdquo; and, inside it, add the following classes:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> TaskMonitor<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">ProcessHistory</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> ProcessName <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">string</span> Status <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> Details <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> CreatedBy <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> TaskMonitor<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">Notification</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> Message <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><h3 id="creating-the-context-and-seed-classes">Creating the Context and Seed Classes</h3><p>The next step is to create the context class to configure EF Core entities and a class to initialize sample data. So, create a new folder called &ldquo;Data&rdquo; and add the following classes inside it:</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> TaskMonitor<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> TaskMonitor<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>ProcessHistory<span class="token operator">&gt;</span> ProcessHistory <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> DbSet<span class="token operator">&lt;</span>Notification<span class="token operator">&gt;</span> Notifications <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 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><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> TaskMonitor<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> TaskMonitor<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> FinanceTracker<span class="token punctuation">.</span>Data<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">DbInitializer</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">SeedAsync</span><span class="token punctuation">(</span>AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>Database<span class="token punctuation">.</span><span class="token function">MigrateAsync</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><span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>ProcessHistory<span class="token punctuation">.</span><span class="token function">AnyAsync</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> now <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">;</span>

            <span class="token keyword">var</span> sampleEntries <span class="token operator">=</span> Enumerable<span class="token punctuation">.</span><span class="token function">Range</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>i <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token class-name">ProcessHistory</span>
                <span class="token punctuation">{</span>
                    CreatedAt <span class="token operator">=</span> now<span class="token punctuation">.</span><span class="token function">AddDays</span><span class="token punctuation">(</span><span class="token operator">-</span>i<span class="token punctuation">)</span><span class="token punctuation">,</span>
                    ProcessName <span class="token operator">=</span> $<span class="token string">"Sample Process: #{i}"</span><span class="token punctuation">,</span>
                    Status <span class="token operator">=</span> <span class="token string">"Completed"</span><span class="token punctuation">,</span>
                    Details <span class="token operator">=</span> $<span class="token string">"Process details: #{i}"</span><span class="token punctuation">,</span>
                    CreatedBy <span class="token operator">=</span> <span class="token string">"admin"</span>
                <span class="token punctuation">}</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>ProcessHistory<span class="token punctuation">.</span><span class="token function">AddRangeAsync</span><span class="token punctuation">(</span>sampleEntries<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 punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="creating-the-job-class">Creating the Job Class</h3><p>Now, let&rsquo;s create the job class that will perform the data cleanup and insert the notification log. </p><p>Create a new folder called &ldquo;Jobs&rdquo; and add the following class to it:</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> Quartz<span class="token punctuation">;</span>
<span class="token keyword">using</span> TaskMonitor<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> TaskMonitor<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> TaskMonitor<span class="token punctuation">.</span>Jobs<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CleanupHistoryJob</span> <span class="token punctuation">:</span> IJob
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> AppDbContext _dbContext<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ILogger<span class="token operator">&lt;</span>CleanupHistoryJob<span class="token operator">&gt;</span> _logger<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CleanupHistoryJob</span><span class="token punctuation">(</span>AppDbContext dbContext<span class="token punctuation">,</span> ILogger<span class="token operator">&lt;</span>CleanupHistoryJob<span class="token operator">&gt;</span> logger<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _dbContext <span class="token operator">=</span> dbContext<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">Execute</span><span class="token punctuation">(</span>IJobExecutionContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> cutoffDate <span class="token operator">=</span> DateTime<span class="token punctuation">.</span>UtcNow<span class="token punctuation">.</span><span class="token function">AddDays</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> oldEntries <span class="token operator">=</span> <span class="token keyword">await</span> _dbContext<span class="token punctuation">.</span>ProcessHistory
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>h <span class="token operator">=</span><span class="token operator">&gt;</span> h<span class="token punctuation">.</span>CreatedAt <span class="token operator">&lt;</span> cutoffDate<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">if</span> <span class="token punctuation">(</span>oldEntries<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"No old history entries found for cleanup."</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>

        <span class="token keyword">await</span> <span class="token function">RemoveHistory</span><span class="token punctuation">(</span>oldEntries<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> notification <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Notification</span>
        <span class="token punctuation">{</span>
            Message <span class="token operator">=</span> $<span class="token string">"{oldEntries.Count} history entries cleaned up at {DateTime.UtcNow:O}"</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 keyword">await</span> <span class="token function">SaveNotification</span><span class="token punctuation">(</span>notification<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">SaveNotification</span><span class="token punctuation">(</span>Notification notification<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _dbContext<span class="token punctuation">.</span>Notifications<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>notification<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>

        _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Cleanup notification created."</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">RemoveHistory</span><span class="token punctuation">(</span>List<span class="token operator">&lt;</span>ProcessHistory<span class="token operator">&gt;</span> oldEntries<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _dbContext<span class="token punctuation">.</span>ProcessHistory<span class="token punctuation">.</span><span class="token function">RemoveRange</span><span class="token punctuation">(</span>oldEntries<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>

        _logger<span class="token punctuation">.</span><span class="token function">LogInformation</span><span class="token punctuation">(</span><span class="token string">"Deleted {Count} old history entries."</span><span class="token punctuation">,</span> oldEntries<span class="token punctuation">.</span>Count<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 <code class="inline-code">CleanupHistoryJob</code> class implements Quartz&rsquo;s <code class="inline-code">IJob</code> interface, which means that whenever the job is executed, the <code class="inline-code">Execute(IJobExecutionContext context)</code> method will be automatically called by the scheduler, containing the main history cleaning logic.</p><h3 id="configuring-the-program-class">Configuring the Program Class</h3><p>Now let&rsquo;s add the job settings we created earlier to the Program class. Add the following code to it:</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> TaskMonitor<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> Quartz<span class="token punctuation">;</span>
<span class="token keyword">using</span> TaskMonitor<span class="token punctuation">.</span>Jobs<span class="token punctuation">;</span>
<span class="token keyword">using</span> FinanceTracker<span class="token punctuation">.</span>Data<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">AddQuartz</span><span class="token punctuation">(</span>q <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> jobKey <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JobKey</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryJob"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    q<span class="token punctuation">.</span><span class="token generic-method function">AddJob<span class="token punctuation">&lt;</span>CleanupHistoryJob<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts<span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    q<span class="token punctuation">.</span><span class="token function">AddTrigger</span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts
        <span class="token punctuation">.</span><span class="token function">ForJob</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryTrigger"</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">AddQuartzHostedService</span><span class="token punctuation">(</span>opt <span class="token operator">=</span><span class="token operator">&gt;</span> opt<span class="token punctuation">.</span>WaitForJobsToComplete <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

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

<span class="token keyword">using</span> <span class="token punctuation">(</span><span class="token keyword">var</span> scope <span class="token operator">=</span> app<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">CreateScope</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> dbContext <span class="token operator">=</span> scope<span class="token punctuation">.</span>ServiceProvider<span class="token punctuation">.</span><span class="token generic-method function">GetRequiredService<span class="token punctuation">&lt;</span>AppDbContext<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">await</span> DbInitializer<span class="token punctuation">.</span><span class="token function">SeedAsync</span><span class="token punctuation">(</span>dbContext<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 execution of the job is done through the code:</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">AddQuartz</span><span class="token punctuation">(</span>q <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span> 
<span class="token keyword">var</span> jobKey <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JobKey</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryJob"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

q<span class="token punctuation">.</span><span class="token generic-method function">AddJob<span class="token punctuation">&lt;</span>CleanupHistoryJob<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts<span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

q<span class="token punctuation">.</span><span class="token function">AddTrigger</span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts 
<span class="token punctuation">.</span><span class="token function">ForJob</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span> 
<span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryTrigger"</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">AddQuartzHostedService</span><span class="token punctuation">(</span>opt <span class="token operator">=</span><span class="token operator">&gt;</span> opt<span class="token punctuation">.</span>WaitForJobsToComplete <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Here we configure Quartz.NET to run the <code class="inline-code">CleanupHistoryJob</code> job. First, the Quartz service is registered using <code class="inline-code">builder.Services.AddQuartz(...)</code>, passing an internal configuration through the <code class="inline-code">q =&gt; { ... }</code> function.</p><p>Within this configuration, we create an identification key (<code class="inline-code">jobKey</code>) for the job using <code class="inline-code">new JobKey("CleanupHistoryJob")</code>. This key serves to uniquely identify the job within the scheduler.</p><p>Next, the <code class="inline-code">CleanupHistoryJob</code> job is added using <code class="inline-code">q.AddJob&lt;CleanupHistoryJob&gt;(...)</code>, associating it with the created key.</p><p>After that, a trigger is configured for this job using <code class="inline-code">q.AddTrigger(...)</code>. This trigger determines when the job should run. In the example, it is linked to the job using <code class="inline-code">.ForJob(jobKey)</code> and is given its own identity called <code class="inline-code">CleanupHistoryTrigger</code>.</p><p>Finally, <code class="inline-code">builder.Services.AddQuartzHostedService(...)</code> registers the hosted service that keeps Quartz running while the application is active. The <code class="inline-code">opt.WaitForJobsToComplete = true</code> option sets it so that, when the application closes, Quartz waits for all running jobs to finish before shutting down.</p><h2 id="running-the-job"> Running the Job</h2><p>Now that everything is set up, we can run the job and verify its execution through the data. When the job runs, the database will be loaded, and the job will delete records created up to 30 days ago from the history table and finally insert a record into the notification table, as seen in the images below:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-10/running-the-job.png?sfvrsn=4dec7336_2" title="running the job" alt="Running the job" /><br /><span style="font-size:14px;">Job execution showing that 71 records were found to be deleted.</span></p><p>After finishing the job execution, when checking the data, it is possible to notice that both the deletion of the 71 records and the insertion into the history were executed:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-10/checking-the-data.png?sfvrsn=56df04db_2" title="checking the data" alt="Checking the data" /></p><p>This way, we have a functional job that will execute the scheduled routine.</p><h2 id="understanding-the-cron-concept">⌛ Understanding the Cron Concept</h2><p>In technology, &ldquo;cron&rdquo; is a concept commonly associated with routines, originating from the Unix/Linux cron utility used to schedule the automatic execution of commands or scripts at specific times and intervals. The name was inspired by &ldquo;chronos&rdquo; (from the Greek &ldquo;time&rdquo;).</p><p>In the specific context of backend jobs, a cron expression is a string that indicates when the task should be executed, using a standardized format with fields separated by spaces, such as:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-10/cron-demo.png?sfvrsn=d0c48f53_2" title="cron-demo" alt="CRON demo" /></p><p>Each field can contain numbers, ranges, commas and wildcards (* for any value):</p><ul><li>Minute: 0-59</li><li>Hour: 0-23</li><li>Day of the month: 1-31</li><li>Month: 1-12 or abbreviated names (JAN, FEB&hellip;)</li><li>Day of the week: 0-6 (Sunday=0) or names (SUN, MON&hellip;)</li></ul><p>In the job we built above, to run the job locally, we did not make any additional time configuration. However, it is possible to configure a time interval for execution, with the <code class="inline-code">.WithCronSchedule("CRON") );</code> extension method in the Quartz configuration in the Program class.</p><p>The complete configuration would look like this:</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">AddQuartz</span><span class="token punctuation">(</span>q <span class="token operator">=</span><span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> jobKey <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JobKey</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryJob"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    q<span class="token punctuation">.</span><span class="token generic-method function">AddJob<span class="token punctuation">&lt;</span>CleanupHistoryJob<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts<span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    q<span class="token punctuation">.</span><span class="token function">AddTrigger</span><span class="token punctuation">(</span>opts <span class="token operator">=</span><span class="token operator">&gt;</span> opts
        <span class="token punctuation">.</span><span class="token function">ForJob</span><span class="token punctuation">(</span>jobKey<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithIdentity</span><span class="token punctuation">(</span><span class="token string">"CleanupHistoryTrigger"</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithCronSchedule</span><span class="token punctuation">(</span><span class="token string">"0 3 * * *"</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>This way, the job will run every day at 3:00 a.m.</p><h2 id="conclusion"> Conclusion</h2><p>Dealing with daily routines like cleaning up old data, sending emails and generating reports is part of the daily routine for most backend developers. In this context, finding alternatives that simplify this work is always a goal, and it&rsquo;s in these situations that Quartz shines. Its simple configuration and easy integration with .NET are its main strengths.</p><p>In this post, we saw how to implement a history-cleanup job integrated with SQL Server and learned, hands on, how to configure a cron to run the job at defined intervals. However, there are still functions in Quartz that can be explored, such as recording job execution history using native resources like Persistent Store, where Quartz records the execution script in its own tables. This is especially useful if you need scheduling telemetry.</p><p>I hope this post helped you better understand how to use Quartz in your projects and that it serves as a starting point for further automating your backend routines.</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">Tips for Efficient Data Queries 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/tips-efficient-data-queries-aspnet-core">Efficient queries</a> are essential to the performance and scalability of APIs that handle large volumes of data. In this post, we will explore best practices and strategies for optimizing backend queries using ASP.NET Core.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17184760.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:c051eb8a-e9e5-4fa2-a984-b4706720c6fe</id>
    <title type="text">Tips for Efficient Data Queries in ASP.NET Core</title>
    <summary type="text">Efficient queries are essential to the performance and scalability of APIs that handle large volumes of data. In this post, we will explore best practices and strategies for optimizing backend queries using ASP.NET Core.</summary>
    <published>2025-09-30T12:03:02Z</published>
    <updated>2026-04-11T21:14:59Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17174425/tips-efficient-data-queries-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Efficient queries are essential to the performance and scalability of APIs that handle large volumes of data. In this post, we will explore best practices and strategies for optimizing backend queries using ASP.NET Core.</span></p><p>In backend applications that consume relational databases, the way queries are structured can be crucial to application performance. Inefficient queries, excessive data loading and the lack of appropriate filters are some of the pitfalls that can compromise performance, especially in APIs exposed to high access volumes.</p><p>In this post, we will cover techniques for implementing efficient queries, such as designing only the necessary properties, eliminating looping queries, using cursor-based paging, dynamic filters and others.</p><h2 id="-the-importance-of-optimizing-data-searches-in-web-apis"> The Importance of Optimizing Data Searches in Web APIs</h2><p>Efficient data processing is one of the main challenges faced by backend developers today. With the growing demand for data-driven applications, it&rsquo;s become common to handle requests involving large volumes of information, requiring solutions that balance performance, scalability and usability.</p><p>Scenarios such as generating analytical reports, building real-time dashboards and managing large product catalogs in marketplaces are just a few examples where backend performance can become a bottleneck if not properly addressed.</p><p>In this post, we&rsquo;ll explore some performance-improving resources and strategies we can adopt when creating web APIs that will process large volumes of data.</p><p>All code examples covered in this post are available in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/turbo-note-blog">TurboNote Source Code</a>.</p><h2 id="-1.-projecting-only-the-necessary-properties"> 1. Projecting Only the Necessary Properties</h2><p>Using Entity Framework Core (EF Core) to query entity instances is very useful because it abstracts away the complexity of database interactions, allowing the use of strongly typed objects instead of raw SQL commands. However, it&rsquo;s necessary to consider what data should be extracted in queries to optimize them.</p><p>Consider the example below. Although this code only requests the Slug property of each Post, the entire entity is retrieved. Consequently, unnecessary columns are transferred from the database during the query.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> post <span class="token keyword">in</span> db<span class="token punctuation">.</span>Posts<span class="token punctuation">.</span><span class="token function">AsAsyncEnumerable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    post<span class="token punctuation">.</span>Slug<span class="token punctuation">.</span><span class="token function">Validate</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-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CategoryId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Slug<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Title<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UpdatedAt<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
</code></pre><p>It is possible to optimize this query with the Select extension method by telling EF which columns to project:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> postSlug <span class="token keyword">in</span> db<span class="token punctuation">.</span>Posts<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>b <span class="token operator">=</span><span class="token operator">&gt;</span> b<span class="token punctuation">.</span>Slug<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">AsAsyncEnumerable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    postSlug<span class="token punctuation">.</span><span class="token function">Validate</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 resulting SQL retrieves only the desired column:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Slug<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
</code></pre><h2 id="️-eliminating-looped-queries">2. ️ Eliminating Looped Queries</h2><p>The problem known as N+1 queries is common when using EF and iterating over a list, resulting in new queries for each item.</p><p>Below is an example that uses multiple queries:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> posts <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts<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">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> post <span class="token keyword">in</span> posts<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> comments <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Comments
        <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>PostId <span class="token operator">==</span> post<span class="token punctuation">.</span>PostId<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>

    post<span class="token punctuation">.</span>Comments <span class="token operator">=</span> comments<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CommentId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Comments<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">@__post_PostId_0</span>

<span class="token keyword">SELECT</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CommentId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Comments<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">@__post_PostId_0</span>

<span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>successive queries<span class="token punctuation">)</span>
</code></pre><p>If there are 100 posts, the code will do 1 + 100 queries (1 for the posts and 1 for each group of comments).</p><p>Instead, we can do the following:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> comments <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts<span class="token punctuation">.</span><span class="token function">Include</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>Comments<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>
</code></pre><p>Which would result in the following SQL:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CategoryId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Slug<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Title<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UpdatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CommentId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
<span class="token keyword">LEFT</span> <span class="token keyword">JOIN</span> <span class="token punctuation">[</span>Comments<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span> <span class="token keyword">ON</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span>
<span class="token keyword">ORDER</span> <span class="token keyword">BY</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span>
</code></pre><p>Note that when using <code class="inline-code">.Include(p =&gt; p.Comments)</code>, EF already brings the comments related to each post in a single query with <code class="inline-code">LEFT JOIN</code>, avoiding the N+1 problem.</p><h2 id="-using-cursor-based-pagination">3.  Using Cursor-based Pagination</h2><p>Cursor-based pagination is a pagination technique where results are fetched from a marker (the cursor), usually represented by an item&rsquo;s unique key, such as an ID or a creation date.</p><p>It works as follows: Consider a single reference value, called a cursor, which will be used to retrieve the next records from that point.</p><p>Instead of using a page number or an offset as in the OFFSET method, the client sends this cursor in the request (<code class="inline-code">?after=105</code>, for example), and the server returns the records that come after that value, limiting the number with <code class="inline-code">LIMIT</code> or <code class="inline-code">Take</code>. This method is more efficient and reliable, especially in large databases or those that undergo frequent changes, as it avoids the problems of duplicate or lost records that can occur with offset-based pagination.</p><p>One point to note is that, although more efficient than the <code class="inline-code">OFFSET</code> method, cursor-based pagination does not allow for direct jumps to a specific page; it requires sequential navigation.</p><p>The code below demonstrates how to implement cursor-based pagination:</p><pre class=" language-csharp"><code class="prism  language-csharp">app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/posts/pagination"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>
    <span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span><span class="token operator">?</span> after<span class="token punctuation">,</span>
    <span class="token punctuation">[</span>FromQuery<span class="token punctuation">]</span> <span class="token keyword">int</span> pageSize<span class="token punctuation">,</span>
    BlogDbContext 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">const</span> <span class="token keyword">int</span> MaxPageSize <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span>
    pageSize <span class="token operator">=</span> pageSize <span class="token operator">&gt;</span> MaxPageSize <span class="token operator">?</span> MaxPageSize <span class="token punctuation">:</span> pageSize<span class="token punctuation">;</span>

    <span class="token keyword">var</span> query <span class="token operator">=</span> db<span class="token punctuation">.</span>Posts
        <span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">OrderBy</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PostId<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token operator">!</span>after<span class="token punctuation">.</span>HasValue <span class="token operator">||</span> p<span class="token punctuation">.</span>PostId <span class="token operator">&gt;</span> after<span class="token punctuation">.</span>Value<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>pageSize <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Get 1 more to know if there is a next page</span>

    <span class="token keyword">var</span> posts <span class="token operator">=</span> <span class="token keyword">await</span> query<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> hasNextPage <span class="token operator">=</span> posts<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> pageSize<span class="token punctuation">;</span>
    <span class="token keyword">var</span> results <span class="token operator">=</span> posts<span class="token punctuation">.</span><span class="token function">Take</span><span class="token punctuation">(</span>pageSize<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> nextCursor <span class="token operator">=</span> hasNextPage <span class="token operator">?</span> results<span class="token punctuation">.</span><span class="token function">Last</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>PostId <span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token operator">?</span><span class="token punctuation">)</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">Ok</span><span class="token punctuation">(</span><span class="token keyword">new</span>
    <span class="token punctuation">{</span>
        Data <span class="token operator">=</span> results<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span>
        <span class="token punctuation">{</span>
            p<span class="token punctuation">.</span>PostId<span class="token punctuation">,</span>
            p<span class="token punctuation">.</span>Title<span class="token punctuation">,</span>
            p<span class="token punctuation">.</span>Slug<span class="token punctuation">,</span>
            p<span class="token punctuation">.</span>PublishedAt
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        NextCursor <span class="token operator">=</span> nextCursor
    <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>If we execute the GET endpoint <code class="inline-code">/posts/pagination/?pageSize=10 after=50</code>, EF will generate the following SQL:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token keyword">TOP</span><span class="token punctuation">(</span><span class="token number">11</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CategoryId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Slug<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Title<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UpdatedAt<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span> <span class="token operator">&gt;</span> <span class="token number">50</span>
<span class="token keyword">ORDER</span> <span class="token keyword">BY</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre><p>Note that here we define the after parameter, which acts as a cursor, allowing the client to request the next records from a specific <code class="inline-code">PostId</code>.</p><p>Also note that one more item than the requested <code class="inline-code">pageSize</code> is sent <code class="inline-code">(Take(pageSize + 1))</code>, which allows the server to know if there are more pages to be loaded without relying on additional counts. This extra item is not returned to the client, but it serves to define whether the <code class="inline-code">NextCursor</code> field will be populated with the <code class="inline-code">PostId</code> of the last visible item, which facilitates sequential navigation.</p><p>Finally, we use <code class="inline-code">AsNoTracking()</code> to improve performance by preventing EF Core from tracking the returned objects, something unnecessary in read-only scenarios.</p><h2 id="-using-dynamic-filters-with-iqueryable">4.  Using Dynamic Filters with IQueryable</h2><p>A common mistake in database queries is loading all records into memory and only then applying filters. This approach is inefficient, as it consumes unnecessary resources and impacts application performance.</p><p>Instead, it&rsquo;s recommended to apply filters directly to the query. One way to do this is through the <code class="inline-code">IQueryable</code> interface, which allows the filter to be translated into SQL and executed in the database, returning only the data actually needed.</p><p>Note the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token comment">// Inefficient: loads everything from the database</span>
    <span class="token keyword">var</span> allPosts <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts<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 comment">// Filtering in memory (LINQ to Objects)</span>
    <span class="token keyword">var</span> filteredPosts <span class="token operator">=</span> allPosts<span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>published <span class="token operator">==</span> <span class="token keyword">false</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        filteredPosts <span class="token operator">=</span> filteredPosts<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>categoryId<span class="token punctuation">.</span>HasValue<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        filteredPosts <span class="token operator">=</span> filteredPosts<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>CategoryId <span class="token operator">==</span> categoryId<span class="token punctuation">.</span>Value<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>

    filteredPosts <span class="token operator">=</span> filteredPosts
        <span class="token punctuation">.</span><span class="token function">OrderByDescending</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Here we are loading all the data into memory, and then filtering it later.</p><p>An alternative would be to use <code class="inline-code">IQueryable</code>, dynamically assembling the filter and finally applying the query to the database:</p><pre class=" language-csharp"><code class="prism  language-csharp">IQueryable<span class="token operator">&lt;</span>Post<span class="token operator">&gt;</span> query <span class="token operator">=</span> db<span class="token punctuation">.</span>Posts<span class="token punctuation">;</span>

<span class="token comment">// Filter applied to the SQL query</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>published <span class="token operator">==</span> <span class="token keyword">false</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    query <span class="token operator">=</span> query<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt <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>

<span class="token keyword">if</span> <span class="token punctuation">(</span>categoryId<span class="token punctuation">.</span>HasValue<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    query <span class="token operator">=</span> query<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>CategoryId <span class="token operator">==</span> categoryId<span class="token punctuation">.</span>Value<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// Sorting applied in the SQL query</span>
query <span class="token operator">=</span> query<span class="token punctuation">.</span><span class="token function">OrderByDescending</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Execute the query in the database</span>
<span class="token keyword">var</span> posts <span class="token operator">=</span> <span class="token keyword">await</span> query<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>
</code></pre><p>Assuming <code class="inline-code">categoryId = 3</code> and <code class="inline-code">published = false</code>, the following SQL will be generated:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PostId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>AuthorId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CategoryId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Content<span class="token punctuation">]</span><span class="token punctuation">,</span> 
       <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Slug<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Title<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span> <span class="token operator">IS</span> <span class="token boolean">NULL</span> <span class="token operator">AND</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CategoryId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3</span>
<span class="token keyword">ORDER</span> <span class="token keyword">BY</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span> <span class="token keyword">DESC</span>
</code></pre><p>This way, a more efficient query is executed than the first example, as it filters the records we need directly in SQL, without the need to work with the data in memory.</p><h2 id="-do-not-transfer-unnecessary-functions-to-the-query">5.  Do Not Transfer Unnecessary Functions to the Query</h2><p>Sometimes we need to perform conversions on manipulated data. A common example is converting text to uppercase or lowercase, which in C# can be done using the <code class="inline-code">ToLower()</code> and <code class="inline-code">ToUpper()</code> methods. However, if we&rsquo;re not careful, these functions can be sent to the query and compromise performance. Note the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> user <span class="token operator">=</span> db<span class="token punctuation">.</span>Users<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>UserName<span class="token punctuation">.</span><span class="token function">ToUpper</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> userName<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>
</code></pre><p>This code will generate the following SQL query:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token punctuation">[</span>u<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UserId<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>u<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>CreatedAt<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>u<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>Email<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>u<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UserName<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Users<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>u<span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> UPPER<span class="token punctuation">(</span><span class="token punctuation">[</span>u<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>UserName<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token variable">@__ToUpper_0</span>
</code></pre><p>The problem here is that the <code class="inline-code">UPPER</code> clause was transferred to the query, and this can compromise performance depending on the scenario. After all, this function will be executed on all lines resulting from the query.</p><p>An alternative in this case is to rely solely on the collation of the <code class="inline-code">UserName</code> column. If the column uses a case-insensitive collation (such as <code class="inline-code">SQL_Latin1_General_CP1_CI_AS</code> in SQL Server), then no transformation is necessary:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> user <span class="token operator">=</span> db<span class="token punctuation">.</span>Users<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>u <span class="token operator">=</span><span class="token operator">&gt;</span> u<span class="token punctuation">.</span>UserName <span class="token operator">==</span> userName<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The generated SQL will be:</p><pre><code>SELECT [u].[UserId], [u].[CreatedAt], [u].[Email], [u].[UserName]
FROM [Users] AS [u]
WHERE [u].[UserName] = @__userName_0
</code></pre><p>Therefore, it is not necessary to use <code class="inline-code">ToUpper()</code>; SQL Server itself will perform the conversion using the <code class="inline-code">SQL_Latin1_General_CP1_CI_AS</code> collation. In this case, the name &ldquo;john&rdquo; will be the same as &ldquo;John&rdquo; and &ldquo;JOHN.&rdquo;</p><p>To check if the column has the <code class="inline-code">SQL_Latin1_General_CP1_CI_AS</code> collation, simply run the query:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> COLUMN_NAME<span class="token punctuation">,</span> COLLATION_NAME 
<span class="token keyword">FROM</span> INFORMATION_SCHEMA<span class="token punctuation">.</span><span class="token keyword">COLUMNS</span> 
<span class="token keyword">WHERE</span> TABLE_NAME <span class="token operator">=</span> <span class="token string">'Users'</span> <span class="token operator">AND</span> COLUMN_NAME <span class="token operator">=</span> <span class="token string">'UserName'</span><span class="token punctuation">;</span>
</code></pre><p>If it has the collation, it will return:</p><table><thead><tr><th data-role="resizable" style="width:50%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">COLUMN_NAME</th><th style="width:50%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">COLLATION_NAME</th></tr></thead><tbody><tr><td style="width:50%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">UserName</td><td style="width:50%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">SQL_Latin1_General_CP1_CI_AS</td></tr></tbody></table><br /><h2 id="-reduce-the-number-of-queries-with-caching">6.  Reduce the Number of Queries with Caching</h2><p>Imagine an endpoint that returns post categories. They rarely change but are accessed thousands of times a day. In this case, querying the database every time is a waste of resources. To address this, we can use caching strategies, such as using the local memory cache (<code class="inline-code">ImemoryCache</code>).</p><p>Note the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/posts/categories"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>
    <span class="token punctuation">[</span>FromServices<span class="token punctuation">]</span> IMemoryCache cache<span class="token punctuation">,</span>
    <span class="token punctuation">[</span>FromServices<span class="token punctuation">]</span> BlogDbContext 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">const</span> <span class="token keyword">string</span> cacheKey <span class="token operator">=</span> <span class="token string">"categories_list"</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>cache<span class="token punctuation">.</span><span class="token function">TryGetValue</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> <span class="token keyword">out</span> List<span class="token operator">&lt;</span>Category<span class="token operator">&gt;</span> categories<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        categories <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Categories
            <span class="token punctuation">.</span><span class="token function">OrderBy</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">ToListAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> cacheOptions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MemoryCacheEntryOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">SetSlidingExpiration</span><span class="token punctuation">(</span>TimeSpan<span class="token punctuation">.</span><span class="token function">FromMinutes</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 function">SetAbsoluteExpiration</span><span class="token punctuation">(</span>TimeSpan<span class="token punctuation">.</span><span class="token function">FromHours</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>

        cache<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> categories<span class="token punctuation">,</span> cacheOptions<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>categories<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>Instead of accessing the database with each request, the database checks whether data is present in the cache. If there is not already data, the query is executed, and then the data is temporarily saved in the cache in memory. Thus, the next requests will use the data directly from the cache.</p><p>Using a caching strategy reduces unnecessary database accesses, resulting in improved performance and enabling scalability.</p><h2 id="✨-using-optimized-raw-sql">7. ✨ Using Optimized Raw SQL</h2><p>Let&rsquo;s analyze a situation where writing a manual SQL query (raw SQL) is more advantageous than using LINQ alone.</p><p>Imagine an endpoint that displays a blog summary through a dashboard with aggregated statistics. This dashboard should display: total posts, total published posts, total comments and last published date.</p><p>Using the LINQ approach and in-memory manipulation, we could do the following:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> totalPosts <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts<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>

<span class="token keyword">var</span> publishedPosts <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts<span class="token punctuation">.</span><span class="token function">CountAsync</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt <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">var</span> totalComments <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Comments<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>

<span class="token keyword">var</span> lastPublished <span class="token operator">=</span> <span class="token keyword">await</span> db<span class="token punctuation">.</span>Posts
     <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
     <span class="token punctuation">.</span><span class="token function">MaxAsync</span><span class="token punctuation">(</span>p <span class="token operator">=</span><span class="token operator">&gt;</span> p<span class="token punctuation">.</span>PublishedAt<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>
     totalPosts<span class="token punctuation">,</span>
     publishedPosts<span class="token punctuation">,</span>
     totalComments<span class="token punctuation">,</span>
     lastPublished
 <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Which will generate the following SQL:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>

<span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span> <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>

<span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Comments<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span><span class="token number">c</span><span class="token punctuation">]</span>

<span class="token keyword">SELECT</span> <span class="token function">MAX</span><span class="token punctuation">(</span><span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token keyword">FROM</span> <span class="token punctuation">[</span>Posts<span class="token punctuation">]</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>
<span class="token keyword">WHERE</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedAt<span class="token punctuation">]</span> <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>
</code></pre><p>The problem with this approach is that four separate queries are executed, which can cause overhead in high-load environments. Furthermore, it&rsquo;s more difficult to optimize queries using only SQL indexes.</p><p>A more performant approach would be to create a single query using raw SQL:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token keyword">await</span> db
     <span class="token punctuation">.</span>DashboardStats
     <span class="token punctuation">.</span><span class="token function">FromSqlRaw</span><span class="token punctuation">(</span><span class="token string">@"
         SELECT
             (SELECT COUNT(*) FROM Posts) AS TotalPosts,
             (SELECT COUNT(*) FROM Posts WHERE PublishedAt IS NOT NULL) AS PublishedPosts,
             (SELECT COUNT(*) FROM Comments) AS TotalComments,
             (SELECT MAX(PublishedAt) FROM Posts WHERE PublishedAt IS NOT NULL) AS LastPublished
     "</span><span class="token punctuation">)</span>
     <span class="token punctuation">.</span><span class="token function">AsNoTracking</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
     <span class="token punctuation">.</span><span class="token function">FirstAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

 <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Ok</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Note that in this approach, we use raw SQL to manually add a custom query that returns all results in a single query. This way, SQL Server can evaluate all subqueries in parallel and optimize cached page reads.</p><p>The generated SQL will look like this:</p><pre class=" language-sql"><code class="prism  language-sql"><span class="token keyword">SELECT</span> <span class="token keyword">TOP</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>t<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>LastPublished<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>t<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>PublishedPosts<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>t<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>TotalComments<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>t<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token punctuation">[</span>TotalPosts<span class="token punctuation">]</span>
<span class="token keyword">FROM</span> <span class="token punctuation">(</span>
    <span class="token keyword">SELECT</span>
    <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">FROM</span> Posts<span class="token punctuation">)</span> <span class="token keyword">AS</span> TotalPosts<span class="token punctuation">,</span>
    <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">FROM</span> Posts <span class="token keyword">WHERE</span> PublishedAt <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> PublishedPosts<span class="token punctuation">,</span>
    <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">FROM</span> Comments<span class="token punctuation">)</span> <span class="token keyword">AS</span> TotalComments<span class="token punctuation">,</span>
    <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">MAX</span><span class="token punctuation">(</span>PublishedAt<span class="token punctuation">)</span> <span class="token keyword">FROM</span> Posts <span class="token keyword">WHERE</span> PublishedAt <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> LastPublished
<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">[</span>t<span class="token punctuation">]</span>
</code></pre><h2 id="-conclusion"> Conclusion</h2><p>A prerequisite for creating a good API is enabling good performance. To achieve this, it&rsquo;s essential to adopt practices that optimize data access and querying. Throughout this post, we&rsquo;ve explored seven tips on how to make the most of ASP.NET Core&rsquo;s features and the database, reducing bottlenecks and improving the efficiency of backend applications.</p><p>I hope these tips help you develop even faster and more scalable APIs capable of efficiently handling large volumes of data. See you next 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">Supercharging API Development with Fast Endpoints</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Fast endpoints allow you to create web APIs with automatic validation, high performance and less code. In this post, we will understand how to use fast endpoints as an alternative to classic approaches and combine the best of both worlds when creating new web <a target="_blank" href="https://www.telerik.com/blogs/supercharging-api-development-fast-endpoints">APIs in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17174425.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:1d6fcd85-2847-463a-afea-4a7f7ae5732a</id>
    <title type="text">.NET 10 Release Candidate 1 Builds on Early Promise Seen in Preview Releases</title>
    <summary type="text">The first release candidate for .NET 10 delivers few surprises, improves persistent component state and delivers more useful metrics.</summary>
    <published>2025-09-17T12:51:14Z</published>
    <updated>2026-04-11T21:14:59Z</updated>
    <author>
      <name>Jon Hilton </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17161938/net-10-release-candidate-1-builds-early-promise"/>
    <content type="text"><![CDATA[<p><span class="featured">The first release candidate for .NET 10 delivers few surprises, improves persistent component state and delivers more useful metrics.</span></p><p>It&rsquo;s September, which can only mean one thing: It&rsquo;s .NET Release Candidate time!</p><p>.NET 10 RC1 has landed, and with it come a few interesting last-minute improvements.</p><h2 id="persistentstate">PersistentState</h2><p>New in .NET 10 is the <code class="inline-code">PersistentState</code> attribute. This is a renamed version of the attribute that shipped in earlier .NET 10 preview releases.&nbsp;Goodbye <code class="inline-code">SupplyParameterFromPersistentComponentState</code>. We barely knew you.</p><p>But it isn&rsquo;t just a name change. The attribute has some subtle new behavior, especially relevant if you&rsquo;re using enhanced navigation.</p><p>First, a quick recap.</p><p>If you apply the <code class="inline-code">PersistentState</code> attribute to data in your component, two things will happen. </p><ol><li>The state will be persisted during the first render then reused for subsequent renders. This means, if you&rsquo;re prerendering your components, the data fetched during prerendering is then reused when the component renders again (using one of Blazor&rsquo;s interactive render modes).</li><li>If you&rsquo;re using Interactive Server render mode, any data marked with this attribute will be persisted when the underlying circuit is evicted. So your users might lose their connection, reconnect to a new circuit, but keep their persisted state.</li></ol><h3 id="now-plays-nicely-with-enhanced-navigation">Now Plays Nicely with Enhanced Navigation</h3><p>The big change in RC1 is how this all works when you&rsquo;re using enhanced navigation. Enhanced navigation is a useful way to provide a &ldquo;SPA-like&rdquo; feel for your Blazor apps.</p><p>With enhanced navigation, when a user follows a link which routes to a Razor component in your app, Blazor will intercept the navigation, issue a <code class="inline-code">fetch</code> request, retrieve the new page content then patch it into the existing DOM.</p><p>This makes for a smoother experience, and avoids reloading the entire page when only a small part of it has actually changed.&nbsp;You&rsquo;ll encounter this if your app is set up for <strong>per page/component interactivity</strong>.</p><p>For example, take this slightly modified version of the famous &ldquo;Weather Data&rdquo; Blazor page.</p><pre class=" language-html"><code class="prism  language-html">@page "/weather"
@rendermode InteractiveServer

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Weather<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 punctuation">&gt;</span></span>Weather<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 comment">&lt;!-- markup to show the weather here --&gt;</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    
    <span class="token keyword">public</span> WeatherForecast<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">?</span> Forecasts <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">protected</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">OnInitializedAsync</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>Forecasts <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token comment">// Simulate asynchronous loading to demonstrate streaming rendering</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">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> startDate <span class="token operator">=</span> DateOnly<span class="token punctuation">.</span><span class="token function">FromDateTime</span><span class="token punctuation">(</span>DateTime<span class="token punctuation">.</span>Now<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> summaries <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 string">"Freezing"</span><span class="token punctuation">,</span> <span class="token string">"Bracing"</span><span class="token punctuation">,</span> <span class="token string">"Chilly"</span><span class="token punctuation">,</span> <span class="token string">"Cool"</span><span class="token punctuation">,</span> <span class="token string">"Mild"</span><span class="token punctuation">,</span> <span class="token string">"Warm"</span><span class="token punctuation">,</span> <span class="token string">"Balmy"</span><span class="token punctuation">,</span> <span class="token string">"Hot"</span><span class="token punctuation">,</span> <span class="token string">"Sweltering"</span><span class="token punctuation">,</span> <span class="token string">"Scorching"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
            Forecasts <span class="token operator">=</span> Enumerable<span class="token punctuation">.</span><span class="token function">Range</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>index <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">new</span> <span class="token class-name">WeatherForecast</span>
            <span class="token punctuation">{</span>
                Date <span class="token operator">=</span> startDate<span class="token punctuation">.</span><span class="token function">AddDays</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">,</span>
                TemperatureC <span class="token operator">=</span> Random<span class="token punctuation">.</span>Shared<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">55</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                Summary <span class="token operator">=</span> summaries<span class="token punctuation">[</span>Random<span class="token punctuation">.</span>Shared<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>summaries<span class="token punctuation">.</span>Length<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">ToArray</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>When a user navigates to this page within your app (meaning they started on a different page then followed a link to this one), they will likely end up using enhanced navigation.</p><p>But it won&rsquo;t look great because they&rsquo;ll see one set of weather data, then a &ldquo;flash,&rdquo; followed by different weather data.</p><p>This is thanks to prerendering.</p><p>When the new page is requested, Blazor renders your component once on the server, returns that initial HTML to the browser, then spins up interactive server mode and renders the component again.</p><p>The result is a visible flash when the initial data is shown, then the second set of data is shown when the component starts running interactively.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/prerendering.png?sfvrsn=d9818c10_2" title="Blazor Server including prerendering request" alt="Architecture diagram showing browser-based weather application rendering flow. Two browser windows are connected by an arrow, representing navigation between pages. Below each browser are two rendering approaches: On the left, a &#39;Prerender&#39; box contains a blue &#39;Weather.razor&#39; component that receives weather data from a database via server request. On the right, an &#39;Interactive Render&#39; box contains the same blue &#39;Weather.razor&#39; component that also receives weather data from a database, but uses DOM diffing for client-side updates. Both approaches connect to identical cylindrical database icons representing weather data storage." /></p><p>As you can see here, there is one call to fetch the weather data during prerendering, and a second during the interactive render.</p><p>Now this is precisely what <code class="inline-code">PersistentState</code> is designed to fix.</p><p>We can decorate the <code class="inline-code">Forecasts</code> property with the <code class="inline-code">PersistentState</code> attribute.&nbsp;In theory, this means the initial data will be persisted (during prerendering) and then reused during the second (interactive) render.</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    
    <span class="token punctuation">[</span>PersistentState<span class="token punctuation">]</span>
    <span class="token keyword">public</span> WeatherForecast<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">?</span> Forecasts <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 comment">// rest of code</span>
    
<span class="token punctuation">}</span>
</code></pre><p>But it turns out this doesn&rsquo;t work with enhanced navigation.&nbsp;When users navigate internally (within your app), enhanced navigation will be used and they&rsquo;ll still see that &ldquo;flash&rdquo; as the data is fetched, then fetched again.</p><p>So what gives?</p><p>Well, the new default in .NET 10 is that persisted component state won&rsquo;t be applied for interactive components during enhanced navigation.</p><p>This is to avoid any dramas if the user ends up navigating to the same page they&rsquo;re already on and has data they want to keep, for example in a form. In that case, they probably don&rsquo;t want their form values to be overridden by persisted component state.</p><p>But there is a way to bypass this and have persistent state work as we might expect (for data like this, which the user isn&rsquo;t changing).</p><p>We can set <code class="inline-code">AllowUpdates = true</code> on the <code class="inline-code">[PersistentState]</code> attribute.</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    
    <span class="token punctuation">[</span><span class="token function">PersistentState</span><span class="token punctuation">(</span>AllowUpdates <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> WeatherForecast<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">?</span> Forecasts <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 comment">// rest of code</span>
    
<span class="token punctuation">}</span>
</code></pre><p>This indicates to Blazor that we&rsquo;re happy for the persisted state to be used during enhanced navigation.</p><p>When a user navigates to this weather page, we want the data that&rsquo;s fetched during prerendering to be applied when the component renders a second time (rendered interactively).</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/prerendering-persistent-state.png?sfvrsn=efae3dd2_2" title="Blazor Server including prerendering request and persisted state" alt="Architecture diagram showing browser-based weather application with two rendering approaches. Two browser windows are connected by an arrow representing page navigation. The left side shows a &#39;Prerender&#39; approach where the browser makes a weather request and receives HTML with persisted data. A blue &#39;Weather.razor&#39; component receives weather data from a cylindrical database icon below. The right side shows an &#39;Interactive Render&#39; approach where the browser performs rendering with persisted state and uses DOM diffing for updates. It contains the same blue &#39;Weather.razor&#39; component but operates client-side. Both approaches connect to the same weather data database at the bottom of the diagram." /></p><p>With that, the &ldquo;flash&rdquo; disappears and we get just one version of the data.</p><p>Note that persisted data is usually serialized (using JSON) and included in the rendered HTML for the initial prerender, then passed back to the component when it spins up in one of the interactive render modes.</p><h3 id="more-control-over-persisted-state">More Control over Persisted State</h3><p>In addition to enabling persisted state during enhanced navigation, we also get more fine-grained control over how persisted state is applied in general.</p><p>Remember how Blazor will now (as of .NET 10) attempt to restore persisted state when reconnecting to a new circuit on the server?</p><p>Should you decide you don&rsquo;t want this to happen, you can switch this behavior off using the new <code class="inline-code">RestoreBehavior</code> parameter.</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">PersistentState</span><span class="token punctuation">(</span>RestoreBehavior <span class="token operator">=</span> RestoreBehavior<span class="token punctuation">.</span>SkipLastSnapshot<span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> WeatherForecast<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">?</span> Forecasts <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>By default, when a user loses their connection and Blazor connects them to a new circuit, Blazor will take the weather data they had, persist it when the circuit is evicted and restore it to the new circuit.</p><p>From the user&rsquo;s perspective, they won&rsquo;t notice any change. The weather data that they had before being disconnected will be the same weather data they see on the page after they&rsquo;re reconnected.</p><p>But when you set <code class="inline-code">RestoreBehavior</code> to <code class="inline-code">SkipLastSnapshot</code>, the persisted state <strong>won&rsquo;t be restored during reconnection</strong>.</p><p>Instead, new weather data will be retrieved and the user will see the data change in the UI when their browser reconnects to the server.</p><p>Alternatively, you may be happy for the persisted state to be used during reconnection, but don&rsquo;t want it used between prerendering and interactive rendering. In that case you can set <code class="inline-code">RestoreBehavior</code> to <code class="inline-code">SkipInitialValue</code>.</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">PersistentState</span><span class="token punctuation">(</span>RestoreBehavior <span class="token operator">=</span> RestoreBehavior<span class="token punctuation">.</span>SkipInitialValue<span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> WeatherForecast<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">?</span> Forecasts <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>In this scenario, let&rsquo;s say a user hits this weather page directly when visiting your app. With <code class="inline-code">SkipInitialValue</code>, Blazor won&rsquo;t attempt to share the <code class="inline-code">Forecasts</code> data between prerendering and the subsequent interactive rendering.</p><p>Which means that, for that first load, we&rsquo;re back to the flash as the data is loaded once, then loaded again during the second render.</p><p>But, the <strong>persisted data will be used when the user loses their connection and reconnects</strong>.</p><p>Clear? Me neither!</p><p>Let&rsquo;s try putting all this into a table to see if it clears things up.</p><p>Here are the various combinations of options and results (how many times the weather data is fetched) for a number of different scenarios.</p><table><thead><tr><th style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">RestoreBehavior</th><th style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">AllowUpdates</th><th data-role="resizable" style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Result &ndash; direct page load</th><th style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Result &ndash; enhanced navigation</th><th style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Result &ndash; reconnection</th></tr></thead><tbody><tr><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">SkipInitialValue</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">TRUE</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched twice</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched once</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Existing data restored</td></tr><tr><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">SkipInitialValue</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">FALSE</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched twice</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched twice</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Existing data restored</td></tr><tr><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">SkipLastSnapshot</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">TRUE</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched once</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched once</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data refetched</td></tr><tr><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">SkipLastSnapshot</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">FALSE</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched once</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data fetched twice</td><td style="width:20%;border-width:1px;border-style:dotted;border-color:#bdbdba;padding:5px;margin:5px;">Data refetched</td></tr></tbody></table><br /><p>The three scenarios shown are:</p><ul><li><strong>Direct page load</strong> &ndash; When a user directly requests our weather page (for example by entering the URL directly in the browser)</li><li><strong>Enhanced navigation</strong> &ndash; When the user arrives at the weather page via internal navigation (within the app, following a link from another page)</li><li><strong>Reconnection</strong> &ndash; When the user loses their circuit and Blazor reconnects them to a new one</li></ul><p>The takeaway here is that you can now precisely control how persisted state is restored. (It just takes a little bit of work to figure out which combination you need for your specific situation!)</p><h2 id="validation-improvements">Validation Improvements</h2><p>Onto simpler changes&hellip;</p><p>You can now apply validation attributes to classes and records (not just properties). This opens the door to validating complex objects with custom logic.</p><p>Take this (somewhat contrived) example.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>NoBot<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserRegistration</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Username is required"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">StringLength</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">,</span> MinimumLength <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> ErrorMessage <span class="token operator">=</span> <span class="token string">"Username must be between 3 and 50 characters"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Username <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Email is required"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">EmailAddress</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Please enter a valid email address"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Honeypot <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>See the <code class="inline-code">NoBot</code> attribute?</p><p>That points to a custom validation attribute, which can work with the entire object (not just individual fields).</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">NoBotAttribute</span> <span class="token punctuation">:</span> ValidationAttribute
<span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword">override</span> ValidationResult<span class="token operator">?</span> <span class="token function">IsValid</span><span class="token punctuation">(</span><span class="token keyword">object</span><span class="token operator">?</span> <span class="token keyword">value</span><span class="token punctuation">,</span> ValidationContext _<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">int</span> botWarningSigns <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">value</span> <span class="token keyword">is</span> not UserRegistration registration<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ValidationResult</span><span class="token punctuation">(</span><span class="token string">"Invalid registration data."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">// Honeypot field: bots often fill hidden fields</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrEmpty</span><span class="token punctuation">(</span>registration<span class="token punctuation">.</span>Honeypot<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            botWarningSigns <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>          

        <span class="token comment">// Username and email should not be identical</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>registration<span class="token punctuation">.</span>Username<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>
            <span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>registration<span class="token punctuation">.</span>Email<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>
            registration<span class="token punctuation">.</span>Username<span class="token punctuation">.</span><span class="token function">Equals</span><span class="token punctuation">(</span>registration<span class="token punctuation">.</span>Email<span class="token punctuation">,</span> StringComparison<span class="token punctuation">.</span>OrdinalIgnoreCase<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            botWarningSigns <span class="token operator">+</span><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">if</span><span class="token punctuation">(</span>botWarningSigns <span class="token operator">&gt;=</span> <span class="token number">2</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">ValidationResult</span><span class="token punctuation">(</span><span class="token string">"Bot-like behavior detected!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> ValidationResult<span class="token punctuation">.</span>Success<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>So as well as the standard validation attributes on the class&rsquo;s properties, we&rsquo;ve also written some custom logic to assign a &ldquo;score&rdquo; based on warning signs that the form may have been filled in by a bot, then fail validation if that score exceeds a certain number.</p><p>It&rsquo;s also now possible to indicate that a property should skip validation.</p><p>For example, if you have an <code class="inline-code">Address</code> class that is reused in a couple of places and you don&rsquo;t want it to be validated in one of those cases. You can apply the <code class="inline-code">[SkipValidation]</code> attribute to a specific property or parameter, or to a type.</p><h2 id="new-asp.net-core-identity-metrics">New ASP.NET Core Identity Metrics</h2><p>If you&rsquo;re using ASP.NET Core Identity, you can now tap into some handy built-in metrics for key user and sign-in operations.</p><p>Notably the following (in <code class="inline-code">Microsoft.AspNetCore.Identity</code>):</p><ul><li><code class="inline-code">aspnetcore.identity.user.create.duration</code></li><li><code class="inline-code">aspnetcore.identity.user.update.duration</code></li><li><code class="inline-code">aspnetcore.identity.user.delete.duration</code></li><li><code class="inline-code">aspnetcore.identity.user.check_password_attempts</code></li><li><code class="inline-code">aspnetcore.identity.user.generated_tokens</code></li><li><code class="inline-code">aspnetcore.identity.user.verify_token_attempts</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.authenticate.duration</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.check_password_attempts</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.sign_ins</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.sign_outs</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.two_factor_clients_remembered</code></li><li><code class="inline-code">aspnetcore.identity.sign_in.two_factor_clients_forgotten</code></li></ul><h2 id="all-the-other-changes-landed-in-earlier-releases">All the Other Changes Landed in Earlier Releases</h2><p>.NET 10 is on track to release in November 2025. If this is the first time you&rsquo;ve looked at .NET 10, there are lots more changes not covered here, which were already included in earlier preview releases and are now baked into RC1.</p><p>Here are some of the highlights:</p><ul><li>The Blazor script is now served as a static asset (complete with precompression, preloading and fingerprinting)</li><li><code class="inline-code">NavigateTo</code> preserves scroll position</li><li>The web app template now includes the markup, CSS and JS for the Blazor Server reconnection UI component</li><li>New Blazor metrics and traces for monitoring the performance of your Blazor apps</li><li>Improved component not found handling (404s)</li><li>Improved JavaScript Interop</li><li>Persistent component state can be stored in memory or hybrid cache</li><li>New APIs for pausing and resuming circuits</li><li>Validation for nested objects</li><li>Passkey support</li></ul><p>Based on this release, it seems Blazor in .NET 10 is firmly on track to be the most stable and performant release of Blazor so far.</p><p>Long-standing challenges (most notably around how Blazor Server disconnects are handled) have been addressed, performance improved and observability enhanced (both in development and production using the new metrics).</p><p>RC1 is the first of the &ldquo;go-live&rdquo; releases, and at this point it seems unlikely any major changes will land between now and the RTM release in November.</p><hr /><p>See more details about .NET 10 in our previous updates:</p><ul><li><a href="https://www.telerik.com/blogs/net-10-preview-release-6-tackles-blazor-server-lost-state-problem" target="_blank">.NET 10 Preview Release 6 Tackles Blazor Server&rsquo;s Lost State Problem</a></li><li><a href="https://www.telerik.com/blogs/net-10-previews-4-5-improve-blazor-performance-monitoring-js-interop" target="_blank" style="font-family:inherit;font-size:inherit;text-align:inherit;text-transform:inherit;word-spacing:normal;white-space:inherit;">.NET 10 Previews 4 and 5 Improve Blazor Performance Monitoring and JS Interop</a></li><li><a href="https://www.telerik.com/blogs/notable-blazor-improvements-land-early-dotnet-10-preview-releases" target="_blank" style="font-family:inherit;font-size:inherit;text-align:inherit;text-transform:inherit;word-spacing:normal;white-space:inherit;">Notable Blazor Improvements Land in the Early .NET 10 Preview Releases</a></li></ul><img src="https://feeds.telerik.com/link/23052/17161938.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:2b1f50d6-dcd7-42ac-bfe2-e0c9207d6239</id>
    <title type="text">.NET Aspire 5: Orchestration and Service Discovery</title>
    <summary type="text">In Part 5, we introduce a brand-new Orders API, teach it to talk to Inventory and see how the Aspire orchestrator handles all the messy wiring for us.</summary>
    <published>2025-09-16T15:04:02Z</published>
    <updated>2026-04-11T21:14:59Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23052/17161239/net-aspire-5-orchestration-service-discovery"/>
    <content type="text"><![CDATA[<p><span class="featured">In Part 5, we introduce a brand-new Orders API, teach it to talk to Inventory and see how the Aspire orchestrator handles all the messy wiring for us.</span></p><p><em>This is Part 5 of our six‑part deep dive into .NET Aspire.</em></p><ul><li>Part 1: <a target="_blank" href="https://www.telerik.com/blogs/net-aspire-1-what-is-net-aspire">What is .NET Aspire?</a></li><li>Part 2: <a target="_blank" href="https://www.telerik.com/blogs/net-aspire-2-developer-dashboard">Exploring the Developer Dashboard</a></li><li>Part 3: <a target="_blank" href="https://www.telerik.com/blogs/net-aspire-3-service-defaults">Service Defaults</a></li><li>Part 4: <a target="_blank" href="https://www.telerik.com/blogs/net-aspire-4-integrations">Integrations</a></li><li><strong>This post (Part 5)</strong>: Orchestration &amp; Service Discovery</li><li>Part 6: <a href="https://www.telerik.com/blogs/net-aspire-6-deployment-using-azure-container-apps" target="_blank">Deployment Using Azure Container Apps</a></li></ul><p>Welcome back, friends. We&rsquo;re really cooking with gas now.</p><p>Up to this point, we&rsquo;ve:</p><ul><li>Said &ldquo;hello, world&rdquo; to .NET Aspire and learned why an application model beats a folder full of ad-hoc Docker Compose files</li><li>Poked around the Developer Dashboard to watch our app light up in real-time</li><li>Saw how Service Defaults added opinionated capabilities like OpenTelemetry, health probes and resilience across every service with one line of code</li><li>Plugged SQL Server and Redis into the mix</li></ul><p>We&rsquo;ve had a lot of fun so far, but up to this point we&rsquo;ve been hosting a party of one: an Inventory API that lists guitars for sale. Today, we&rsquo;ll introduce a brand-new Orders API, teach it how to talk to Inventory, and see how the Aspire orchestrator handles all the messy wiring for us.</p><p><strong>Note</strong>: This series focuses on .NET Aspire and not writing APIs, so I&rsquo;ll be assuming you have general working knowledge of how to write C# APIs. I will explain new code where relevant.</p><h2 id="why-orchestration-matters">Why Orchestration Matters</h2><p>Why does orchestration matter? Docker Compose and Kubernetes absolutely help with scheduling containers, but they live outside of your codebase. Your CI pipeline has to juggle a bunch of YAML files, keep ports in sync and pray nobody commits a hard‑coded endpoint by mistake.</p><p>With Aspire, orchestration moves back into C#. You declare what exists and how pieces relate.</p><ul><li><strong>Lifecycle</strong>: Databases come online before the APIs that depend on them.</li><li><strong>Configuration</strong>: Connection strings, secrets and URLs flow in through environment variables.</li><li><strong>Resilience and telemetry</strong>: Opt in once with <code class="inline-code">AddServiceDefaults()</code> and every outbound call is wrapped with retries, time‑outs and OpenTelemetry spans.</li></ul><p>In short, with Aspire orchestration is the central hub that knows the dependency graph, watches health probes and keeps secrets safe. No more depending on a dated <code class="inline-code">README.md</code> and launching nine terminals.</p><h2 id="revisiting-service-defaults">Revisiting Service Defaults</h2><p>Before we start wiring up new services, let&rsquo;s revisit service defaults. In our previous post, we saw how a single call to <code class="inline-code">builder.AddServiceDefaults()</code> adds OpenTelemetry instrumentation, health probes, service discovery and a resilience pipeline. Those defaults apply to every ASP.NET Core project that opts in by calling <code class="inline-code">builder.AddServiceDefaults()</code>.</p><p>This allows service defaults to do the heavy lifting for orchestration. When the AppHost injects environment variables like <code class="inline-code">ConnectionStrings__guitardb</code>, our services automatically pick them up through configuration binding. Telemetry flows to the dashboard without any extra code. And when we call another service via <code class="inline-code">HttpClient</code>, the standard resilience handler adds retries, timeouts and circuit breakers.</p><p>We won&rsquo;t rehash all of the implementation details here&mdash;see <a target="_blank" href="https://www.telerik.com/blogs/net-aspire-3-service-defaults">Part 3</a> for in-depth details. However, keep in mind that everything we build in this post rests on these conventions.</p><h2 id="meet-the-orders-api">Meet the Orders API</h2><p>Our guitar shop currently consists of a Blazor frontend and a single Inventory (<code class="inline-code">/guitars</code>) API that handles basic create-read-update-delete (CRUD) operations. We&rsquo;ll now introduce an Orders API.</p><p>The Orders API will:</p><ol><li>Accept an order from our Blazor app</li><li>For each line item, ask the Inventory API whether the product exists and has stock</li><li>If validation passes, calculate tax, persist the order and reply <code class="inline-code">201 Created</code> with an order summary</li></ol><p><strong>Note</strong>: For clarity and conciseness, the following code is under a single endpoint. For a &ldquo;real-world&rdquo; production app, much of this logic will live in other parts of your application.</p><h3 id="build-the-endpoint">Build the Endpoint</h3><p>Let&rsquo;s create the Orders API. Let&rsquo;s take a look at our <code class="inline-code">POST</code> endpoint. I&rsquo;ll walk you through it next.</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">"/orders"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>CreateOrderRequest req<span class="token punctuation">,</span>
                              OrdersDbContext db<span class="token punctuation">,</span>
                              InventoryClient inventory<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 operator">!</span>req<span class="token punctuation">.</span>Lines<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> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token string">"At least one line is required."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> validatedLines <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>OrderLine<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">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> line <span class="token keyword">in</span> req<span class="token punctuation">.</span>Lines<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> product <span class="token operator">=</span> <span class="token keyword">await</span> inventory<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span>line<span class="token punctuation">.</span>ProductId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>product <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">BadRequest</span><span class="token punctuation">(</span>$<span class="token string">"Product {line.ProductId} not found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>product<span class="token punctuation">.</span>Stock <span class="token operator">&lt;</span> line<span class="token punctuation">.</span>Quantity<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">"Insufficient stock for {product.Sku}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        validatedLines<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">OrderLine</span>
        <span class="token punctuation">{</span>
            ProductId  <span class="token operator">=</span> line<span class="token punctuation">.</span>ProductId<span class="token punctuation">,</span>
            Quantity   <span class="token operator">=</span> line<span class="token punctuation">.</span>Quantity<span class="token punctuation">,</span>
            UnitPrice  <span class="token operator">=</span> product<span class="token punctuation">.</span>Price
        <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> subtotal <span class="token operator">=</span> validatedLines<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span>l <span class="token operator">=</span><span class="token operator">&gt;</span> l<span class="token punctuation">.</span>LineTotal<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> tax <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">Round</span><span class="token punctuation">(</span>subtotal <span class="token operator">*</span> <span class="token number">0</span><span class="token punctuation">.</span>05m<span class="token punctuation">,</span> <span class="token number">2</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>
        CustomerName <span class="token operator">=</span> req<span class="token punctuation">.</span>CustomerName<span class="token punctuation">,</span>
        Subtotal     <span class="token operator">=</span> subtotal<span class="token punctuation">,</span>
        Tax          <span class="token operator">=</span> tax<span class="token punctuation">,</span>
        Lines        <span class="token operator">=</span> validatedLines
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

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

    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/orders/{order.Id}"</span><span class="token punctuation">,</span>
        <span class="token keyword">new</span> <span class="token punctuation">{</span> order<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> order<span class="token punctuation">.</span>OrderNumber<span class="token punctuation">,</span> order<span class="token punctuation">.</span>Subtotal<span class="token punctuation">,</span> order<span class="token punctuation">.</span>Tax<span class="token punctuation">,</span> order<span class="token punctuation">.</span>Total <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>What just happened?</p><ol><li>We use a guard clause to reject an empty order.</li><li>We use cross-service validation to check inventory, so we don&rsquo;t just assume a product is in stock.</li><li>We calculate tax.</li><li>We use Entity Framework Core to write the order to a <code class="inline-code">OrdersDb</code> database.</li><li>And we respond with a <code class="inline-code">201 Created</code> with the new resource.</li></ol><h3 id="how-does-orders-talk-to-inventory">How Does Orders Talk to Inventory?</h3><p>In our new API&rsquo;s <code class="inline-code">Program.cs</code>, we register an HTTP client:</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 generic-method function">AddHttpClient<span class="token punctuation">&lt;</span>InventoryClient<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>client <span class="token operator">=</span><span class="token operator">&gt;</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">"https+http://inventory"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The URI looks a little funky: notice the scheme (<code class="inline-code">https+http</code>) and the host (<code class="inline-code">inventory</code>).</p><ul><li><code class="inline-code">https+http</code> tells Aspire&rsquo;s resolver to try HTTPS first, then fall back to HTTP.</li><li>As you likely noticed, <code class="inline-code">inventory</code> isn&rsquo;t a DNS name. It&rsquo;s the canonical service name we&rsquo;ll define in the <code class="inline-code">AppHost</code>.</li></ul><p>If you remember, we defined it earlier:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> inventoryApi <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token generic-method function">AddProject<span class="token punctuation">&lt;</span>Projects<span class="token punctuation">.</span>Api<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"inventory"</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>inventoryDb<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>cache<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WaitFor</span><span class="token punctuation">(</span>inventoryDb<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>With this in place, Aspire injects the actual URL via configuration. As a result, we don&rsquo;t require port numbers, a localhost or environment‑specific configuration. At runtime, Aspire injects two environment variables:</p><pre><code>Services__inventory__https = https://localhost:6001
Services__inventory__http  = http://localhost:5001
</code></pre><p>The resolver swaps the placeholder URI for the real endpoint. Just like that, service discovery handles it all.</p><h2 id="upgrade-the-blazor-frontend">Upgrade the Blazor Frontend</h2><p>Our new service isn&rsquo;t helpful if our customers can&rsquo;t see it. Let&rsquo;s now walk through the three components that showcase the Orders API.</p><p>Each component uses typed <code class="inline-code">HttpClient</code> services so they inherit telemetry, resilience and service discovery out of the box.</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 generic-method function">AddHttpClient<span class="token punctuation">&lt;</span>OrdersHttpClient<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>client <span class="token operator">=</span><span class="token operator">&gt;</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">"https+http://orders"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">AddServiceDiscovery</span><span class="token punctuation">(</span><span class="token punctuation">)</span>           
        <span class="token punctuation">.</span><span class="token function">AddStandardResilienceHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="order-list-page">Order List Page</h3><p>For an order list, we&rsquo;re using a paginated grid that lets you click a row for details. It also supports inline deletes without a page refresh.</p><pre class=" language-csharp"><code class="prism  language-csharp">@page <span class="token string">"/orders"</span>
@inject OrdersHttpClient Http
@inject NavigationManager Nav
@inject IJSRuntime JS

<span class="token operator">&lt;</span>PageTitle<span class="token operator">&gt;</span>Orders<span class="token operator">&lt;</span><span class="token operator">/</span>PageTitle<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"container py-4"</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"d-flex justify-content-between align-items-center mb-3"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>h1 <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"display-6 d-flex gap-2 mb-0"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>span<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span> Orders
        <span class="token operator">&lt;</span><span class="token operator">/</span>h1<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"btn btn-primary"</span> @onclick<span class="token operator">=</span><span class="token string">"CreateNewOrder"</span><span class="token operator">&gt;</span>
            ➕ New <span class="token class-name">Order</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>

    @<span class="token keyword">if</span> <span class="token punctuation">(</span>_orders <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token operator">&lt;</span>p<span class="token operator">&gt;</span>Loading&hellip;<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">else</span>
    <span class="token punctuation">{</span>
        <span class="token operator">&lt;</span>table <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"table table-striped"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>thead <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"table-light small text-uppercase"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>#<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Date<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Customer Name<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Total<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Delete<span class="token operator">?</span><span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>thead<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tbody<span class="token operator">&gt;</span>
                @<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> o <span class="token keyword">in</span> _orders<span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token operator">&lt;</span>tr style<span class="token operator">=</span><span class="token string">"cursor:pointer"</span>
                        @onclick<span class="token operator">=</span><span class="token string">"@(() =&gt; Nav.NavigateTo($"</span><span class="token operator">/</span>orders<span class="token operator">/</span><span class="token punctuation">{</span>o<span class="token punctuation">.</span>Id<span class="token punctuation">}</span><span class="token string">"))"</span><span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@o<span class="token punctuation">.</span>OrderNumber<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@o<span class="token punctuation">.</span>CreatedUtc<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"yyyy-MM-dd"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@o<span class="token punctuation">.</span>CustomerName<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@o<span class="token punctuation">.</span>Total<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"btn btn-sm btn-link text-danger"</span>
                                    title<span class="token operator">=</span><span class="token string">"Delete"</span>
                                    @onclick<span class="token punctuation">:</span>stopPropagation
                                    @onclick<span class="token operator">=</span><span class="token string">"() =&gt; DeleteOrder(o.Id)"</span><span class="token operator">&gt;</span>
                                
                            <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
                <span class="token punctuation">}</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>tbody<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>table<span class="token operator">&gt;</span>
    <span class="token punctuation">}</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>

@code <span class="token punctuation">{</span>
    <span class="token keyword">private</span> List<span class="token operator">&lt;</span>OrderSummaryDto<span class="token operator">&gt;</span><span class="token operator">?</span> _orders<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">OnInitializedAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token operator">=</span><span class="token operator">&gt;</span> _orders <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">await</span> Http<span class="token punctuation">.</span><span class="token function">GetOrdersAsync</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">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">async</span> Task <span class="token function">DeleteOrder</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">bool</span> ok <span class="token operator">=</span> <span class="token keyword">await</span> JS<span class="token punctuation">.</span><span class="token generic-method function">InvokeAsync<span class="token punctuation">&lt;</span><span class="token keyword">bool</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"confirm"</span><span class="token punctuation">,</span> <span class="token string">"Delete this order?"</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>ok<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> resp <span class="token operator">=</span> <span class="token keyword">await</span> Http<span class="token punctuation">.</span><span class="token function">DeleteOrderAsync</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>resp<span class="token punctuation">.</span>IsSuccessStatusCode<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> row <span class="token operator">=</span> _orders<span class="token punctuation">.</span><span class="token function">FirstOrDefault</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> id<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>row <span class="token keyword">is</span> not <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                _orders<span class="token punctuation">.</span><span class="token function">Remove</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span><span class="token punctuation">;</span>    
                <span class="token function">StateHasChanged</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">else</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> JS<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"alert"</span><span class="token punctuation">,</span> $<span class="token string">"Delete failed &ndash; {resp.StatusCode}"</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">CreateNewOrder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> Nav<span class="token punctuation">.</span><span class="token function">NavigateTo</span><span class="token punctuation">(</span><span class="token string">"/create-order"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Here&rsquo;s the finished Order List page.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/orderlist.png?sfvrsn=e1a8dc02_2" alt="orderlist" /></p><h3 id="order-details-page">Order Details Page</h3><p>With the Order List page set, we can build an Order Details page. This page displays the full invoice with the line-item pricing pulled directly from the server.</p><pre class=" language-csharp"><code class="prism  language-csharp">@page <span class="token string">"/orders/{Id:guid}"</span>
@inject OrdersHttpClient Http

<span class="token operator">&lt;</span>h1 <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"mb-3"</span><span class="token operator">&gt;</span>Order @Id<span class="token operator">&lt;</span><span class="token operator">/</span>h1<span class="token operator">&gt;</span>

@<span class="token keyword">if</span> <span class="token punctuation">(</span>_order <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>p<span class="token operator">&gt;</span>Loading&hellip;<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span>
<span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>p<span class="token operator">&gt;</span><span class="token operator">&lt;</span>b<span class="token operator">&gt;</span>Customer<span class="token punctuation">:</span><span class="token operator">&lt;</span><span class="token operator">/</span>b<span class="token operator">&gt;</span> @_order<span class="token punctuation">.</span>CustomerName<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>p<span class="token operator">&gt;</span><span class="token operator">&lt;</span>b<span class="token operator">&gt;</span>Date<span class="token punctuation">:</span><span class="token operator">&lt;</span><span class="token operator">/</span>b<span class="token operator">&gt;</span> @_order<span class="token punctuation">.</span>CreatedUtc<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"u"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>

    <span class="token operator">&lt;</span>table <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"table"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>thead<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Product<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>th <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Qty<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>th <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Line Total<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>thead<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>tbody<span class="token operator">&gt;</span>
            @<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> l <span class="token keyword">in</span> _order<span class="token operator">!</span><span class="token punctuation">.</span>Lines<span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@l<span class="token punctuation">.</span>ProductName<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@l<span class="token punctuation">.</span>Quantity<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@l<span class="token punctuation">.</span>LineTotal<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
            <span class="token punctuation">}</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>tbody<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>tfoot<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span><span class="token operator">&lt;</span>td colspan<span class="token operator">=</span><span class="token string">"2"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Subtotal<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@_order<span class="token punctuation">.</span>Subtotal<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span><span class="token operator">&lt;</span>td colspan<span class="token operator">=</span><span class="token string">"2"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Tax<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@_order<span class="token punctuation">.</span>Tax<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tr <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"fw-bold"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span>td colspan<span class="token operator">=</span><span class="token string">"2"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Total<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@_order<span class="token punctuation">.</span>Total<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>tfoot<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>table<span class="token operator">&gt;</span>
<span class="token punctuation">}</span>

@code <span class="token punctuation">{</span>
    <span class="token punctuation">[</span>Parameter<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">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">private</span> OrderDetailDto<span class="token operator">?</span> _order<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">OnParametersSetAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token operator">=</span><span class="token operator">&gt;</span> _order <span class="token operator">=</span> <span class="token keyword">await</span> Http<span class="token punctuation">.</span><span class="token function">GetOrderAsync</span><span class="token punctuation">(</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>We can then easily view a breakdown of an order:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/orderdetails.png?sfvrsn=1310d955_2" alt="orderdetails" /></p><h3 id="create-order-page">Create Order Page</h3><p>We also built a friendly form that queries the Inventory API for the guitar catalog, lets the user order multiple line items and then calculates the totals client-side.</p><pre class=" language-csharp"><code class="prism  language-csharp">@page <span class="token string">"/create-order"</span>
@<span class="token keyword">using</span> Entities
@inject InventoryHttpClient Inventory
@inject OrdersHttpClient    Orders
@inject NavigationManager   Nav
@inject IJSRuntime          JS

<span class="token operator">&lt;</span>PageTitle<span class="token operator">&gt;</span>Create Order<span class="token operator">&lt;</span><span class="token operator">/</span>PageTitle<span class="token operator">&gt;</span>

@<span class="token keyword">if</span> <span class="token punctuation">(</span>_guitars <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>p <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"m-4"</span><span class="token operator">&gt;</span>Loading catalog&hellip;<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"container py-4"</span> style<span class="token operator">=</span><span class="token string">"max-width:720px"</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>h1 <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"display-6 mb-4"</span><span class="token operator">&gt;</span>➕ Create Order<span class="token operator">&lt;</span><span class="token operator">/</span>h1<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"mb-3"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>label <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label fw-semibold"</span><span class="token operator">&gt;</span>Customer name<span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>InputText @bind<span class="token operator">-</span>Value<span class="token operator">=</span><span class="token string">"_customerName"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-control"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>EditForm Model<span class="token operator">=</span><span class="token string">"_draft"</span> OnValidSubmit<span class="token operator">=</span><span class="token string">"AddLine"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row g-2 align-items-end"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-7"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>label <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label"</span><span class="token operator">&gt;</span>Product<span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>InputSelect TValue<span class="token operator">=</span><span class="token string">"Guid?"</span> @bind<span class="token operator">-</span>Value<span class="token operator">=</span><span class="token string">"_draft.ProductId"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-select"</span><span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>option <span class="token keyword">value</span><span class="token operator">=</span><span class="token string">""</span><span class="token operator">&gt;</span>‒ <span class="token keyword">select</span> guitar ‒<span class="token operator">&lt;</span><span class="token operator">/</span>option<span class="token operator">&gt;</span>
                    @<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> g <span class="token keyword">in</span> _guitars<span class="token punctuation">)</span>
                    <span class="token punctuation">{</span>
                        <span class="token operator">&lt;</span>option <span class="token keyword">value</span><span class="token operator">=</span><span class="token string">"@g.Id"</span><span class="token operator">&gt;</span>
                            @<span class="token punctuation">(</span>$<span class="token string">"{g.Brand} {g.Model} &mdash; {g.Price:C}"</span><span class="token punctuation">)</span>
                        <span class="token operator">&lt;</span><span class="token operator">/</span>option<span class="token operator">&gt;</span>
                    <span class="token punctuation">}</span>
                <span class="token operator">&lt;</span><span class="token operator">/</span>InputSelect<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-2"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>label <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label"</span><span class="token operator">&gt;</span>Qty<span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>InputNumber @bind<span class="token operator">-</span>Value<span class="token operator">=</span><span class="token string">"_draft.Quantity"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-control"</span> min<span class="token operator">=</span><span class="token string">"1"</span> max<span class="token operator">=</span><span class="token string">"10"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-3 text-end"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>label <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"form-label invisible"</span><span class="token operator">&gt;</span>btn<span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"btn btn-outline-primary w-100"</span> disabled<span class="token operator">=</span><span class="token string">"@(!_draft.IsValid)"</span><span class="token operator">&gt;</span>
                    Add
                <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>EditForm<span class="token operator">&gt;</span>

    @<span class="token keyword">if</span> <span class="token punctuation">(</span>_lines<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 operator">&lt;</span>table <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"table table-sm table-hover my-4"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>thead <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"table-light small text-uppercase"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span>Product<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Qty<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>Line<span class="token operator">&amp;</span>nbsp<span class="token punctuation">;</span>Total<span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>th<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>th<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>thead<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>tbody<span class="token operator">&gt;</span>
                @<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> l <span class="token keyword">in</span> _lines<span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">var</span> g <span class="token operator">=</span> _guitarsById<span class="token punctuation">[</span>l<span class="token punctuation">.</span>ProductId<span class="token punctuation">]</span><span class="token punctuation">;</span>
                    <span class="token operator">&lt;</span>tr<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td<span class="token operator">&gt;</span>@g<span class="token punctuation">.</span>Brand @g<span class="token punctuation">.</span>Model<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@l<span class="token punctuation">.</span>Quantity<span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>@<span class="token punctuation">(</span><span class="token punctuation">(</span>g<span class="token punctuation">.</span>Price <span class="token operator">*</span> l<span class="token punctuation">.</span>Quantity<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>td <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end"</span><span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"btn btn-sm btn-link text-danger"</span>
                                    @onclick<span class="token operator">=</span><span class="token string">"() =&gt; RemoveLine(l)"</span><span class="token operator">&gt;</span>
                                ✖
                            <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span><span class="token operator">/</span>td<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span><span class="token operator">/</span>tr<span class="token operator">&gt;</span>
                <span class="token punctuation">}</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>tbody<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>table<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"text-end mb-3"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>Subtotal<span class="token punctuation">:</span> <span class="token operator">&lt;</span>strong<span class="token operator">&gt;</span>@_subtotal<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>strong<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span><span class="token function">Tax</span> <span class="token punctuation">(</span><span class="token number">5</span> <span class="token operator">%</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token operator">&lt;</span>strong<span class="token operator">&gt;</span>@_tax<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>strong<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"fs-5"</span><span class="token operator">&gt;</span>Total<span class="token punctuation">:</span> <span class="token operator">&lt;</span>strong<span class="token operator">&gt;</span>@_total<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>strong<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"btn btn-success"</span> @onclick<span class="token operator">=</span><span class="token string">"SubmitOrder"</span><span class="token operator">&gt;</span>Submit Order<span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token punctuation">}</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>

@code <span class="token punctuation">{</span>
    <span class="token keyword">private</span> IReadOnlyList<span class="token operator">&lt;</span>GuitarDto<span class="token operator">&gt;</span><span class="token operator">?</span> _guitars<span class="token punctuation">;</span>
    <span class="token keyword">private</span> Dictionary<span class="token operator">&lt;</span>Guid<span class="token punctuation">,</span> GuitarDto<span class="token operator">&gt;</span> _guitarsById <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">private</span> <span class="token keyword">readonly</span> List<span class="token operator">&lt;</span>CreateOrderLine<span class="token operator">&gt;</span> _lines <span class="token operator">=</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">readonly</span> LineDraft _draft <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">private</span> <span class="token keyword">string</span> _customerName <span class="token operator">=</span> <span class="token string">"Web Customer"</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">decimal</span> _subtotal<span class="token punctuation">,</span> _tax<span class="token punctuation">,</span> _total<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">OnInitializedAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _guitars <span class="token operator">=</span> <span class="token keyword">await</span> Inventory<span class="token punctuation">.</span><span class="token function">GetGuitarsAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        _guitarsById <span class="token operator">=</span> _guitars<span class="token punctuation">.</span><span class="token function">ToDictionary</span><span class="token punctuation">(</span>g <span class="token operator">=</span><span class="token operator">&gt;</span> g<span class="token punctuation">.</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">void</span> <span class="token function">AddLine</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>_draft<span class="token punctuation">.</span>IsValid<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

        _lines<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">CreateOrderLine</span><span class="token punctuation">(</span>_draft<span class="token punctuation">.</span>ProductId<span class="token operator">!</span><span class="token punctuation">.</span>Value<span class="token punctuation">,</span> _draft<span class="token punctuation">.</span>Quantity<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        _draft<span class="token punctuation">.</span><span class="token function">Reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">RecalcTotals</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">void</span> <span class="token function">RemoveLine</span><span class="token punctuation">(</span>CreateOrderLine l<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _lines<span class="token punctuation">.</span><span class="token function">Remove</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">RecalcTotals</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">void</span> <span class="token function">RecalcTotals</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _subtotal <span class="token operator">=</span> _lines<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span>l <span class="token operator">=</span><span class="token operator">&gt;</span> _guitarsById<span class="token punctuation">[</span>l<span class="token punctuation">.</span>ProductId<span class="token punctuation">]</span><span class="token punctuation">.</span>Price <span class="token operator">*</span> l<span class="token punctuation">.</span>Quantity<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _tax      <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">Round</span><span class="token punctuation">(</span>_subtotal <span class="token operator">*</span> <span class="token number">0</span><span class="token punctuation">.</span>05m<span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        _total    <span class="token operator">=</span> _subtotal <span class="token operator">+</span> _tax<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">async</span> Task <span class="token function">SubmitOrder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> req  <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CreateOrderRequest</span><span class="token punctuation">(</span>_customerName<span class="token punctuation">,</span> _lines<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> Orders<span class="token punctuation">.</span><span class="token function">SubmitOrderAsync</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>resp<span class="token punctuation">.</span>IsSuccessStatusCode<span class="token punctuation">)</span>
            Nav<span class="token punctuation">.</span><span class="token function">NavigateTo</span><span class="token punctuation">(</span><span class="token string">"/orders"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">else</span>
            <span class="token keyword">await</span> JS<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"alert"</span><span class="token punctuation">,</span> $<span class="token string">"Order failed &ndash; {resp.StatusCode}"</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">class</span> <span class="token class-name">LineDraft</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">public</span> Guid<span class="token operator">?</span> 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">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 operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">bool</span> IsValid <span class="token operator">=</span><span class="token operator">&gt;</span> ProductId<span class="token punctuation">.</span>HasValue <span class="token operator">&amp;&amp;</span> Quantity <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            ProductId <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
            Quantity  <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here&rsquo;s our new Create Order page.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/createorder.png?sfvrsn=d067f62c_2" alt="createorder" /></p><p>With these three Razor components in place, the UI now consumes the full Orders API&mdash;and, transitively, the Inventory API&mdash;all without hard-coding a single URL.</p><h2 id="checking-in-on-the-apphost">Checking In on the AppHost</h2><p>With our new service in place, let&rsquo;s orchestrate them. Let&rsquo;s look at the final <code class="inline-code">Program.cs</code> for the AppHost project:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> DistributedApplication<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> password <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">AddParameter</span><span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">,</span> secret<span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> server <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">AddSqlServer</span><span class="token punctuation">(</span><span class="token string">"server"</span><span class="token punctuation">,</span> password<span class="token punctuation">,</span> <span class="token number">1433</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithDataVolume</span><span class="token punctuation">(</span><span class="token string">"guitar-data"</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithLifetime</span><span class="token punctuation">(</span>ContainerLifetime<span class="token punctuation">.</span>Persistent<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> inventoryDb <span class="token operator">=</span> server<span class="token punctuation">.</span><span class="token function">AddDatabase</span><span class="token punctuation">(</span><span class="token string">"guitardb"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> orderDb <span class="token operator">=</span> server<span class="token punctuation">.</span><span class="token function">AddDatabase</span><span class="token punctuation">(</span><span class="token string">"ordersdb"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> cache <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">AddRedis</span><span class="token punctuation">(</span><span class="token string">"cache"</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithRedisInsight</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">WithLifetime</span><span class="token punctuation">(</span>ContainerLifetime<span class="token punctuation">.</span>Persistent<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> inventoryApi <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token generic-method function">AddProject<span class="token punctuation">&lt;</span>Projects<span class="token punctuation">.</span>Api<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"inventory"</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>inventoryDb<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>cache<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WaitFor</span><span class="token punctuation">(</span>inventoryDb<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> ordersApi <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token generic-method function">AddProject<span class="token punctuation">&lt;</span>Projects<span class="token punctuation">.</span>OrdersApi<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"orders"</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>orderDb<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>inventoryApi<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">WaitFor</span><span class="token punctuation">(</span>orderDb<span class="token punctuation">)</span><span class="token punctuation">;</span>  

builder<span class="token punctuation">.</span><span class="token generic-method function">AddProject<span class="token punctuation">&lt;</span>Projects<span class="token punctuation">.</span>Frontend<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"frontend"</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>inventoryApi<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithReference</span><span class="token punctuation">(</span>ordersApi<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WaitFor</span><span class="token punctuation">(</span>inventoryApi<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WaitFor</span><span class="token punctuation">(</span>ordersApi<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">WithExternalHttpEndpoints</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">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>When you run the project through <code class="inline-code">AppHost</code>, the orchestrator spins up SQL Server, the Inventory API and the Orders API&mdash;all in the correct order.</p><p>Each project receives environment variables pointing at its dependencies. Because we called <code class="inline-code">AddServiceDefaults</code> in both our APIs, they automatically read these variables.</p><p>For example, the inventory service reads <code class="inline-code">ConnectionStrings__guitardb</code> to configure EF Core, and the orders service reads <code class="inline-code">Services__inventory__https</code> to configure its HTTP client.</p><p>With multiple services, the built-in health endpoints are even more important. Our <code class="inline-code">AppHost</code> uses these endpoints to decide when a service is ready. If the inventory service fails its health probe, the orders service waits until it is healthy. If a downstream call repeatedly fails, the circuit breaker configured by <code class="inline-code">AddServiceDefaults</code> prevents overwhelming the dependency.</p><h2 id="observing-the-end‑to‑end-flow">Observing the End‑to‑end Flow</h2><p>Let&rsquo;s fire up our full solution and observe what happens. From the terminal, run:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet run --project GuitarShop.AppHost
</code></pre><p>Aspire builds each project, creates a SQL Server container, launches the inventory and orders services, and opens the Developer Dashboard. On the <strong>Resources</strong> page you&rsquo;ll see what we&rsquo;ve built so far.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/resource-graph.png?sfvrsn=66664c5c_2" alt="resource graph" /></p><p>Clicking on <code class="inline-code">orders</code> reveals its environment variables&mdash;notice the <code class="inline-code">Services__inventory</code> entry pointing to the actual endpoints.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/inventory.png?sfvrsn=45ed2c3d_2" alt="inventory" /></p><p>Let&rsquo;s place an order from the frontend. Open the <strong>Traces</strong> tab and you&rsquo;ll see a span for the incoming POST <code class="inline-code">/orders</code> request, a child span for the outgoing GET <code class="inline-code">/guitars/{id}</code> call to inventory. This confirms that our instrumentation is working&mdash;the entire chain is captured and visualized.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-09/entire-chain.png?sfvrsn=343a4ed2_2" alt="entire chain" /></p><h2 id="wrapping-up-and-what’s-next">Wrapping Up (And What&rsquo;s Next)</h2><p>In this post, things got real: we added our first real orchestration scenario to Dave&rsquo;s Guitar Shop. We built a new orders service alongside our existing inventory service, used Aspire&rsquo;s service defaults to add telemetry and resilience, and orchestrated everything through the AppHost. The AppHost now declares separate databases for inventory and orders, and injects connection strings into the services.</p><p>In the <a href="https://www.telerik.com/blogs/net-aspire-6-deployment-using-azure-container-apps" target="_blank">final part of this series</a>, we&rsquo;ll take our project of our laptops and to the cloud. We&rsquo;ll see how Aspire&rsquo;s Azure Container Apps integration maps our SQL and Redis resources to Azure offerings and how the same AppHost definition can be used to deploy our entire guitar shop with a single <code class="inline-code">az containerapp up</code> command.</p><p>Stay tuned and see you soon!</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 Is DeepSeek? Dive in Using DeepSeek, .NET Aspire and Blazor</h4></div><div class="col-8"><p class="u-fs16 u-mb0">A new AI model has taken the tech world, and the actual world, by storm. <a target="_blank" href="https://www.telerik.com/blogs/what-deepseek-dive-using-deepseek-net-aspire-blazor">Learn about DeepSeek and see it in action.</a></p></div></div></aside><img src="https://feeds.telerik.com/link/23052/17161239.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
