AI Engineering

Embeddings and Semantic Search for ASP.NET Core Developers

Afzal AhmedFaz Ahmed
·20 August 2026·29 min read
ASP.NET CoreEmbeddingsVector SearchSemantic SearchHybrid SearchAzure AI SearchMicrosoft.Extensions.AIRAG

Why This Matters

A practical guide to embeddings, vector similarity, HNSW, Top-K, thresholds, metadata security filters, semantic ranking and hybrid search with ASP.NET Core and Azure AI Search.

Embeddings and Semantic Search for ASP.NET Core Developers

How meaning becomes searchable—and why retrieval still needs engineering judgement

Context engineering asks us to give a model the smallest trustworthy collection of information needed for one task. That creates an immediate practical problem.

If a company has millions of document sections, database records and knowledge articles, how does an ASP.NET Core application find the five pieces relevant to the user's question?

It cannot send everything to the model. It needs retrieval.

Embeddings and vector search make it possible to retrieve content by related meaning, even when the user's wording differs from the source. Keyword search remains essential for exact terms. Hybrid search combines both. Metadata filtering keeps results inside the correct tenant, project, document version and permission scope.

This guide develops those ideas slowly, then joins them into a realistic BuildEstate Pro example. BuildEstate Pro is my public enterprise property-development case study. The search architecture described here is a proposed teaching extension rather than a claim that this feature already exists in the repository.

The central lesson is:

Embeddings do not answer a question. They help the application locate information with related meaning so that useful evidence can be supplied to the model.

1. Why literal words are sometimes not enough

Imagine a planning document contains this sentence:

Construction vehicles must not enter the development from the western road.
The user asks:
Are there any restrictions on lorry access?
The ideas are closely related, but the phrases differ:
User wordingDocument wording
lorryconstruction vehicle
restrictionsmust not
accessenter
A basic keyword search may miss or undervalue the passage because it mainly compares terms. A meaning-based search can recognise that the concepts are related.

This does not make keyword search obsolete. Exact identifiers, legal references, error codes and names are often served better by lexical matching. Production retrieval normally combines techniques instead of declaring one universally superior.


2. What is an embedding?

An embedding is a numerical representation produced by an embedding model. For text, it represents aspects of the text's meaning as an ordered list of floating-point values.

Conceptually:

"Approval of the drainage strategy is required before construction"
                         ↓ embedding model
[0.017, -0.248, 0.761, 0.034, ... many more values ...]

That list is a vector.

The individual positions do not normally have simple labels such as “drainage” or “planning.” Meaning is represented by the overall pattern across many dimensions.

Texts with related meanings tend to occupy nearby regions in the embedding space. The application can therefore compare the vector for a question with vectors stored for document chunks.

An embedding model does not normally write the answer. Its output is the vector. A generative model has a different role: it uses retrieved evidence to explain, summarise or reason within the application's constraints.


3. A vector is an ordinary data structure with an unusual meaning

C# developers already understand ordered numeric data.

ReadOnlyMemory<float> vector =
    new float[] { 0.017f, -0.248f, 0.761f, 0.034f };

Real embeddings commonly contain hundreds or thousands of dimensions. Each vector represents a point in a high-dimensional mathematical space.

Humans can draw two dimensions and imagine three. A search engine can calculate across thousands without needing to visualise them.

Think of a meaning-organised library. Cricket books are near one another, football books form another neighbourhood and software engineering sits elsewhere. A book about AI analysis in cricket may sit between the cricket and AI regions. An embedding model produces the coordinates; vector search finds nearby coordinates.

The analogy is useful, but remember that the space is learned from data and mathematics—not a hand-labelled map of every concept.


4. Generate embeddings through a .NET abstraction

Microsoft.Extensions.AI provides IEmbeddingGenerator, a provider-neutral abstraction similar in spirit to IChatClient.

using Microsoft.Extensions.AI;

