Building an AI-powered application in .NET no longer requires writing a separate integration for every AI provider. Microsoft has introduced a common set of AI abstractions that make it easier to connect language models, manage conversations, and integrate AI capabilities into existing applications.
With .NET 11, developers can use the evolving Microsoft.Extensions.AI ecosystem to build AI tools using familiar C# patterns.
One important detail is that Microsoft.Extensions.AI is distributed through NuGet and is not exclusive to .NET 11. The same abstractions are available to applications targeting earlier supported .NET versions. This makes it easier to introduce AI functionality without tying the entire application to one framework release.
Let's build something useful rather than another chatbot.
Suppose your team maintains an application that generates technical logs. Developers often spend time reading these logs to understand what failed. We will build a small console tool that accepts an error message and uses an AI model to explain the problem and suggest troubleshooting steps.
The example uses .NET 11, Microsoft.Extensions.AI, and a locally running Ollama model.
Understanding AI Development in .NET 11
Before writing code, it helps to understand what Microsoft.Extensions.AI actually provides.
Microsoft.Extensions.AI introduces common interfaces for interacting with AI services. Instead of writing application logic directly against a particular provider's SDK, developers can use abstractions such as IChatClient.
The provider-specific library handles communication with the model, while the application works with a consistent interface.
Some important components include:
| Component | Purpose |
| Microsoft.Extensions.AI | Provides AI integration utilities and middleware |
| IChatClient | Common interface for interacting with chat models |
| ChatMessage | Represents messages sent to a model |
| ChatOptions | Configures model requests |
| ChatClientBuilder | Builds a pipeline around an AI client |
| IEmbeddingGenerator | Provides a common interface for generating embeddings |
This separation is useful in production applications.
For example, you might initially use a local model during development and later move to a hosted AI service. If your application depends on IChatClient rather than a provider-specific API throughout its business logic, the migration becomes easier.
You still need to configure the new provider and verify its capabilities, but the application architecture remains largely unchanged.
Step 1: Create a .NET 11 Console Application
Start by creating a console application using the .NET CLI.
dotnet new console -n AiLogAnalyzer -f net11.0
cd AiLogAnalyzer
The project file should contain the following configuration:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
The target framework specifies .NET 11, while nullable reference types and implicit usings help keep the code clean.
You will need the .NET 11 SDK installed to build this project.
Step 2: Install the Required NuGet Packages
We need two packages for this example.
dotnet add package Microsoft.Extensions.AI
dotnet add package OllamaSharp
Microsoft.Extensions.AI provides the common AI interfaces and supporting functionality.
OllamaSharp provides the client implementation for communicating with an Ollama server.
We are using Ollama because it allows us to run a language model locally without requiring a cloud API key.
Install Ollama separately, start its server, and download a supported model.
For example:
ollama pull llama3.2
Make sure the Ollama service is running on its default local endpoint.
The example assumes the model is available at localhost on port 11434.
Step 3: Connect the Application to an AI Model
Now we can create our first AI client.
Open Program.cs and add:
using Microsoft.Extensions.AI;
using OllamaSharp;
IChatClient chatClient = new OllamaApiClient(
new Uri("http://localhost:11434"),
"llama3.2");
var response = await chatClient.GetResponseAsync(
"Explain what a NullReferenceException means in C#.");
Console.WriteLine(response.Text);
There are two important parts of this implementation.
First, OllamaApiClient provides the actual connection to the local model.
Second, the application uses the IChatClient interface rather than depending on the concrete implementation everywhere.
The GetResponseAsync method sends a request to the model and returns the generated response.
Run the application:
dotnet run
If Ollama is running and the model is available, the console should display an explanation of NullReferenceException.
At this point, we have established communication with the model. Next, we will turn this into a useful developer tool.
Step 4: Build an AI-Powered Log Analyzer
Instead of accepting a fixed question, our application should allow developers to enter an error message.
We also want the model to respond like a technical troubleshooting assistant rather than a general-purpose chatbot.
Replace Program.cs with the following code:
using Microsoft.Extensions.AI;
using OllamaSharp;
IChatClient chatClient = new OllamaApiClient(
new Uri("http://localhost:11434"),
"llama3.2");
Console.WriteLine("AI Log Analyzer");
Console.WriteLine("Enter an error message:");
Console.WriteLine();
string? errorMessage = Console.ReadLine();
if (string.IsNullOrWhiteSpace(errorMessage))
{
Console.WriteLine("No error message provided.");
return;
}
var messages = new List<ChatMessage>
{
new(
ChatRole.System,
"""
You are an experienced .NET engineer.
Analyze the supplied application error.
Explain:
1. What the error means.
2. Possible causes.
3. How to investigate it.
4. Recommended fixes.
Keep explanations practical.
Do not invent missing stack trace details.
"""),
new(
ChatRole.User,
errorMessage)
};
var response = await chatClient.GetResponseAsync(messages);
Console.WriteLine();
Console.WriteLine("Analysis:");
Console.WriteLine(response.Text);
The system message defines how the model should approach the task.
The user message contains the actual error that needs to be analyzed.
Keeping these messages separate makes the application easier to maintain. It also allows you to change the model's instructions without modifying the user input.
Step 5: Test the Application With a Real Error
Run the application again:
dotnet run
Enter an error such as:
System.NullReferenceException:
Object reference not set to an instance of an object.
The AI model should explain that the application attempted to access a member through a null reference.
It may also recommend checking object initialization, validating nullable values, and reviewing the stack trace.
The exact response depends on the model.
This is worth remembering: AI-generated troubleshooting suggestions are not guaranteed to be correct. The model can explain common causes, but it cannot identify the exact failing line without sufficient context.
For production debugging, the output should support investigation rather than replace logs, stack traces, and developer judgment.
Step 6: Add Streaming Responses
Waiting for an entire response is acceptable for short answers. However, longer technical explanations can take several seconds to generate.
Streaming improves the user experience by displaying output as it becomes available.
Microsoft.Extensions.AI provides GetStreamingResponseAsync for this purpose.
Replace the response-handling code with:
await foreach (var update in
chatClient.GetStreamingResponseAsync(messages))
{
Console.Write(update.Text);
}
Console.WriteLine();
Instead of waiting for the complete response, the application prints text as the model generates it.
Streaming is particularly useful for interactive console tools, developer assistants, and applications that generate lengthy explanations.
It does not necessarily reduce the total processing time, but users can start reading the response earlier.
Step 7: Handle Errors and Cancellation
The previous example works for demonstration purposes, but a production application needs better error handling.
The model might not be running, the request might fail, or the user might cancel the operation.
A simple improvement is to introduce a cancellation token and handle common failures.
using var cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(60));
try
{
var response = await chatClient.GetResponseAsync(
messages,
cancellationToken: cancellation.Token);
Console.WriteLine(response.Text);
}
catch (OperationCanceledException)
{
Console.WriteLine(
"The AI request was cancelled or timed out.");
}
catch (HttpRequestException ex)
{
Console.WriteLine(
$"Unable to connect to the AI service: {ex.Message}");
}
The cancellation token prevents the application from waiting indefinitely.
In a hosted application, you would normally use structured logging rather than writing exception details directly to the console.
Also, avoid automatically retrying every failed request. Retries should depend on the type of failure, request cost, and whether the operation is safe to repeat.
Step 8: Keep the AI Provider Separate From Business Logic
As an application grows, it becomes important to separate model communication from application logic.
One approach is to create a dedicated service.
using Microsoft.Extensions.AI;
public class LogAnalysisService
{
private readonly IChatClient _chatClient;
public LogAnalysisService(IChatClient chatClient)
{
_chatClient = chatClient;
}
public async Task<string> AnalyzeAsync(
string error,
CancellationToken cancellationToken = default)
{
var messages = new List<ChatMessage>
{
new(
ChatRole.System,
"Explain this .NET error and suggest practical fixes."),
new(ChatRole.User, error)
};
var response = await _chatClient.GetResponseAsync(
messages,
cancellationToken: cancellationToken);
return response.Text;
}
}
This design makes the application easier to test.
You can replace the actual AI client with a test implementation when running unit tests.
It also keeps the application from depending on Ollama-specific code in every class.
The same design works well when moving the functionality into an ASP.NET Core API or background worker.
Comparing Direct SDK Integration and Microsoft.Extensions.AI
| Area | Direct provider SDK | Microsoft.Extensions.AI |
| Provider integration | Provider-specific APIs | Common interfaces |
| Switching providers | May require application changes | Usually fewer business-logic changes |
| Testing | Depends on SDK design | Easier to substitute IChatClient |
| Middleware | Provider-specific | Shared pipeline capabilities |
| Advanced model features | Often available directly | May require provider-specific access |
| Best suited for | Deep provider integration | Applications needing consistent AI integration |
Microsoft.Extensions.AI does not replace every feature offered by provider SDKs.
If your application requires a provider-specific capability that is not represented by the common abstractions, you may still need to use the provider SDK directly.
The abstraction is most valuable when you want consistent integration, testing, and application architecture.
Best Practices for Production AI Tools
Building a working AI prototype is relatively easy. Making it reliable enough for everyday use requires more attention.
Start by validating user input. Do not send unrestricted log files to an AI model without checking their contents.
Application logs may contain access tokens, personal information, connection strings, or internal system details. Remove sensitive information before sending logs to a hosted provider.
Keep system instructions separate from user input. Treat log messages as untrusted data because they may contain text that attempts to change the model's behavior.
Use cancellation tokens and sensible request timeouts. Track failures, response latency, and model usage.
If you move to a paid AI provider, monitor token consumption and request costs.
Finally, evaluate the model using a fixed collection of real error messages. A model that produces convincing explanations is not necessarily producing accurate diagnoses.
Common Problems and Troubleshooting
| Problem | Possible cause | Solution |
| Cannot connect to Ollama | Local server is not running | Start the Ollama service |
| Model not found | Model has not been downloaded | Pull the configured model |
| Slow responses | Limited hardware or large model | Test a smaller model |
| Empty or incomplete output | Model or request issue | Inspect response and provider logs |
| Incorrect troubleshooting advice | Missing context or model limitations | Supply relevant stack trace details |
| Application hangs | Long-running request | Configure cancellation and timeouts |
Advantages and Disadvantages
Advantages
- Uses familiar C# and .NET development patterns.
- Reduces direct dependencies on individual AI providers.
- Supports local and hosted AI models.
- Makes testing easier through common interfaces.
- Supports streaming responses and middleware integration.
- Can be introduced into existing applications without major architectural changes.
Disadvantages
- Requires an external or locally hosted AI model.
- AI responses may be inaccurate.
- Local models consume memory and computing resources.
- Hosted models introduce network dependency and potential usage costs.
- Provider-specific features may not be available through common abstractions.
- Sensitive input requires careful handling.
Summary
Microsoft.Extensions.AI provides a practical way to integrate language models into .NET applications without tightly coupling business logic to one AI provider.
In this article, we created a .NET 11 console application, connected it to a local Ollama model, built a simple log analyzer, added streaming responses, and introduced error handling and a reusable service layer.
The most useful part of this approach is the separation between application logic and the underlying AI provider. That makes it easier to test, maintain, and extend the tool as requirements change.
For developers introducing AI into existing .NET applications, I would start with a small, well-defined task such as log analysis, text classification, or document summarization. Build the integration around IChatClient, test it with realistic input, and measure its reliability before expanding the functionality.
A small AI tool that solves one problem consistently is often more useful than a complicated assistant that tries to handle everything.
Join the conversation! Your thoughts help the community grow.