<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Indirakumar R</title>
    <description>The latest articles on DEV Community by Indirakumar R (@indirakumar710).</description>
    <link>https://dev.to/indirakumar710</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3987893%2Ff95a4baf-d997-4406-8c51-e73072408ee6.jpg</url>
      <title>DEV Community: Indirakumar R</title>
      <link>https://dev.to/indirakumar710</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/indirakumar710"/>
    <language>en</language>
    <item>
      <title>Using Microsoft.Extensions.AI to Build Provider-Agnostic AI Applications in .NET</title>
      <dc:creator>Indirakumar R</dc:creator>
      <pubDate>Tue, 23 Jun 2026 17:46:30 +0000</pubDate>
      <link>https://dev.to/indirakumar710/using-microsoftextensionsai-to-build-provider-agnostic-ai-applications-in-net-1d87</link>
      <guid>https://dev.to/indirakumar710/using-microsoftextensionsai-to-build-provider-agnostic-ai-applications-in-net-1d87</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the previous article, we built a secure AI-ready chat API using ASP.NET Core. We used a custom interface called IAiChatService and implemented a local MockAiChatService so the project could run without Azure OpenAI quota.&lt;/p&gt;

&lt;p&gt;That was a good first step.&lt;/p&gt;

&lt;p&gt;In this article, we will take the next step and use Microsoft’s official AI abstraction approach with Microsoft.Extensions.AI.&lt;/p&gt;

&lt;p&gt;The main goal is simple:&lt;/p&gt;

&lt;p&gt;Build AI applications where the business code does not directly depend on one specific AI provider SDK.&lt;/p&gt;

&lt;p&gt;Instead of writing application code that is tightly coupled to Azure OpenAI, OpenAI, Ollama, GitHub Models, or any other provider, we can build against a common abstraction such as IChatClient.&lt;/p&gt;

&lt;p&gt;This makes the application cleaner, easier to test, and easier to change later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Provider-Agnostic AI Design Matters
&lt;/h2&gt;

&lt;p&gt;In many enterprise applications, the AI provider may change over time.&lt;/p&gt;

&lt;p&gt;Today, the application may use:&lt;/p&gt;

&lt;p&gt;Mock AI service for local development&lt;/p&gt;

&lt;p&gt;Tomorrow, it may use:&lt;/p&gt;

&lt;p&gt;Azure OpenAI&lt;/p&gt;

&lt;p&gt;Later, it may use:&lt;/p&gt;

&lt;p&gt;OpenAI&lt;br&gt;
Local model&lt;br&gt;
GitHub Models&lt;br&gt;
Another OpenAI-compatible endpoint&lt;/p&gt;

&lt;p&gt;If the controller or business service directly depends on one specific SDK, changing providers becomes difficult.&lt;/p&gt;

&lt;p&gt;Bad design:&lt;/p&gt;

&lt;p&gt;ChatController&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
Azure OpenAI SDK directly&lt;/p&gt;

&lt;p&gt;Better design:&lt;/p&gt;

&lt;p&gt;ChatController&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
IChatClient&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
AI Provider Implementation&lt;/p&gt;

&lt;p&gt;This is where Microsoft.Extensions.AI becomes useful.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is Microsoft.Extensions.AI?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Microsoft.Extensions.AI&lt;/strong&gt; provides common abstractions for AI services in .NET applications.&lt;/p&gt;

&lt;p&gt;It includes abstractions such as:&lt;/p&gt;

&lt;p&gt;IChatClient&lt;br&gt;
IEmbeddingGenerator&lt;br&gt;
ChatMessage&lt;br&gt;
ChatResponse&lt;br&gt;
ChatOptions&lt;/p&gt;

&lt;p&gt;The Microsoft.Extensions.AI.Abstractions package provides core exchange types, including IChatClient and &lt;strong&gt;IEmbeddingGenerator&lt;/strong&gt;. Any .NET library that provides an LLM client can implement IChatClient so consuming code can integrate with it more easily.&lt;/p&gt;

&lt;p&gt;The important idea is:&lt;/p&gt;

&lt;p&gt;Application code should depend on abstractions, not directly on provider-specific SDKs.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Will Build
&lt;/h2&gt;

&lt;p&gt;We will build an ASP.NET Core Web API that uses IChatClient.&lt;/p&gt;

&lt;p&gt;For local development, we will create a custom MockChatClient.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46wo5fjjhfv4lcibcfpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46wo5fjjhfv4lcibcfpj.png" alt="localvsprod" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The controller does not need to know which provider is behind IChatClient.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Create a new ASP.NET Core Web API project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;dotnet new webapi -n ProviderAgnosticAi.Api&lt;br&gt;
cd ProviderAgnosticAi.Api&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add the required packages:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;dotnet add package Microsoft.Extensions.AI.Abstractions&lt;br&gt;
dotnet add package Swashbuckle.AspNetCore&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a future OpenAI-compatible implementation, you can also add:&lt;/p&gt;

&lt;p&gt;_dotnet add package Microsoft.Extensions.AI.OpenAI&lt;br&gt;
dotnet add package Azure.AI.OpenAI&lt;br&gt;
dotnet add package Azure.Identity&lt;br&gt;
_&lt;br&gt;
For this article, we will keep the project runnable locally using a mock IChatClient.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recommended Project Structure
&lt;/h2&gt;