public sealed class TextEmbeddingService(
    IEmbeddingGenerator<string, Embedding<float>> generator)
{
    public Task<ReadOnlyMemory<float>> GenerateAsync(
        string text,
        CancellationToken cancellationToken)
        => generator.GenerateVectorAsync(text, cancellationToken: cancellationToken);
}

For batches, generate several vectors in one operation when supported.

GeneratedEmbeddings<Embedding<float>> embeddings =
    await generator.GenerateAsync(
        chunks.Select(chunk => chunk.Text),
        cancellationToken: cancellationToken);

The concrete generator may use an approved Azure-hosted model, another provider or a local implementation. Keep the provider configuration in infrastructure and expose business-focused services to application code.

Embedding calls still need cancellation, timeouts, rate control, telemetry and privacy review. Numerical output does not remove the fact that source text was sent to a model service.


5. Documents are prepared before users search

In a typical RAG ingestion flow:

  1. locate an authorised source document;
  2. extract and clean its text;
  3. split it into meaningful chunks;
  4. enrich each chunk with metadata;
  5. generate an embedding for each chunk;
  6. store text, vector and metadata in a searchable index.
Planning document
      ↓ extract
Clean structured text
      ↓ chunk
Section 1  Section 2  Section 3 ...
      ↓ embed each
Vector 1   Vector 2   Vector 3 ...
      ↓ index
Text + vector + source + version + permissions

Why not embed the whole document once?

A planning report may discuss drainage, highways, noise, landscaping, ecology and working hours. One document-level vector blends these subjects. Chunk-level vectors let retrieval return the exact section relevant to the question.

Chunking is not a mechanical “every 500 characters” decision. A useful chunk should preserve enough surrounding meaning to stand on its own. The next complete ingestion guide will explore headings, boundaries, overlap, tables, OCR and versioning in detail.


6. Store the vector with human-readable content and provenance

A useful search record includes much more than an array of numbers.

public sealed record SearchChunk(
    string ChunkId,
    string Text,
    ReadOnlyMemory<float> Vector,
    string TenantId,
    Guid ProjectId,
    string DocumentId,
    int DocumentVersion,
    int? PageNumber,
    string Section,
    string DocumentType,
    string SecurityClassification,
    DateTimeOffset IndexedAt,
    string EmbeddingModel,
    int EmbeddingDimensions,
    string ChunkingVersion);

The text is returned to the user or generative model. Metadata provides citations, deterministic filtering, freshness checks and deletion. Model and chunking versions make re-indexing manageable.

A vector store is a search-optimised knowledge layer, not automatically the source of truth. SQL Server can remain authoritative for project state and transactions while Azure AI Search contains selected representations for retrieval.


7. The query is embedded at runtime

When a user asks:

Can construction start before the drainage plan is approved?
the application:
  1. creates an embedding for the question;
  2. searches for nearby stored vectors;
  3. applies metadata and security filters;
  4. returns the original chunk text and provenance;
  5. decides whether the matches are good enough;
  6. supplies approved evidence to the generative model.
The source might say:
Approval of the drainage strategy is required before construction begins.
Different words, related meaning.
public sealed class PlanningEvidenceRetriever(
    IEmbeddingGenerator<string, Embedding<float>> embeddings,
    IPlanningVectorSearch search)
{
    public async Task<IReadOnlyList<RetrievedChunk>> RetrieveAsync(
        PlanningSearchRequest request,
        CancellationToken cancellationToken)
    {
        var queryVector = await embeddings.GenerateVectorAsync(
            request.Question,
            cancellationToken: cancellationToken);

        return await search.FindAsync(
            queryVector,
            new PlanningSearchFilter(
                request.TenantId,
                request.ProjectId,
                CurrentVersionsOnly: true,
                request.AccessGroups),
            cancellationToken);
    }
}

The metadata filter is part of the search contract, not an optional cleanup performed after confidential text has already been returned.


