Leaders Logo

Beyond Full-Text Search: exploring the potential of SqlVector in the .NET 10 ecosystem

Introduction

Going beyond Full-Text Search (FTS) does not mean abandoning a mature technology, but recognizing that many modern queries rely less on literal matching and more on semantic proximity. When the application needs to understand that “recurrent default” and “frequent payment delays” describe the same phenomenon, embeddings start to complement the classic text index. This advancement stems from the consolidation of dense language representations and the use of neural retrieval in production (DEVLIN et al., 2019) (REIMERS; GUREVYCH, 2019).

In the .NET 10 ecosystem, this movement becomes more practical because EF Core 10 already offers direct support for SQL Server’s vector type. At the application layer, the data appears as SqlVector<float>; in the database, it is persisted as vector(n). The result is a simpler design for scenarios such as semantic search, recommendation, content deduplication, and RAG pipelines, without requiring the entire architecture to be shifted to a specialized database.

Why FTS Alone Is Not Enough

FTS remains excellent for lexical filters, boolean operators, prefixes, textual proximity, and term-based relevance rules. The problem arises when the user asks questions using vocabulary different from the indexed text. Embeddings reduce this gap because they project meaning into a vector space in which semantically similar texts tend to be neighbors (DEVLIN et al., 2019).

In practice, the best answer is rarely “FTS or vectors,” but rather “FTS first, vectors after.” The textual filter reduces the universe of candidates at low cost; the vector distance refines the final ranking for ambiguous, lengthy, or naturally worded questions. This hybrid pattern also aligns well with dense retrieval pipelines used in QA and RAG (KARPUKHIN et al., 2020) (LEWIS et al., 2020).

Modeling in .NET 10 with EF Core 10

The cleanest modeling approach is to keep textual content, metadata, and embedding in the same entity when the application requires transactional consistency and centralized governance. This simplifies auditing, backup, permissions, and domain versioning.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

public sealed class KnowledgeDocument
{
    [Key]
    public Guid Id { get; set; }

    [MaxLength(200)]
    public string Title { get; set; } = string.Empty;

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

    [Column(TypeName = "vector(1536)")]
    public SqlVector<float> Embedding { get; set; } = default!;

    [MaxLength(80)]
    public string Category { get; set; } = string.Empty;
}

public sealed class SearchDbContext(DbContextOptions<SearchDbContext> options)
    : DbContext(options)
{
    public DbSet<KnowledgeDocument> Documents => Set<KnowledgeDocument>();
}

This format is more adherent to the current state of the platform than handcrafted conversions to JSON strings or simulated proprietary types. In .NET 10, it is better to prefer native mapping and let the ORM layer translate vector operations into SQL.

Generation and persistence of embeddings

The generation of embeddings should take place outside of the database, in an application service or ingestion pipeline. This maintains flexibility to switch models, control costs per document, and version reprocessing. In corporate search scenarios, sentence embedding models usually offer a better balance between quality and operational simplicity (REIMERS; GUREVYCH, 2019).

using Microsoft.Extensions.AI;

public sealed class DocumentIngestionService(
    SearchDbContext dbContext,
    IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator)
{
    public async Task<Guid> SaveAsync(string title, string content, string category, CancellationToken cancellationToken = default)
    {
        var embedding = await embeddingGenerator.GenerateVectorAsync(content, cancellationToken: cancellationToken);

        var document = new KnowledgeDocument
        {
            Id = Guid.NewGuid(),
            Title = title,
            Content = content,
            Category = category,
            Embedding = new SqlVector<float>(embedding)
        };

        dbContext.Documents.Add(document);
        await dbContext.SaveChangesAsync(cancellationToken);

        return document.Id;
    }
}

From a governance perspective, it is also recommended to persist the embedding model version and the generation date. When the model changes, comparing old and new vectors may degrade ranking consistency; therefore, broad reindexing should be treated as explicit operational events.

Exact Vector Search with LINQ

For small datasets or previously filtered sets, exact search is usually sufficient. The advantage here is not in "semantic magic," but in exposing a way to rank candidates by the distance between the query vector and the persisted vectors.

public sealed class SemanticSearchService(
    SearchDbContext dbContext,
    IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator)
{
    public async Task<List<KnowledgeDocument>> SearchAsync(string question, CancellationToken cancellationToken = default)
    {
        var queryEmbedding = await embeddingGenerator.GenerateVectorAsync(question, cancellationToken: cancellationToken);
        var queryVector = new SqlVector<float>(queryEmbedding);

        return await dbContext.Documents
            .OrderBy(d => EF.Functions.VectorDistance("cosine", d.Embedding, queryVector))
            .Take(5)
            .ToListAsync(cancellationToken);
    }
}

This pattern solves the main case discussed in the article: moving from pure FTS to a search that is closer to the user's intent, without disrupting the existing relational model.

Hybrid search: textual filter with semantic refinement

In production, the most pragmatic design is usually hybrid. First, FTS is used to ensure thematic filtering and low cost for the initial selection. Then, vector distance reorders the remaining candidates. This arrangement tends to improve perceived accuracy without sacrificing operational predictability (KARPUKHIN et al., 2020).

public async Task<List<KnowledgeDocument>> SearchHybridAsync(
    string question,
    string keywords,
    CancellationToken cancellationToken = default)
{
    var queryEmbedding = await embeddingGenerator.GenerateVectorAsync(question, cancellationToken: cancellationToken);
    var queryVector = new SqlVector<float>(queryEmbedding);

    return await dbContext.Documents
        .FromSql($"""
            SELECT TOP (10) Id, Title, Content, Category, Embedding
            FROM dbo.Documents
            WHERE CONTAINS(Content, {keywords})
            ORDER BY VECTOR_DISTANCE('cosine', Embedding, {queryVector})
            """)
        .ToListAsync(cancellationToken);
}