&lt;p&gt;ProviderAgnosticAi.Api&lt;br&gt;
│&lt;br&gt;
├── Controllers&lt;br&gt;
│   └── ChatController.cs&lt;br&gt;
│&lt;br&gt;
├── Models&lt;br&gt;
│   ├── ChatRequestDto.cs&lt;br&gt;
│   └── ChatResponseDto.cs&lt;br&gt;
│&lt;br&gt;
├── Services&lt;br&gt;
│   ├── MockChatClient.cs&lt;br&gt;
│   ├── IProviderAgnosticChatService.cs&lt;br&gt;
│   └── ProviderAgnosticChatService.cs&lt;br&gt;
│&lt;br&gt;
└── Program.cs&lt;/p&gt;
&lt;h2&gt;
  
  
  Create the Request Model
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Models/ChatRequestDto.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatRequestDto&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model represents the user input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Response Model
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Models/ChatResponseDto.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatResponseDto&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Answer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ErrorMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Provider field helps us identify which implementation generated the response.&lt;/p&gt;

&lt;p&gt;For local development, it will show:&lt;/p&gt;

&lt;p&gt;MockChatClient&lt;/p&gt;

&lt;p&gt;Later, it can show an Azure OpenAI or OpenAI-backed client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Mock IChatClient
&lt;/h2&gt;

&lt;p&gt;Now create a mock implementation of Microsoft’s IChatClient.&lt;/p&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/MockChatClient.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.AI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockChatClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IChatClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetResponseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ChatOptions&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ChatRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="s"&gt;"Provider-agnostic AI design means your application depends on an abstraction like IChatClient, "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"instead of directly depending on one AI provider SDK."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.Extensions.AI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="s"&gt;"Microsoft.Extensions.AI provides common abstractions such as IChatClient and IEmbeddingGenerator "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"so .NET applications can integrate AI services using familiar dependency injection patterns."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"azure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="s"&gt;"Azure OpenAI can be used behind the same IChatClient abstraction when a deployment and quota are available."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="s"&gt;"This is a local mock response using IChatClient. The controller depends on Microsoft.Extensions.AI abstractions, "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"so the implementation can later be replaced with Azure OpenAI, OpenAI, or another compatible provider."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Assistant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IAsyncEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatResponseUpdate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetStreamingResponseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ChatOptions&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Streaming is not implemented in the mock client for this demo."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;serviceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;serviceKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mock client lets us test the application without Azure OpenAI quota.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Application Service Interface
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/IProviderAgnosticChatService.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IProviderAgnosticChatService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatResponseDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AskAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ChatRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service keeps the controller clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Application Service Implementation
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/ProviderAgnosticChatService.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.AI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProviderAgnosticChatService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IProviderAgnosticChatService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IChatClient&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProviderAgnosticChatService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProviderAgnosticChatService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IChatClient&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProviderAgnosticChatService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_chatClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatResponseDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AskAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ChatRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatResponseDto&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;ErrorMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"User message is required."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"IChatClient"&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"You are a helpful enterprise AI assistant. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;"Explain concepts clearly and professionally."&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

                &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="n"&gt;ChatResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetResponseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatResponseDto&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Answer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;ErrorMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Provider-agnostic AI chat failed."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatResponseDto&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;ErrorMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Unable to generate a response at this time."&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service depends on IChatClient.&lt;/p&gt;

&lt;p&gt;It does not care whether the implementation is:&lt;/p&gt;

&lt;p&gt;MockChatClient&lt;br&gt;
Azure OpenAI client&lt;br&gt;
OpenAI client&lt;br&gt;
Local model client&lt;/p&gt;

&lt;p&gt;That is the main benefit of provider-agnostic design.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create the API Controller
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Controllers/ChatController.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/provider-agnostic-chat"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IProviderAgnosticChatService&lt;/span&gt; &lt;span class="n"&gt;_chatService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ChatController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IProviderAgnosticChatService&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_chatService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromBody&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;ChatRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ChatResponseDto&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AskAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller only talks to the application service.&lt;/p&gt;

&lt;p&gt;The application service talks to IChatClient.&lt;/p&gt;

&lt;p&gt;This creates a clean separation of concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update Program.cs
&lt;/h2&gt;

&lt;p&gt;Replace Program.cs with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.AI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ProviderAgnosticAi.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IChatClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MockChatClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IProviderAgnosticChatService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProviderAgnosticChatService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEndpointsApiExplorer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwagger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwaggerUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run and Test the API&lt;/p&gt;

&lt;p&gt;Run the application:&lt;/p&gt;

&lt;p&gt;dotnet run&lt;/p&gt;

&lt;p&gt;Open Swagger:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://localhost:" rel="noopener noreferrer"&gt;https://localhost:&lt;/a&gt;/swagger&lt;/p&gt;

&lt;p&gt;Test the endpoint:&lt;/p&gt;

&lt;p&gt;POST /api/provider-agnostic-chat&lt;/p&gt;

&lt;p&gt;Request:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "userMessage": "What is provider-agnostic AI design in .NET?"&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Expected response:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "success": true,&lt;br&gt;
  "answer": "Provider-agnostic AI design means your application depends on an abstraction like IChatClient, instead of directly depending on one AI provider SDK.",&lt;br&gt;
  "provider": "MockChatClient",&lt;br&gt;
  "errorMessage": null&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Test another request:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "userMessage": "What is Microsoft.Extensions.AI?"&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Expected response:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "success": true,&lt;br&gt;
  "answer": "Microsoft.Extensions.AI provides common abstractions such as IChatClient and IEmbeddingGenerator so .NET applications can integrate AI services using familiar dependency injection patterns.",&lt;br&gt;
  "provider": "MockChatClient",&lt;br&gt;
  "errorMessage": null&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cag5mmvjfcantwg6jw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cag5mmvjfcantwg6jw8.png" alt="Swagger Request" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5bfiaa0wgtiyy4kx0ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5bfiaa0wgtiyy4kx0ue.png" alt="Swagger Response" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Microsoft.Extensions.AI Fits in Enterprise Architecture
&lt;/h2&gt;

