Integrating Large Language Models with ASP.NET Core

Introduction

Adding an LLM to an ASP.NET Core application is more than sending a prompt to an API.

A production integration needs a clear boundary between the HTTP API, application logic, model provider, prompt construction, retrieval, security, observability, and failure handling.

A useful architecture is:

Angular / Client
       ↓
ASP.NET Core API
       ↓
Application Service
       ↓
LLM / AI Abstraction
       ↓
Model Provider

When retrieval is required:

Request
  ↓
API
  ↓
Embedding
  ↓
Vector Search
  ↓
Relevant Context
  ↓
LLM
  ↓
Response

Keep the Model Provider Behind an Application Boundary

Avoid calling a model SDK directly from controllers.

Instead:

public interface IChatService
{
    Task<string> CompleteAsync(
        string prompt,
        CancellationToken cancellationToken);
}

The infrastructure implementation can use the selected provider.

This keeps provider-specific details away from business workflows.

Request Flow

A typical endpoint might look like:

app.MapPost("/api/chat",
    async (
        ChatRequest request,
        IChatService chatService,
        CancellationToken cancellationToken) =>
    {
        var response = await chatService.CompleteAsync(
            request.Message,
            cancellationToken);

        return Results.Ok(new ChatResponse(response));
    });

The endpoint remains responsible for HTTP concerns, while the service owns the AI interaction.

Prompt Construction

Do not concatenate untrusted input into a large system prompt without considering injection and data-boundary risks.

Separate:

  • system instructions
  • application context
  • retrieved context
  • user input

Treat retrieved documents and user-provided text as data, not trusted instructions.

Structured Output

When an application needs machine-readable results, prefer structured responses supported by the selected model/provider rather than asking the model to “please return valid JSON” and hoping for the best.

Validate model output before using it as application data.

For example:

public sealed record ClassificationResult(
    string Category,
    double Confidence);

The application should validate the resulting structure before taking business actions.

Semantic Kernel

Semantic Kernel can provide abstractions for AI services, prompts, plugins, and orchestration.

Use it when the application benefits from those abstractions.

Do not introduce an AI orchestration framework simply to call one model once.

The architectural principle remains:

Business Workflow
       ↓
AI Abstraction
       ↓
Provider

Retrieval-Augmented Generation

For domain-specific answers, an LLM often needs external knowledge.

A RAG pipeline can be:

User Question
     ↓
Embedding
     ↓
Vector Search
     ↓
Top Relevant Chunks
     ↓
Prompt Context
     ↓
LLM
     ↓
Grounded Answer

The vector database is responsible for retrieval; it is not the LLM itself.

For .NET applications, PostgreSQL with pgvector and managed vector services are both viable depending on operational requirements.

Conversation History

Do not send unlimited conversation history on every request.

Define:

  • maximum history
  • summarization strategy
  • relevant context
  • token budget

Store conversation state deliberately if users need conversations to persist.

Security

AI endpoints should be treated like any other sensitive API.

Consider:

  • authentication
  • authorization
  • rate limiting
  • input size limits
  • output validation
  • secret management
  • logging controls
  • prompt injection defenses
  • tenant isolation

Never expose provider API keys to Angular or other untrusted clients.

Streaming

For chat interfaces, streaming can improve perceived responsiveness.

Possible architectures include:

LLM
 ↓
ASP.NET Core streaming endpoint
 ↓
Angular

Depending on requirements, use an appropriate streaming mechanism such as Server-Sent Events or SignalR.

Do not assume that streaming automatically improves overall backend throughput. It changes how results are delivered to the client.

Observability

Track operational information such as:

  • request duration
  • provider latency
  • failures
  • token usage when available
  • model/provider
  • retrieval latency
  • number of retrieved chunks

Avoid logging raw prompts or responses when they may contain sensitive information.

Resilience

Model providers can fail, throttle, or become temporarily unavailable.

Use appropriate:

  • timeouts
  • cancellation
  • retry policies for transient failures
  • circuit breakers where justified
  • fallback behavior

Do not blindly retry every model request because retries can increase cost and duplicate side effects.

Testing

Test deterministic application behavior independently of the model.

Use abstractions such as IChatService so application tests can provide a fake implementation.

For model behavior, use curated evaluation cases rather than relying only on traditional unit tests.

Conclusion

ASP.NET Core provides a strong boundary for integrating LLM capabilities into enterprise applications.

Keep AI provider details behind application abstractions, validate model output, protect the endpoint, control context and token usage, and treat retrieval and model orchestration as explicit application concerns.

The goal is not simply to “add AI.” The goal is to integrate model capabilities without allowing unpredictable model behavior or provider-specific code to leak throughout the application.

Let's connect

Interested in discussing software architecture, engineering challenges or potential collaboration? Feel free to get in touch.

© 2026 Rajdip Khavad. All rights reserved.