8. Use the same compatible embedding space

Stored chunk vectors and query vectors must be comparable. In practice, use the same embedding model and dimensional configuration for both.

Coordinates from two unrelated maps cannot be compared meaningfully. Likewise, vectors from incompatible models do not share a reliable geometry.

Store:

  • embedding model and deployment identifier;
  • dimensions;
  • generation date;
  • source content hash and version;
  • preprocessing version;
  • chunking strategy version.
public sealed record EmbeddingProfile(
    string ModelId,
    int Dimensions,
    string PreprocessingVersion,
    string ChunkingVersion);

Changing the model or dimensions usually means generating new document embeddings and rebuilding or migrating the index. Treat this as versioned data infrastructure, not a hidden configuration toggle.

A safe migration can build a new index alongside the old one, evaluate it, switch reads gradually and retain rollback until confidence is established.


9. Similarity measures relatedness, not truth

Vector search uses a similarity or distance measure. Cosine similarity is a common example: it considers how similarly two vectors point within the embedding space.

You do not need to implement the mathematics yourself to use it responsibly. You do need to understand what the score can and cannot claim.

If the user asks:

Has the drainage condition been approved?
search may retrieve:
The drainage condition must be approved before construction.
The passage is highly related. It does not prove that approval occurred.

The application may need a current SQL query or later approval notice.

Similarity score says: this content appears related.
It does not say: this statement is current, permitted or correct.

Vector relevance is one signal inside a retrieval and validation pipeline.


10. Exact search and approximate nearest-neighbour search

With 100 vectors, a search system can compare the query with every vector. This is exhaustive K-nearest-neighbour search: calculate all distances and return the closest k items.

With 200 million vectors, repeating a full comparison for every request is expensive. Approximate nearest-neighbour algorithms use an index to navigate toward promising regions without checking everything.

Imagine searching for a house:

United Kingdom
   → London
      → West London
         → town
            → street
               → matching property

The search avoids obviously distant areas.

Azure AI Search supports:

  • exhaustive KNN, which checks the vector space and provides exact nearest neighbours;
  • HNSW, a graph-based approximate nearest-neighbour algorithm designed for high-recall, low-latency search at scale.
HNSW builds additional in-memory graph structures and trades a small possibility of missing the mathematically nearest item for much faster scalable retrieval. Its parameters affect recall, latency, memory and index footprint.

Exhaustive search is useful for smaller datasets and for creating a ground-truth set against which to measure approximate recall.

Approximate does not mean careless. It means deliberately trading exhaustive comparison for scalable performance, then measuring whether retrieval quality meets the business need.

11. Top-K controls how many candidates return

K is simply the requested number of nearest results.

For “Why is project 152 delayed?”, retrieval might rank:

  1. unapproved drainage strategy;
  2. outstanding highways approval;
  3. incomplete environmental survey;
  4. landscaping condition;
  5. working-hours restriction;
  6. bicycle storage requirement.
If k = 1, the final answer may mention drainage and miss two other material causes.

If k = 50, the model may receive the three useful chunks plus considerable noise. That adds cost and latency, and can distract generation.

There is no universal best K.

Evaluate by use case:

  • simple policy lookup may need three chunks;
  • risk analysis may need five or ten;
  • broad research may retrieve a larger candidate set and rerank it to a smaller final set.
With Azure AI Search semantic ranking over vector results, current guidance recommends supplying k = 50 candidates so the semantic ranker has sufficient input. That is a platform-specific candidate-stage recommendation, not a rule that all 50 chunks should be passed to the language model.

12. Nearest does not always mean relevant enough

A search engine can return the closest results even when every result is poor.

Ask a planning index:

Which medicine should I take for a headache?
Something must still rank first unless the application rejects weak matches.

A similarity threshold or relevance rule can say: accept a result only when it is strong enough for this domain and query type.