&lt;p&gt;A provider-agnostic AI architecture can look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs2b33aq46s2xxmhs0tcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs2b33aq46s2xxmhs0tcq.png" alt="System_Arch" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern is useful when:&lt;/p&gt;

&lt;p&gt;Azure quota is not available during development&lt;br&gt;
Teams want local testing&lt;br&gt;
The organization may change model providers later&lt;br&gt;
You want to mock AI responses in unit tests&lt;br&gt;
You want to centralize logging and telemetry&lt;br&gt;
You want clean dependency injection&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Enterprise Considerations
&lt;/h2&gt;

&lt;p&gt;Even when using abstractions, we still need enterprise controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended practices:
&lt;/h2&gt;

&lt;p&gt;Do not call AI providers directly from the frontend.&lt;br&gt;
Keep AI access behind ASP.NET Core APIs.&lt;br&gt;
Do not store provider keys in source code.&lt;br&gt;
Use local mock providers for development and testing.&lt;br&gt;
Use Azure Key Vault or Managed Identity for production secrets.&lt;br&gt;
Validate user input before calling the model.&lt;br&gt;
Avoid logging full prompts if they contain sensitive information.&lt;br&gt;
Track model usage, latency, and errors.&lt;br&gt;
Add rate limiting to control cost.&lt;br&gt;
Use RAG for company-specific answers.&lt;/p&gt;

&lt;p&gt;Microsoft.Extensions.AI helps with abstraction, but architecture, security, and governance still need to be designed carefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we built a provider-agnostic AI API using ASP.NET Core and Microsoft.Extensions.AI.&lt;/p&gt;

&lt;p&gt;The main design principle is:&lt;/p&gt;

&lt;p&gt;Application code should depend on AI abstractions, not directly on one provider SDK.&lt;/p&gt;

&lt;p&gt;By using IChatClient, our application becomes easier to test, easier to extend, and easier to switch to another provider later.&lt;/p&gt;

&lt;p&gt;For local development, we used:&lt;/p&gt;

&lt;p&gt;MockChatClient&lt;/p&gt;

&lt;p&gt;For production, we can later replace it with an Azure OpenAI-backed implementation.&lt;/p&gt;

&lt;p&gt;This approach is useful for enterprise developers because it supports clean architecture, dependency injection, local testing, provider flexibility, and long-term maintainability.&lt;/p&gt;

&lt;p&gt;Github Link : &lt;a href="https://github.com/indirakumar710/ProviderAgnosticAi.Api" rel="noopener noreferrer"&gt;https://github.com/indirakumar710/ProviderAgnosticAi.Api&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>azure</category>
      <category>programming</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Building a Secure AI Chat API using ASP.NET Core: Local Mock Today, Azure OpenAI Ready Tomorrow</title>
      <dc:creator>Indirakumar R</dc:creator>
      <pubDate>Mon, 22 Jun 2026 18:06:12 +0000</pubDate>
      <link>https://dev.to/indirakumar710/building-a-secure-ai-chat-api-using-aspnet-core-local-mock-today-azure-openai-ready-tomorrow-27p8</link>
      <guid>https://dev.to/indirakumar710/building-a-secure-ai-chat-api-using-aspnet-core-local-mock-today-azure-openai-ready-tomorrow-27p8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the previous article, we discussed a practical roadmap for building AI applications with .NET. We covered how ASP.NET Core, Azure OpenAI, Semantic Kernel, Microsoft.Extensions.AI, Azure AI Search, RAG, agents, and Responsible AI fit into enterprise AI application development.&lt;/p&gt;

&lt;p&gt;In this article, we will move from roadmap to implementation.&lt;/p&gt;

&lt;p&gt;We will build a secure AI-ready chat API using ASP.NET Core.&lt;br&gt;
The key point is this:&lt;/p&gt;

&lt;p&gt;Do not call AI services directly from the frontend. Keep the AI service behind a secure ASP.NET Core backend API.&lt;/p&gt;

&lt;p&gt;However, there is one practical challenge many developers face while learning Azure OpenAI:&lt;/p&gt;

&lt;p&gt;Some Azure free subscriptions may not have available quota to deploy Azure OpenAI models.&lt;/p&gt;

&lt;p&gt;This is not a blocker. We can still build the application using clean architecture.&lt;/p&gt;

&lt;p&gt;In this article, we will create an interface-based AI service layer. First, we will use a local MockAiChatService so the project can run without Azure OpenAI quota. Later, the same interface can be replaced with AzureOpenAiChatService when Azure OpenAI model quota is available.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Approach?
&lt;/h2&gt;

&lt;p&gt;In real enterprise applications, we should avoid tightly coupling the controller directly to one AI provider.&lt;br&gt;
Bad approach:&lt;/p&gt;

&lt;p&gt;ChatController -&amp;gt; Azure OpenAI SDK directly&lt;/p&gt;

&lt;p&gt;Better approach:&lt;/p&gt;

&lt;p&gt;ChatController -&amp;gt; IAiChatService -&amp;gt; MockAiChatService / AzureOpenAiChatService&lt;/p&gt;

&lt;p&gt;This gives us flexibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiuhdamuau6iyic3qf27x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiuhdamuau6iyic3qf27x.png" alt="Mock vs Live" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a useful enterprise pattern because the controller does not need to know which AI provider is being used.&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Will Build
&lt;/h2&gt;

&lt;p&gt;We will build a simple ASP.NET Core Web API with this endpoint:&lt;/p&gt;

&lt;p&gt;POST /api/chat&lt;/p&gt;

&lt;p&gt;The API will accept a user message and return a structured response.&lt;/p&gt;

