Verified against .NET 10 and Microsoft.Extensions.AI 10.x. Last reviewed: July 23, 2026. These APIs changed significantly between preview and stable releases, so compare copied samples with the current names shown below.
Your Sample Code Compiles Against a Version That No Longer Exists
An older Microsoft.Extensions.AI sample can look perfectly reasonable and still fail on the first build. The package stabilised quickly, and several preview-era method names were replaced before the current 10.x line. Blog posts, videos, GitHub snippets, and AI-generated answers often preserve those older names long after the package has moved on.
The failures usually fall into the same clusters: CompleteAsync no longer exists, .AsChatClient() cannot be found, a NuGet warning says the Ollama adapter is deprecated, keyed clients have no middleware, or a tool is offered to the model but never executes. These are not five unrelated problems. They are signs that the sample mixes preview APIs, outdated provider adapters, and an incomplete client pipeline.
Mistake 1 & 2: Obsolete Method and Extension Names
The most obvious symptom is a compile error on code copied from an older sample. Current clients use GetResponseAsync for a normal response and GetStreamingResponseAsync for streamed updates. Provider SDK clients are adapted with .AsIChatClient(), not the older .AsChatClient() extension.
There is another stale assumption nearby: IChatClient does not expose a Metadata property. The current interface is intentionally small. Its main members are GetResponseAsync, GetStreamingResponseAsync, GetService, and Dispose. Use GetService when a client or wrapper exposes an underlying service.
The returned framework type is Microsoft.Extensions.AI.ChatResponse. Its useful values include Text, Usage, FinishReason, ModelId, and ConversationId. Those names are a useful quick test when checking whether a snippet matches the stable API.
// WRONG: preview-era names
ChatResponse response = await client.CompleteAsync(messages);
await foreach (var update in client.CompleteStreamingAsync(messages)) { }
IChatClient chatClient = openAiChatClient.AsChatClient();
object? metadata = chatClient.Metadata;
// FIX: current Microsoft.Extensions.AI surface
IChatClient currentClient = openAiChatClient.AsIChatClient();
ChatResponse currentResponse =
await currentClient.GetResponseAsync(messages, cancellationToken: ct);
Console.WriteLine(currentResponse.Text);
Console.WriteLine(currentResponse.FinishReason);
Console.WriteLine(currentResponse.Usage);
await foreach (ChatResponseUpdate update in
currentClient.GetStreamingResponseAsync(messages, cancellationToken: ct))
{
Console.Write(update.Text);
}
// Ask the client or middleware for an exposed service.
OpenAI.Chat.ChatClient? inner =
currentClient.GetService<OpenAI.Chat.ChatClient>();
Mistake 3: Installing the Deprecated Ollama Package
A second common problem begins before any code is written: the project installs Microsoft.Extensions.AI.Ollama because an older tutorial uses OllamaChatClient. That package is deprecated. Its NuGet page states that no further updates, features, or fixes are planned and recommends OllamaSharp instead.
The replacement is not merely a package-name change. OllamaApiClient from OllamaSharp implements IChatClient directly, so it can be registered as the provider client and wrapped with the same middleware used for cloud providers. New projects should not deliberately adopt an adapter that has already reached the end of its update path.
Deprecation warning: Do not treat a successful restore as proof that a package is suitable for new development. NuGet can restore a deprecated package while also warning that it will receive no further fixes.
The same warning applies to local embeddings. If another article shows an old Microsoft Ollama embedding adapter, check the current IEmbeddingGenerator guidance and use the supported OllamaSharp integration instead. The detailed hybrid-search tutorial linked later uses that current approach.
// DEPRECATED — avoid in new code
// IChatClient client = new OllamaChatClient(
// new Uri("http://localhost:11434"),
// "llama3.2");
// CURRENT — OllamaSharp
using OllamaSharp;
IChatClient ollama = new OllamaApiClient(
new Uri("http://localhost:11434"),
"llama3.2");
// Compose it like any other IChatClient.
IChatClient pipeline = new ChatClientBuilder(ollama)
.UseFunctionInvocation()
.UseOpenTelemetry()
.Build();
Mistake 4: Registration That Silently Drops Your Middleware
Keyed dependency injection is useful when an application has several providers, but the registration method matters. AddKeyedSingleton<IChatClient> stores a keyed client and stops there. It does not return the ChatClientBuilder used to add function invocation, OpenTelemetry, logging, caching, or other delegating middleware.
AddKeyedChatClient is designed for this scenario. It registers the keyed client and returns a builder, allowing each provider to receive its own pipeline. An OpenAI client can enable tools and telemetry while another provider can use a smaller pipeline based on its supported capabilities.
A related source of confusion is naming the application's public DTO ChatResponse. That collides with Microsoft.Extensions.AI.ChatResponse and produces ambiguous references. Use names such as GatewayChatRequest and GatewayChatResponse, while reusing the framework's ChatMessage and ChatRole types internally.
Capability warning: A keyed registration identifies a client; it does not prove that its model supports tools, structured output, usage reporting, or the same context size. Keep capability metadata in your provider registry and validate requests before sending them.
// FLAT REGISTRATION: the keyed client exists, but no builder pipeline follows.
builder.Services.AddKeyedSingleton<IChatClient>("ollama", (_, _) =>
new OllamaApiClient(new Uri(ollamaEndpoint), ollamaModel));
// BUILDER-BASED REGISTRATION: middleware belongs to this provider.
builder.Services
.AddKeyedChatClient("ollama", _ =>
new OllamaApiClient(new Uri(ollamaEndpoint), ollamaModel))
.UseFunctionInvocation()
.UseOpenTelemetry();
// Avoid a framework type-name collision.
public sealed record GatewayChatRequest(
string Message,
string? Provider);
public sealed record GatewayChatResponse(
string Content,
string Provider,
string? Model,
string? FinishReason);
How to Tell If a Snippet Is Current
Before copying an AI integration sample into a current .NET project, scan it for four warning signs. Any one of them suggests that the code was written against an older preview or a deprecated adapter. Check the package's official README and API reference before trying to repair the surrounding code.
Current code should normally show GetResponseAsync, GetStreamingResponseAsync, .AsIChatClient(), and OllamaSharp for local Ollama access. It should use a ChatClientBuilder when middleware is required and GetService rather than a nonexistent Metadata property.
// OLD / STALE // CURRENT
CompleteAsync(...) GetResponseAsync(...)
CompleteStreamingAsync(...) GetStreamingResponseAsync(...)
providerClient.AsChatClient() providerClient.AsIChatClient()
Microsoft.Extensions.AI.Ollama OllamaSharp
OllamaChatClient OllamaApiClient
chatClient.Metadata chatClient.GetService<T>()
AddKeyedSingleton<IChatClient>(...) AddKeyedChatClient(...)
// when middleware is required:
ChatOptions.Tools only .UseFunctionInvocation() + Tools