var accepted = candidates
    .Where(item => item.Score >= evaluatedThreshold)
    .Take(finalEvidenceLimit)
    .ToList();

if (accepted.Count == 0)
    return RetrievalOutcome.InsufficientEvidence;

Do not copy a score threshold from a tutorial. Score behaviour varies with model, dimensions, metric, search product, chunking, domain and query style. Hybrid RRF scores also have a different scale from raw vector similarity scores.

A high threshold creates false negatives by discarding useful evidence. A low threshold creates false positives by accepting noise. Choose it with labelled evaluation queries.


13. Keyword, vector and semantic ranking are different layers

The phrase “semantic search” is sometimes used broadly for any meaning-aware retrieval. In Azure AI Search, it helps to distinguish the mechanisms precisely.

Keyword or full-text search

Uses textual terms and BM25 ranking. It is strong for:

  • planning reference numbers;
  • invoice and customer IDs;
  • postcodes;
  • legal clause numbers;
  • product and error codes;
  • exact names and specialist terms.

Vector search

Compares embeddings and finds semantically related content even when words differ.

Semantic ranker

Applies language-understanding models as a secondary ranking stage over an initial candidate set. It can promote results that better match the query's intent.

Vector search and semantic ranker are not synonyms. A system can use vector search without semantic ranking, keyword search with semantic ranking, or a hybrid candidate set followed by semantic ranking.


14. Hybrid search combines exactness and meaning

Consider:

What access restrictions apply to planning application PLN-2026-00452?
Keyword search can strongly locate the exact reference. Vector search can find sections discussing vehicle movements, delivery routes, entrances and construction traffic.

Azure AI Search can run keyword and vector queries in parallel and merge their ranked lists using Reciprocal Rank Fusion (RRF). An optional semantic-ranker stage can then reorder the combined candidates.

Question
   ├── keyword/BM25 search ──┐
   └── vector search ────────┤
                              ↓
                     Reciprocal Rank Fusion
                              ↓
                 optional semantic reranking
                              ↓
                  filtered evidence candidates

Hybrid search is often a strong enterprise starting point because business data contains both natural language and exact identifiers.

Do not interpret a low-looking RRF score as a weak vector similarity score. It comes from a different ranking method and scale.


15. A representative Azure AI Search index

The exact SDK evolves, but the index design principles are stable.

{
  "name": "planning-chunks-v1",
  "fields": [
    { "name": "chunkId", "type": "Edm.String", "key": true, "filterable": true },
    { "name": "content", "type": "Edm.String", "searchable": true },
    { "name": "contentVector", "type": "Collection(Edm.Single)", "searchable": true, "dimensions": 1536, "vectorSearchProfile": "planning-hnsw" },
    { "name": "tenantId", "type": "Edm.String", "filterable": true },
    { "name": "projectId", "type": "Edm.String", "filterable": true },
    { "name": "documentId", "type": "Edm.String", "filterable": true },
    { "name": "documentVersion", "type": "Edm.Int32", "filterable": true },
    { "name": "isCurrent", "type": "Edm.Boolean", "filterable": true },
    { "name": "accessGroups", "type": "Collection(Edm.String)", "filterable": true },
    { "name": "pageNumber", "type": "Edm.Int32", "filterable": true },
    { "name": "section", "type": "Edm.String", "searchable": true }
  ]
}

The dimension value must match the selected embedding configuration. Security and version fields are filterable. Human-readable content remains searchable and retrievable; vector fields normally do not need to be returned to the user.

Index names and aliases can carry schema versions so migrations do not overwrite the live index blindly.


16. Apply deterministic filters with semantic retrieval

Suppose a user is viewing project 152 and asks about drainage restrictions. The globally closest chunk could belong to another tenant's project 829.

The intended search is:

Find meaning-related drainage content, but only for this tenant, project, current document versions and authorised access groups.
tenantId = authenticated tenant
AND projectId = authorised route project
AND isCurrent = true
AND accessGroups contains one of caller's groups