&lt;p&gt;Sample request:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "userMessage": "Explain what deductible means in health insurance."&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sample response:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "success": true,&lt;br&gt;
  "answer": "A deductible is the amount you usually pay for covered services before your insurance plan starts paying.",&lt;br&gt;
  "model": "mock-ai-service-local-demo",&lt;br&gt;
  "errorMessage": null&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Not Call Azure OpenAI Directly from Frontend?
&lt;/h2&gt;

&lt;p&gt;In enterprise applications, the frontend should not directly call Azure OpenAI or any AI model provider.&lt;/p&gt;

&lt;p&gt;Avoid this:&lt;/p&gt;

&lt;p&gt;Browser / Mobile App -&amp;gt; Azure OpenAI&lt;/p&gt;

&lt;p&gt;Use this:&lt;/p&gt;

&lt;p&gt;Browser / Mobile App -&amp;gt; ASP.NET Core API -&amp;gt; AI Service -&amp;gt; Azure OpenAI&lt;/p&gt;

&lt;p&gt;The backend API helps with:&lt;/p&gt;

&lt;p&gt;API key protection&lt;br&gt;
Authentication&lt;br&gt;
Authorization&lt;br&gt;
Input validation&lt;br&gt;
Rate limiting&lt;br&gt;
Logging&lt;br&gt;
Cost control&lt;br&gt;
Prompt governance&lt;br&gt;
Responsible AI checks&lt;br&gt;
Safe error handling&lt;/p&gt;

&lt;p&gt;This is why ASP.NET Core is a strong foundation for enterprise AI applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02n9z6wlz1rlt6nnl52n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02n9z6wlz1rlt6nnl52n.png" alt="HLD" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Project Setup&lt;/p&gt;

&lt;p&gt;Create a new ASP.NET Core Web API project.&lt;/p&gt;

&lt;p&gt;dotnet new webapi -n EnterpriseAiChat.Api&lt;br&gt;
cd EnterpriseAiChat.Api&lt;/p&gt;

&lt;p&gt;Create the following folders:&lt;/p&gt;

&lt;p&gt;Controllers&lt;br&gt;
Models&lt;br&gt;
Options&lt;br&gt;
Services&lt;/p&gt;

&lt;p&gt;Recommended project structure:&lt;/p&gt;