It is also worth noting a security detail: in .NET 10, it is advisable to prefer EF's parameterized APIs instead of concatenating SQL manually, especially when keywords or filters come from external input.

Vector indexes and operational limits

When the collection grows, the bottleneck shifts from the ORM to the search strategy. Exact search remains useful, but starts costing more CPU and latency. This is where approximate indexes come into play. Conceptually, the approximate nearest neighbors literature shows that graph-based structures can drastically reduce search costs with a controlled loss of recall (MALKOV; YASHUNIN, 2020).

CREATE VECTOR INDEX IX_Documents_Embedding
ON dbo.Documents (Embedding)
WITH (METRIC = 'COSINE', TYPE = 'DISKANN');

In the current SQL Server, this requires attention to the feature’s maturity stage: approximate vector search with an index is the most modern approach in SQL Server 2025 and Azure SQL, while exact search remains the most straightforward option for environments that do not yet use the full set of vector functionalities. In enterprise projects, the choice should not be ideological; it depends on cardinality, SLA, and tolerance for approximation.

DECLARE @queryVector VECTOR(1536) = @p0;

SELECT TOP (10) WITH APPROXIMATE
    d.Id,
    d.Title,
    vs.distance
FROM VECTOR_SEARCH(
        TABLE = dbo.Documents AS d,
        COLUMN = Embedding,
        SIMILAR_TO = @queryVector,
        METRIC = 'cosine'
    ) AS vs
ORDER BY vs.distance;

RAG in .NET 10 without inflating the architecture

The most compelling use of this stack appears in RAG. The database retrieves semantically relevant snippets, the application organizes the context, and the generative model responds based on retrieved evidence. This design reduces hallucination, improves traceability, and keeps retrieval decoupled from the generation step (LEWIS et al., 2020).

public async Task<string> AnswerAsync(string question, CancellationToken cancellationToken = default)
{
    var matches = await SearchAsync(question, cancellationToken);

    var context = string.Join(
        "\n\n",
        matches.Select(d => $"[{d.Title}] {d.Content}"));

    return await chatClient.GetResponseAsync(
        $"""
        Answer the question using only the context below.

        Question: {question}

        Context:
        {context}
        """,
        cancellationToken: cancellationToken);
}

This approach is especially useful in legal databases, knowledge bases, technical manuals, and internal policies, where contextual accuracy is more valuable than the sheer volume of documents.

Governance, security, and maintenance

Embeddings are not neutral in terms of risk. Depending on the pipeline, they may preserve clues about the original data, which requires the same level of discipline applied to the source text: access control, masking when appropriate, audit trails, and retention policies. The security literature on language models has already shown that vector artifacts and models can expose sensitive traces when poorly governed (CARLINI et al., 2021).

Operationally, three practices deserve priority: logging the embedding model version, monitoring latency and hit rate by query type, and rebuilding the vector index after major corpus reprocessing. This prevents the false perception that semantic search “degrades out of nowhere”; almost always there is distribution shift, duplicated data, or poorly calibrated filters behind the drop in quality.

Progressive Migration from Full-Text Search

The safest adoption is incremental. It starts with a subset of documents, measures quality using a controlled set of real questions, and compares the FTS ranking with the hybrid ranking. If the gain is consistent, the corpus is expanded, observability and reindexing policies are introduced, and only then does vector search become part of the main flow. This gradual migration is a better fit for enterprise scenarios than a sudden technology switch.

Conclusion

In summary, the potential of SqlVector in the .NET 10 ecosystem is less about “replacing everything” and more about adding semantics to what already works. FTS remains relevant; the vector comes in to address what the literal term cannot reach. When the solution is well modeled, the gain is not just technical: it appears in the user experience, in the quality of the retrieved context, and in the ability to evolve the platform without disruption. For organizations already operating on SQL Server and .NET, this path offers a pragmatic evolution, with gradual adoption, centralized governance, and better adherence to enterprise search and RAG scenarios.

References

  • DEVLIN, Jacob et al. Bert: Pre-training of deep bidirectional transformers for language understanding. In: Proceedings of the 2019 conference of the North American chapter of the association for computational linguistics: human language technologies, volume 1 (long and short papers). 2019. pp. 4171-4186. reference.Description
  • REIMERS, Nils; GUREVYCH, Iryna. Sentence-BERT: Sentence embeddings using Siamese BERT-networks. In: Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing and the 9th International Joint Conference on Natural Language Processing (EMNLP-IJCNLP). 2019. pp. 3982-3992. reference.Description
  • KARPUKHIN, Vladimir et al. Dense passage retrieval for open-domain question answering. In: Proceedings of the 2020 conference on empirical methods in natural language processing (EMNLP). 2020. pp. 6769-6781. reference.Description
  • LEWIS, Patrick et al. Retrieval-augmented generation for knowledge-intensive NLP tasks. Advances in neural information processing systems, v. 33, p. 9459-9474, 2020. reference.Description
  • MALKOV, Yu A.; YASHUNIN, Dmitry A. Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs. IEEE Transactions on Pattern Analysis and Machine Intelligence, v. 42, n. 4, p. 824-836, 2018. reference.Description
  • CARLINI, Nicholas et al. Extracting training data from large language models. In: 30th USENIX Security Symposium (USENIX Security 21). 2021. p. 2633-2650. reference.Description
About the author