Permissions must never be decided by vector similarity.

Be careful with filter mode. Prefiltering narrows the vector search surface before nearest-neighbour selection. Post-filtering removes items after candidate selection and can leave fewer useful results. For security trimming, ensure the chosen query structure applies mandatory access filters consistently to every relevant retrieval branch.

Authorization should be testable without a model call.


17. Rerank candidates before giving evidence to the model

Retrieval often optimises for recall: collect enough possible evidence not to miss the answer. Generation benefits from precision: a small set of highly relevant chunks.

Retrieve 30–50 candidates
          ↓
Apply metadata and security rules
          ↓
Rerank against the actual question
          ↓
Deduplicate related chunks
          ↓
Give the best 5–8 evidence chunks to the model

Semantic ranking adds latency and may add cost, so evaluate the improvement. It is useful when the initial set contains plausible but marginally relevant results.

Keep source identifiers and scores through every stage. If the final answer is poor, you need to know whether the right chunk was never retrieved, retrieved but ranked too low, or supplied and ignored by the model.


18. Evaluate retrieval separately from answer generation

A RAG answer can fail before generation begins.

Create a labelled evaluation set:

public sealed record RetrievalTestCase(
    string Question,
    string TenantId,
    Guid ProjectId,
    IReadOnlySet<string> ExpectedRelevantChunkIds,
    IReadOnlySet<string> ForbiddenChunkIds);

Measure:

  • Recall@K: how many expected relevant chunks appeared in the top K?
  • Precision@K: how many returned chunks were actually useful?
  • Mean reciprocal rank: how early did the first relevant result appear?
  • Latency: how long did retrieval and reranking take?
  • Security correctness: did any forbidden chunk appear?
  • Freshness correctness: were superseded versions excluded?
Use exhaustive KNN on a representative subset to estimate ground truth and compare HNSW recall.

Then test generation separately:

Was the required evidence retrieved?
  no  → retrieval/index/chunking problem
  yes → Did the model use it correctly?
          no  → prompt/model/context problem
          yes → Did the application validate and present it correctly?

This is more actionable than calling every poor answer a hallucination.


19. Protect embeddings as derived business data

An embedding looks like an unreadable number array. It is still derived from the original content.

Before embedding business data, ask:

  • Is the source authorised for this purpose?
  • Does it contain unnecessary personal or confidential information?
  • Is the provider and processing region approved?
  • What are the retention terms?
  • Which identities can query the index?
  • How will deletion and access changes propagate?
  • Are logs exposing source text or vectors?
The vector representation should inherit the source's security classification and lifecycle.
Source document deleted
      ↓
Remove extracted text
      ↓
Remove chunks and vectors
      ↓
Invalidate caches
      ↓
Record deletion completion

Deleting a PDF while retaining all derived chunks and embeddings is not a complete deletion design.


20. Embeddings beyond RAG

Embeddings support many similarity problems.

Similar-case discovery

BuildEstate Pro could find earlier developments with related combinations of drainage, highways and environmental issues.

Recommendations

A developer reading an ASP.NET Core performance article could receive related material about caching, SQL optimisation and async I/O.

Duplicate detection

Near-identical policies or documents can be flagged before redundant indexing.

Clustering

Customer messages may group naturally into payments, complaints, login problems and cancellation requests.

Suggested classification

A chunk can be compared with example category vectors such as drainage, financial, environmental and highways risk.

Similarity can suggest. Deterministic rules and human judgement still decide where consequences matter.


21. Complete BuildEstate Pro flow

A current planning document for project 152 is uploaded.

The ingestion pipeline extracts and chunks it. One chunk says:

Construction vehicles must not enter the development from the western road.
The embedding model converts the chunk into a vector. The index stores:
  • original text;
  • vector;
  • tenant and project;
  • document ID and version;
  • page and section;
  • access groups;
  • current-version flag;
  • embedding and chunking profile.