&lt;p&gt;EnterpriseAiChat.Api&lt;br&gt;
│&lt;br&gt;
├── Controllers&lt;br&gt;
│   └── ChatController.cs&lt;br&gt;
│&lt;br&gt;
├── Models&lt;br&gt;
│   ├── ChatRequest.cs&lt;br&gt;
│   └── ChatResponse.cs&lt;br&gt;
│&lt;br&gt;
├── Options&lt;br&gt;
│   └── AzureOpenAiOptions.cs&lt;br&gt;
│&lt;br&gt;
├── Services&lt;br&gt;
│   ├── IAiChatService.cs&lt;br&gt;
│   ├── MockAiChatService.cs&lt;br&gt;
│   ├── AzureOpenAiChatService.cs&lt;br&gt;
│   ├── IChatRequestValidator.cs&lt;br&gt;
│   └── ChatRequestValidator.cs&lt;br&gt;
│&lt;br&gt;
├── appsettings.json&lt;br&gt;
└── Program.cs&lt;/p&gt;
&lt;h2&gt;
  
  
  Create Request Model
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Models/ChatRequest.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace EnterpriseAiChat.Api.Models; 
public class ChatRequest 
{ 
   public string UserMessage { get; set; } = string.Empty; 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model represents the user input sent to the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Response Model
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Models/ChatResponse.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace EnterpriseAiChat.Api.Models; 
public class ChatResponse 
{ 
  public bool Success { get; set; } 
  public string Answer { get; set; } = string.Empty; 
  public string? Model { get; set; } 
  public string? ErrorMessage { get; set; } 
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This response model gives us a consistent structure whether the response comes from a mock service or Azure OpenAI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create AI Chat Service Interface
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/IAiChatService.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using EnterpriseAiChat.Api.Models; 
namespace EnterpriseAiChat.Api.Services; 
public interface IAiChatService 
{ 
  Task&amp;lt;ChatResponse&amp;gt; GetResponseAsync( ChatRequest request, CancellationToken cancellationToken); 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface is the most important part of the design.&lt;/p&gt;

&lt;p&gt;The controller will depend on IAiChatService, not on a specific implementation.&lt;/p&gt;

&lt;p&gt;That means we can easily switch between:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MockAiChatService&lt;br&gt;
AzureOpenAiChatService&lt;br&gt;
Other provider implementation&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Create Mock AI Chat Service
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/MockAiChatService.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
using EnterpriseAiChat.Models;

namespace EnterpriseAiChat.Services
{
    public class MockAiChatService : IAiChatService
    {
        public Task&amp;lt;ChatResponse&amp;gt; GetResponseAsync(ChatRequest request, CancellationToken cancellationToken)
        {
            string userMessage = request.UserMessage.ToLower(); string answer;
            if (userMessage.Contains("deductible"))
            { 
                answer = "A deductible is the amount you usually pay for covered services before your insurance plan starts paying. " + "For example, if your deductible is $2,000, you may need to pay covered healthcare costs until that amount is met."; 
            }
            else if (userMessage.Contains("rag"))
            { 
                answer = "RAG stands for Retrieval-Augmented Generation. It retrieves relevant information from documents or databases first, " + "then sends that context to an AI model to generate a grounded answer."; 
            }
            else if (userMessage.Contains(".net") || userMessage.Contains("dotnet"))
            { 
                answer = ".NET is useful for AI applications because it provides secure APIs, dependency injection, authentication, logging, " + "database integration, and cloud deployment capabilities."; 
            }
            else 
            { 
                answer = "This is a mock AI response for local development. Replace MockAiChatService with AzureOpenAiChatService when Azure OpenAI quota is available."; 
            }
            ChatResponse response = new() { Success = true, Answer = answer, Model = "mock-ai-service-local-demo", ErrorMessage = null }; 
            return Task.FromResult(response);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to test the full API flow without needing Azure OpenAI quota.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Request Validator
&lt;/h2&gt;

&lt;p&gt;AI APIs should not blindly accept any user input.&lt;/p&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/IChatRequestValidator.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using EnterpriseAiChat.Models;

namespace EnterpriseAiChat.Services;

public interface IChatRequestValidator
{
    string? Validate(ChatRequest request);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create another file:&lt;/p&gt;

&lt;p&gt;Services/ChatRequestValidator.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using EnterpriseAiChat.Models;
using EnterpriseAiChat.Services;

namespace EnterpriseAiChat.Services;

public class ChatRequestValidator : IChatRequestValidator
{
    private const int MaxInputLength = 2000;

    public string? Validate(ChatRequest request)
    {
        if (request == null)
        {
            return "Request body is required.";
        }

        if (string.IsNullOrWhiteSpace(request.UserMessage))
        {
            return "User message is required.";
        }

        if (request.UserMessage.Length &amp;gt; MaxInputLength)
        {
            return $"User message should not exceed {MaxInputLength} characters.";
        }

        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple validator protects the API from empty input and very large prompts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Chat Controller
&lt;/h2&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Controllers/ChatController.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using EnterpriseAiChat.Models;
using EnterpriseAiChat.Services;
using Microsoft.AspNetCore.Mvc;

namespace EnterpriseAiChat.Controllers;

[ApiController]
[Route("api/chat")]
public class ChatController : ControllerBase
{
    private readonly IAiChatService _aiChatService;
    private readonly IChatRequestValidator _validator;

    public ChatController(
        IAiChatService aiChatService,
        IChatRequestValidator validator)
    {
        _aiChatService = aiChatService;
        _validator = validator;
    }

    [HttpPost]
    public async Task&amp;lt;IActionResult&amp;gt; Chat(
        [FromBody] ChatRequest request,
        CancellationToken cancellationToken)
    {
        string? validationError = _validator.Validate(request);

        if (validationError != null)
        {
            return BadRequest(new ChatResponse
            {
                Success = false,
                ErrorMessage = validationError
            });
        }

        ChatResponse response =
            await _aiChatService.GetResponseAsync(
                request,
                cancellationToken);

        if (!response.Success)
        {
            return StatusCode(
                StatusCodes.Status500InternalServerError,
                response);
        }

        return Ok(response);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller is simple because the logic is delegated to services.&lt;/p&gt;

&lt;p&gt;This is a clean API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update Program.cs
&lt;/h2&gt;

&lt;p&gt;Replace Program.cs with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using EnterpriseAiChat.Options;
using EnterpriseAiChat.Services;


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.Configure&amp;lt;AzureOpenAiOptions&amp;gt;(
    builder.Configuration.GetSection("AzureOpenAI"));

builder.Services.AddScoped&amp;lt;IAiChatService, AzureOpenAiChatService&amp;gt;();
builder.Services.AddScoped&amp;lt;IChatRequestValidator, ChatRequestValidator&amp;gt;();

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

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();

app.MapControllers();

app.Run();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the API
&lt;/h2&gt;

&lt;p&gt;Run the application:&lt;/p&gt;

&lt;p&gt;dotnet run&lt;/p&gt;

&lt;p&gt;Open Swagger:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://localhost:" rel="noopener noreferrer"&gt;https://localhost:&lt;/a&gt;/swagger&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;p&gt;POST /api/chat&lt;/p&gt;

&lt;p&gt;Request:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "userMessage": "Explain what deductible means in health insurance."&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Expected response:&lt;/p&gt;

&lt;p&gt;_{&lt;br&gt;
  "success": true,&lt;br&gt;
  "answer": "A deductible is the amount you usually pay for covered services before your insurance plan starts paying. For example, if your deductible is $2,000, you may need to pay covered healthcare costs until that amount is met.",&lt;br&gt;
  "model": "mock-ai-service-local-demo",&lt;br&gt;
  "errorMessage": null&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgdwz8wort7m110gppkr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgdwz8wort7m110gppkr5.png" alt="Request" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb93knt8ldakf2mgkxm8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb93knt8ldakf2mgkxm8b.png" alt="Response" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;_&lt;br&gt;
Try another request:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "userMessage": "What is RAG in AI applications?"&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Expected response:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "success": true,&lt;br&gt;
  "answer": "RAG stands for Retrieval-Augmented Generation. It retrieves relevant information from documents or databases first, then sends that context to an AI model to generate a grounded answer.",&lt;br&gt;
  "model": "mock-ai-service-local-demo",&lt;br&gt;
  "errorMessage": null&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Azure OpenAI Ready Configuration
&lt;/h2&gt;

&lt;p&gt;Even though this article uses a mock service for local testing, we can keep the project ready for Azure OpenAI.&lt;/p&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Options/AzureOpenAiOptions.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace EnterpriseAiChat.Options;

public class AzureOpenAiOptions
{
    public string Endpoint { get; set; } = string.Empty;

    public string DeploymentName { get; set; } = string.Empty;

    public string ApiKey { get; set; } = string.Empty;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update appsettings.json:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;{&lt;br&gt;
  "AzureOpenAI": {&lt;br&gt;
    "Endpoint": "&lt;a href="https://your-resource-name.openai.azure.com/" rel="noopener noreferrer"&gt;https://your-resource-name.openai.azure.com/&lt;/a&gt;",&lt;br&gt;
    "DeploymentName": "your-deployment-name"&lt;br&gt;
  }&lt;br&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do not store the API key in appsettings.json.&lt;/p&gt;

&lt;p&gt;For local development, use User Secrets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dotnet user-secrets init&lt;br&gt;
dotnet user-secrets set "AzureOpenAI:ApiKey" "YOUR_API_KEY"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production, use Azure Key Vault or Managed Identity.&lt;/p&gt;
&lt;h2&gt;
  
  
  AzureOpenAiChatService for Future Use
&lt;/h2&gt;

&lt;p&gt;When Azure OpenAI quota is available, add the Azure OpenAI implementation.&lt;/p&gt;

&lt;p&gt;Install packages:&lt;/p&gt;

&lt;p&gt;dotnet add package Azure.AI.OpenAI&lt;br&gt;
dotnet add package Azure.Identity&lt;/p&gt;

&lt;p&gt;Create a file:&lt;/p&gt;

&lt;p&gt;Services/AzureOpenAiChatService.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Azure;
using Azure.AI.OpenAI;
using EnterpriseAiChat.Models;
using EnterpriseAiChat.Options;
using EnterpriseAiChat.Services;
using Microsoft.Extensions.Options;
using OpenAI.Chat;

namespace EnterpriseAiChat.Services;

public class AzureOpenAiChatService : IAiChatService
{
    private readonly AzureOpenAIClient _client;
    private readonly AzureOpenAiOptions _options;
    private readonly ILogger&amp;lt;AzureOpenAiChatService&amp;gt; _logger;

    public AzureOpenAiChatService(
        IOptions&amp;lt;AzureOpenAiOptions&amp;gt; options,
        ILogger&amp;lt;AzureOpenAiChatService&amp;gt; logger)
    {
        _options = options.Value;
        _logger = logger;

        _client = new AzureOpenAIClient(
            new Uri(_options.Endpoint),
            new AzureKeyCredential(_options.ApiKey));
    }

    public async Task&amp;lt;ChatResponse&amp;gt; GetResponseAsync(
        ChatRequest request,
        CancellationToken cancellationToken)
    {
        try
        {
            ChatClient chatClient =
                _client.GetChatClient(_options.DeploymentName);

            List&amp;lt;ChatMessage&amp;gt; messages = new()
            {
                new SystemChatMessage(
                    "You are a helpful enterprise AI assistant. " +
                    "Give clear, concise, and professional answers. " +
                    "If you are unsure, say you are unsure."),

                new UserChatMessage(request.UserMessage)
            };

            ChatCompletion completion =
                await chatClient.CompleteChatAsync(
                    messages,
                    cancellationToken: cancellationToken);

            string answer = completion.Content.Count &amp;gt; 0
                ? completion.Content[0].Text
                : string.Empty;

            return new ChatResponse
            {
                Success = true,
                Answer = answer,
                Model = _options.DeploymentName
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Azure OpenAI request failed.");

            return new ChatResponse
            {
                Success = false,
                ErrorMessage = "Unable to generate AI response at this time."
            };
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Azure OpenAI Deployment Flow
&lt;/h2&gt;

&lt;p&gt;The normal deployment flow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdemqi9n7ezdp5ur925db.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdemqi9n7ezdp5ur925db.png" alt="OpenAI flow" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using a free subscription and no models are available, you may see quota or region-related issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we built a secure AI-ready chat API using ASP.NET Core.&lt;/p&gt;

&lt;p&gt;Because Azure OpenAI quota may not be available in some free subscriptions, we used a local MockAiChatService first. This allowed us to test the full API flow without waiting for cloud model deployment.&lt;/p&gt;

&lt;p&gt;The most important design pattern is:&lt;/p&gt;

&lt;p&gt;Controller -&amp;gt; Interface -&amp;gt; Implementation&lt;/p&gt;

&lt;p&gt;This allows us to run locally with:&lt;/p&gt;

&lt;p&gt;MockAiChatService&lt;/p&gt;

&lt;p&gt;and later switch to:&lt;/p&gt;

&lt;p&gt;AzureOpenAiChatService&lt;/p&gt;

&lt;p&gt;without changing the controller.&lt;/p&gt;

&lt;p&gt;This is a practical and enterprise-friendly approach for .NET developers who want to start building AI applications today and connect to Azure OpenAI when quota is available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub URL: &lt;a href="https://github.com/indirakumar710/EnterpriseAiChat" rel="noopener noreferrer"&gt;https://github.com/indirakumar710/EnterpriseAiChat&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj
#AI #AzureOpenAI #Microsoft #DotNet</title>
      <dc:creator>Indirakumar R</dc:creator>
      <pubDate>Wed, 17 Jun 2026 21:53:22 +0000</pubDate>
      <link>https://dev.to/indirakumar710/-1off</link>
      <guid>https://dev.to/indirakumar710/-1off</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj" class="crayons-story__hidden-navigation-link"&gt;Building AI Applications with .NET: A Practical Roadmap for Enterprise Developers&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/indirakumar710" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3987893%2Ff95a4baf-d997-4406-8c51-e73072408ee6.jpg" alt="indirakumar710 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/indirakumar710" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Indirakumar R
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Indirakumar R
                
              
              &lt;div id="story-author-preview-content-3927264" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/indirakumar710" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3987893%2Ff95a4baf-d997-4406-8c51-e73072408ee6.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Indirakumar R&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj" id="article-link-3927264"&gt;
          Building AI Applications with .NET: A Practical Roadmap for Enterprise Developers
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/architecture"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;architecture&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Building AI Applications with .NET: A Practical Roadmap for Enterprise Developers</title>
      <dc:creator>Indirakumar R</dc:creator>
      <pubDate>Wed, 17 Jun 2026 21:07:01 +0000</pubDate>
      <link>https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj</link>
      <guid>https://dev.to/indirakumar710/building-ai-applications-with-net-a-practical-roadmap-for-enterprise-developers-2ijj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Artificial Intelligence is becoming a core part of modern enterprise application development. Earlier, many developers assumed that AI development was mainly limited to Python, machine learning notebooks, or data science teams. But today, enterprise developers can build intelligent applications using familiar Microsoft technologies such as .NET, C#, ASP.NET Core, Azure OpenAI, Semantic Kernel, Microsoft.Extensions.AI, Azure AI Search, SQL Server, and Azure cloud services.&lt;/p&gt;

&lt;p&gt;For .NET developers, this creates a major opportunity.&lt;/p&gt;

&lt;p&gt;Many enterprise systems already use .NET for APIs, web applications, background services, authentication, reporting, integrations, and workflow automation. By adding AI capabilities into these existing systems, developers can build smarter applications without completely changing their technology stack.&lt;/p&gt;

&lt;p&gt;This article provides a practical roadmap for enterprise developers who want to start building AI-powered applications with .NET.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why .NET for AI Application Development?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When people hear about AI, they often think about model training, Python libraries, machine learning experiments, or data science platforms. These are important areas, but enterprise AI application development is different.&lt;/p&gt;

&lt;p&gt;In many organizations, the real business need is not to train a large language model from scratch. The actual requirement is to integrate AI into existing business applications.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a chat assistant to an internal portal&lt;/li&gt;
&lt;li&gt;Summarize customer support tickets&lt;/li&gt;
&lt;li&gt;Explain complex insurance documents&lt;/li&gt;
&lt;li&gt;Generate reports from business data&lt;/li&gt;
&lt;li&gt;Search enterprise documents using natural language&lt;/li&gt;
&lt;li&gt;Classify incoming emails or cases&lt;/li&gt;
&lt;li&gt;Build AI agents that can perform workflow actions&lt;/li&gt;
&lt;li&gt;Create secure APIs that connect business systems with AI models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where .NET becomes very useful.&lt;/p&gt;

&lt;p&gt;.NET is already widely used for enterprise applications. It provides strong support for API development, dependency injection, authentication, logging, testing, configuration, deployment, and cloud integration.&lt;/p&gt;

&lt;p&gt;A typical enterprise AI application needs all these capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbvzvflrz30px9q9vs9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbvzvflrz30px9q9vs9e.png" alt="capabilities" width="430" height="1153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, .NET is not replacing data science tools. Instead, .NET helps developers build real-world AI applications around business systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is an AI Application?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A traditional application usually follows fixed business rules.&lt;/p&gt;

&lt;p&gt;An AI application adds intelligence using models, prompts, embeddings, natural language processing, semantic search, reasoning, or automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01x5mgl4644bep8n5wad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01x5mgl4644bep8n5wad.png" alt="Traditional vs AI application" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
A simple AI application may answer user questions.&lt;/p&gt;

&lt;p&gt;A more advanced enterprise AI application may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search internal documents&lt;/li&gt;
&lt;li&gt;Summarize records&lt;/li&gt;
&lt;li&gt;Call business APIs&lt;/li&gt;
&lt;li&gt;Generate reports&lt;/li&gt;
&lt;li&gt;Explain complex content&lt;/li&gt;
&lt;li&gt;Perform multi-step tasks&lt;/li&gt;
&lt;li&gt;Ask for human approval&lt;/li&gt;
&lt;li&gt;Store an audit trail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means AI application development is not only about writing prompts. It is about designing secure, scalable, and responsible software systems around AI capabilities.&lt;/p&gt;

&lt;p&gt;Core Microsoft Technologies for .NET AI Applications&lt;/p&gt;

&lt;p&gt;To build AI applications with .NET, developers should understand the role of each Microsoft technology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. ASP.NET Core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ASP.NET Core is the foundation for building secure AI APIs.&lt;/p&gt;

&lt;p&gt;In an enterprise application, the frontend should not directly call the AI model. Instead, the request should go through a backend API.&lt;/p&gt;

&lt;p&gt;Recommended pattern:&lt;/p&gt;

&lt;p&gt;Frontend -&amp;gt; ASP.NET Core API -&amp;gt; AI Service -&amp;gt; AI Model&lt;/p&gt;

&lt;p&gt;This approach helps with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Input validation&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Cost control&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Responsible AI checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example API endpoints:&lt;/p&gt;

&lt;p&gt;POST /api/chat&lt;br&gt;
POST /api/summarize&lt;br&gt;
POST /api/explain-document&lt;br&gt;
POST /api/search-knowledge-base&lt;br&gt;
POST /api/generate-report&lt;/p&gt;

&lt;p&gt;ASP.NET Core gives developers a clean way to expose AI features as reusable enterprise APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Azure OpenAI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Azure OpenAI allows enterprise applications to use powerful language models through Azure.&lt;/p&gt;

&lt;p&gt;Common use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text generation&lt;/li&gt;
&lt;li&gt;Question answering&lt;/li&gt;
&lt;li&gt;Summarization&lt;/li&gt;
&lt;li&gt;Document explanation&lt;/li&gt;
&lt;li&gt;Classification&lt;/li&gt;
&lt;li&gt;Code explanation&lt;/li&gt;
&lt;li&gt;Reasoning assistance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a .NET application, Azure OpenAI is usually called from a backend service.&lt;/p&gt;

&lt;p&gt;Example flow:&lt;/p&gt;

&lt;p&gt;User Question&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
ASP.NET Core API&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
Azure OpenAI Service&lt;br&gt;
   |&lt;br&gt;
   v&lt;br&gt;
AI Response&lt;/p&gt;

&lt;p&gt;Azure OpenAI is useful when developers want to add natural language understanding and generation into their applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Microsoft.Extensions.AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microsoft.Extensions.AI provides common abstractions for working with AI services in .NET applications.&lt;/p&gt;

&lt;p&gt;The main idea is to avoid tightly coupling your application to one specific AI provider or SDK.&lt;/p&gt;

&lt;p&gt;Instead of designing the entire application around one vendor-specific client, developers can use common interfaces and patterns.&lt;/p&gt;

&lt;p&gt;Conceptual flow:&lt;/p&gt;

&lt;p&gt;Application Code -&amp;gt; AI Abstraction -&amp;gt; AI Provider&lt;/p&gt;

&lt;p&gt;This helps with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner architecture&lt;/li&gt;
&lt;li&gt;Dependency injection&lt;/li&gt;
&lt;li&gt;Easier testing&lt;/li&gt;
&lt;li&gt;Provider flexibility&lt;/li&gt;
&lt;li&gt;Middleware support&lt;/li&gt;
&lt;li&gt;Logging and telemetry&lt;/li&gt;
&lt;li&gt;Better long-term maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprise developers, Microsoft.Extensions.AI is important because it brings AI development closer to familiar .NET patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Semantic Kernel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Semantic Kernel is useful when AI applications need orchestration, plugins, function calling, or agent-like behavior.&lt;/p&gt;

&lt;p&gt;A simple AI application may only send a prompt and receive a response. But enterprise applications often need more than that.&lt;/p&gt;

&lt;p&gt;For example, an AI assistant may need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the user request&lt;/li&gt;
&lt;li&gt;Search internal documents&lt;/li&gt;
&lt;li&gt;Call a C# business function&lt;/li&gt;
&lt;li&gt;Calculate a result&lt;/li&gt;
&lt;li&gt;Generate an explanation&lt;/li&gt;
&lt;li&gt;Ask for human approval&lt;/li&gt;
&lt;li&gt;Return a final answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Semantic Kernel helps developers connect AI models with functions, plugins, prompts, and workflows.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;User: "Explain this claim amount and calculate my remaining deductible."&lt;/p&gt;

&lt;p&gt;Semantic Kernel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls DeductibleCalculatorPlugin&lt;/li&gt;
&lt;li&gt;Uses ExplanationPrompt&lt;/li&gt;
&lt;li&gt;Generates user-friendly response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially useful for building enterprise AI assistants and agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Azure AI Search and RAG&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RAG stands for Retrieval-Augmented Generation.&lt;/p&gt;

&lt;p&gt;A normal AI model answers from its general training knowledge. But enterprise applications often need answers from private company documents, policies, manuals, or databases.&lt;/p&gt;

&lt;p&gt;RAG solves this problem by retrieving relevant information first and then sending that context to the AI model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx0cssmoo8zdidt9ltii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx0cssmoo8zdidt9ltii.png" alt="AI Search and RAG" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
This pattern is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR policy assistants&lt;/li&gt;
&lt;li&gt;Legal document assistants&lt;/li&gt;
&lt;li&gt;Healthcare claim explainers&lt;/li&gt;
&lt;li&gt;Product support knowledge bases&lt;/li&gt;
&lt;li&gt;Technical documentation assistants&lt;/li&gt;
&lt;li&gt;Internal enterprise search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For .NET developers, Azure AI Search can be used with Azure OpenAI to build practical RAG applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Responsible AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Responsible AI is very important in enterprise applications.&lt;/p&gt;

&lt;p&gt;AI-generated answers can sometimes be incorrect, incomplete, or overconfident. This is especially risky in domains like healthcare, finance, legal, compliance, and HR.&lt;/p&gt;

&lt;p&gt;A good enterprise AI application should include controls such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input validation&lt;/li&gt;
&lt;li&gt;PII masking&lt;/li&gt;
&lt;li&gt;Content filtering&lt;/li&gt;
&lt;li&gt;Grounding with trusted data&lt;/li&gt;
&lt;li&gt;Human approval for critical actions&lt;/li&gt;
&lt;li&gt;Audit logging&lt;/li&gt;
&lt;li&gt;Disclaimers&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;If an AI application explains an insurance claim, it should not say:&lt;/p&gt;

&lt;p&gt;You must pay this amount immediately.&lt;/p&gt;

&lt;p&gt;A safer response would be:&lt;/p&gt;

&lt;p&gt;This amount may be your responsibility based on the claim summary. Please verify the final amount with your provider bill and insurance company.&lt;/p&gt;

&lt;p&gt;Responsible AI is not optional. It should be part of the application design from the beginning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Architecture of a .NET AI Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A simple .NET AI application can follow this architecture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx4aowwq19uhkbwktdf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx4aowwq19uhkbwktdf.png" alt=".Net AI Application" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical Learning Roadmap for .NET Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a recommended learning path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8jucc9pmq2kjj99bbjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8jucc9pmq2kjj99bbjt.png" alt="Learning Path" width="617" height="1369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI application development is becoming an important skill for enterprise developers. .NET developers are in a strong position because they already understand APIs, services, authentication, databases, logging, deployment, and enterprise architecture.&lt;/p&gt;

&lt;p&gt;The key is to use AI as part of a well-designed application, not as a standalone experiment.&lt;/p&gt;

&lt;p&gt;A strong .NET AI application should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure ASP.NET Core APIs&lt;/li&gt;
&lt;li&gt;Azure OpenAI or compatible AI model integration&lt;/li&gt;
&lt;li&gt;Clean service architecture&lt;/li&gt;
&lt;li&gt;Deterministic business logic in C#&lt;/li&gt;
&lt;li&gt;AI-based explanation and summarization&lt;/li&gt;
&lt;li&gt;RAG for enterprise documents&lt;/li&gt;
&lt;li&gt;Responsible AI controls&lt;/li&gt;
&lt;li&gt;Audit logging and observability&lt;/li&gt;
&lt;li&gt;Deployment-ready architecture&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>api</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