Later, an authorised user asks:
Can our delivery lorries use the west entrance?
ASP.NET Core:
  1. authorises the project;
  2. embeds the question with the compatible model;
  3. runs keyword and vector retrieval;
  4. filters by tenant, project, version and access group;
  5. reranks and rejects weak results;
  6. retrieves the western-road passage;
  7. supplies it to the generative model with its citation;
  8. validates that the answer refers to supplied evidence.
The final explanation might be:
No. The current planning condition states that construction vehicles must not enter from the western road. The delivery route should be changed or reviewed through the appropriate planning process.
Each component did a different job:
  • the embedding model represented meaning;
  • keyword search preserved exact matching;
  • vector search found related wording;
  • metadata filters protected scope;
  • semantic ranking improved candidate order;
  • the generative model explained evidence;
  • ASP.NET Core controlled authorization, validation and the workflow.

22. Practical implementation checklist

Embedding profile

  • Record model, dimensions, preprocessing and chunking versions.
  • Use compatible document and query embeddings.
  • Plan re-embedding and index migration before changing the model.

Index design

  • Store original text and provenance with every vector.
  • Include filterable tenant, project, version and access metadata.
  • Keep SQL Server or another operational system as the source of truth.

Retrieval

  • Combine keyword and vector retrieval where the domain benefits.
  • Evaluate HNSW recall against exhaustive search on representative data.
  • Tune K, candidate size, thresholds and reranking with labelled queries.
  • Do not confuse vector, RRF and semantic-ranker score scales.

Security

  • Authorise before search.
  • Apply mandatory filters consistently.
  • Preserve source classification and deletion lifecycle.
  • Treat retrieved text as untrusted data when it enters the model context.

Operations

  • Observe model/profile version, filters, scores, latency and result IDs.
  • Diagnose retrieval separately from generation.
  • Monitor index freshness and failed ingestion.
  • Avoid logging confidential chunks and raw vectors by default.

23. What an ASP.NET Core developer should remember

Embeddings are not mysterious intelligence trapped in decimals. They are search representations generated for a purpose.

The difficult production work lies in the boundaries around them:

  • deciding what should be embedded;
  • preserving document structure;
  • choosing chunks and metadata;
  • keeping models and dimensions compatible;
  • combining retrieval approaches;
  • enforcing tenant and permission filters;
  • evaluating recall and precision;
  • versioning, refreshing and deleting derived data;
  • proving that retrieved evidence is relevant enough to use.
My current focus on Microsoft Foundry, Azure AI Search and RAG builds directly on established .NET, SQL, API, security and business-workflow experience. The search index is new infrastructure; the need for trustworthy data boundaries is not.

The final mental model

Offline preparation
Document → extract → chunk → embed → index text + vector + metadata

Runtime retrieval
Question → embed → keyword + vector search → filter → rerank
         → accept useful evidence → give evidence to generative model

Application control
ASP.NET Core authorises, versions, filters, validates, observes and deletes
Vector search finds the nearest available content, but your application must decide whether that content is relevant enough, permitted, current and safe to use.

Continue learning

Current technical references

If this guide helped you understand embeddings and semantic retrieval from an ASP.NET Core perspective, I would be pleased to hear from you. For mentoring, architecture discussion or collaboration, contact dotnetdeveloper20xx@hotmail.com.

Applied In

The thinking in this article has been applied throughout my enterprise portfolio, where architecture, workflows, permissions, notifications, reporting and modular design are all built around real business operations rather than isolated technical features.

View BuildEstate Pro →
Afzal Ahmed

Faz Ahmed

Senior Full Stack Engineer & Technical Lead

A hands-on engineer with 15+ years in commercial software. I publish what I am studying, revising and testing so visitors can see both established experience and learning still in progress.

How would you approach this problem? I'd love to hear your thoughts or continue the discussion.

Connect on LinkedIn →