Entity Framework  

SQL Server Vector Search: Benchmarking EF Core Workloads

Vector search has become an important database capability for applications that need semantic search, recommendation systems, document retrieval, and retrieval-augmented generation (RAG).

For .NET developers using SQL Server, vector support is now available directly through Entity Framework Core. EF Core 10 introduced support for SQL Server's vector data type and VECTOR_DISTANCE(). EF Core 11 extends this with VECTOR_SEARCH() integration and vector indexes.

That creates an interesting engineering question:

How does vector search perform when used through real EF Core workloads?

There is no single answer.

Performance depends on:

  • Number of vectors

  • Vector dimensions

  • Search metric

  • Exact versus approximate search

  • Number of requested results

  • Filtering

  • Database hardware

  • Query shape

  • EF Core projection

  • Concurrent requests

  • Network latency

This makes vector search a good candidate for controlled benchmarking.

The goal should not be to claim that one approach is universally faster. Instead, developers should measure the trade-off between exactness, latency, resource consumption, and operational complexity.

SQL Server Vector Search in EF Core

SQL Server 2025 introduced the vector data type for storing embeddings and vector operations. EF Core 10 added provider support for this functionality.

A vector property can be represented in EF Core using SqlVector<float>:

public class Article
{
    public int Id { get; set; }

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

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

    public SqlVector<float> Embedding { get; set; } = null!;
}

The vector column can be configured with a fixed dimension:

protected override void OnModelCreating(
    ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Article>()
        .Property(x => x.Embedding)
        .HasColumnType("vector(1536)");
}

The dimension must match the embedding model being used.

A 1536-dimensional embedding and a 3072-dimensional embedding are not interchangeable simply because both use floating-point values.

Exact Vector Search With VECTOR_DISTANCE

The first workload to benchmark is exact nearest-neighbor search.

EF Core translates EF.Functions.VectorDistance() to SQL Server's VECTOR_DISTANCE() function.

For example:

var queryEmbedding =
    new SqlVector<float>(queryVector);

var results = await context.Articles
    .OrderBy(article =>
        EF.Functions.VectorDistance(
            "cosine",
            article.Embedding,
            queryEmbedding))
    .Take(10)
    .ToListAsync();

Conceptually, SQL Server performs:

Query Vector
     |
     v
Compare Against Stored Vectors
     |
     v
Calculate Distance
     |
     v
Sort
     |
     v
Return Top K

This is an exact search because the database calculates the distance against the relevant rows rather than using an approximate vector index. Microsoft notes that this can become expensive as the number of vectors grows because the database may need to calculate distances across many rows.

Approximate Vector Search With VECTOR_SEARCH

For larger datasets, SQL Server also provides VECTOR_SEARCH().

EF Core 11 exposes this through VectorSearch():

var results = await context.Articles
    .VectorSearch(
        article => article.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(result => result.Distance)
    .Take(10)
    .WithApproximate()
    .ToListAsync();

The important distinction is:

VECTOR_DISTANCE()
        |
        v
Exact distance calculation

versus:

VECTOR_SEARCH()
        |
        v
Vector index
        |
        v
Approximate nearest neighbors

Microsoft documents WithApproximate() as the switch that causes SQL Server to use the vector index for approximate nearest-neighbor search.

Create a Vector Index

Approximate search requires a vector index.

Configure it in the EF Core model:

protected override void OnModelCreating(
    ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Article>()
        .Property(x => x.Embedding)
        .HasColumnType("vector(1536)");

    modelBuilder.Entity<Article>()
        .HasVectorIndex(
            x => x.Embedding,
            "cosine");
}

EF Core can generate the corresponding migration for the vector index.

Conceptually, the database receives an index similar to:

CREATE VECTOR INDEX [IX_Articles_Embedding]
ON [Articles] ([Embedding])
WITH (METRIC = COSINE);

The metric must match the intended similarity calculation.

Exact vs Approximate Search

The most important benchmark comparison is:

CharacteristicExact SearchApproximate Search
EF Core APIVectorDistance()VectorSearch() + WithApproximate()
Index requiredNoYes
AccuracyExact nearest neighborsApproximate
Large datasetsCan become expensiveDesigned for scalable search
Query behaviorDistance calculationVector index search
ComplexityLowerHigher
Benchmark focusAccuracy and baselineLatency and scalability

Microsoft currently marks VECTOR_SEARCH() and vector indexes as experimental, so applications adopting them should account for API and behavior changes.

Build a Real Benchmark Dataset

A useful benchmark should contain more than a few hundred rows.

Create several dataset sizes, for example:

10,000 vectors
50,000 vectors
100,000 vectors
500,000 vectors
1,000,000 vectors

These are benchmark scenarios, not claims about minimum or maximum supported sizes.

The purpose is to observe how query behavior changes as the dataset grows.

For every dataset, keep the vector dimension constant initially.

Then perform a second experiment with different dimensions.

Benchmark Vector Dimensions

Vector dimensions can affect storage, memory, and computational work.

For example:

384 dimensions
768 dimensions
1536 dimensions
3072 dimensions

The benchmark should use embeddings generated by an appropriate model or deterministic test vectors with the same dimensions.

Do not compare two dimensions while also changing:

Database
Hardware
Metric
Dataset Size
Query Count

Too many variables make the results difficult to interpret.

Generate Test Vectors

For infrastructure benchmarking, deterministic vectors can be useful.

For example:

static float[] CreateVector(
    int dimensions,
    int seed)
{
    var random = new Random(seed);
    var vector = new float[dimensions];

    for (int i = 0; i < vector.Length; i++)
    {
        vector[i] =
            (float)(random.NextDouble() * 2 - 1);
    }

    return vector;
}

This allows repeatable tests.

However, deterministic random vectors do not necessarily represent the distribution of embeddings produced by a real model.

For relevance-quality experiments, use actual embeddings.

For infrastructure-only tests, deterministic vectors can make controlled experiments easier.

Benchmark Top-K Values

Do not benchmark only:

TOP 5

Test different result sizes:

Top 1
Top 5
Top 10
Top 25
Top 50
Top 100

The number of requested results can affect execution behavior and resource usage.

For example:

var results = await context.Articles
    .VectorSearch(
        x => x.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(x => x.Distance)
    .Take(10)
    .WithApproximate()
    .ToListAsync();

The position of WithApproximate() matters in the current API: Microsoft documents that it should be called after Take().

Benchmark Different Distance Metrics

SQL Server vector indexes currently support metrics including:

  • Cosine

  • Euclidean

  • Dot product

Microsoft documents these as supported vector-index metrics.

A benchmark should therefore avoid assuming that cosine is always the right choice.

Use the metric appropriate for the embedding model and workload.

For example:

.VectorSearch(
    x => x.Embedding,
    queryEmbedding,
    "cosine")

The metric should remain consistent between index configuration and search.

Benchmark EF Core Query Shape

The database operation is only part of the workload.

EF Core also determines which columns are materialized.

Consider:

var results = await context.Articles
    .VectorSearch(
        x => x.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(x => x.Distance)
    .Take(10)
    .WithApproximate()
    .ToListAsync();

Now compare that with a projection:

var results = await context.Articles
    .VectorSearch(
        x => x.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(x => x.Distance)
    .Take(10)
    .WithApproximate()
    .Select(x => new
    {
        x.Value.Id,
        x.Value.Title,
        x.Distance
    })
    .ToListAsync();

The second query explicitly requests only the fields required by the application.

This can be important when article content is large.

EF Core 11 Vector Projection Change

There is an additional EF Core 11 consideration.

Starting with EF Core 11, SqlVector<T> properties are no longer loaded by default when entities are materialized. Vector columns can still participate in filtering and ordering, but they are excluded from normal entity projections because embeddings can contain hundreds or thousands of floating-point values.

This is important for benchmarking.

A benchmark comparing EF Core 10 and EF Core 11 should account for this behavior change.

Otherwise, differences in network transfer or materialization may be incorrectly attributed to the vector search algorithm.

Measure Query Execution Time

At minimum, collect:

Elapsed Time
Rows Returned
Errors

For production-oriented testing, add:

p50
p95
p99
Throughput
CPU
Memory
Database IO

Avoid relying only on average latency.

A vector query might produce:

p50 = Low
p95 = Moderate
p99 = High

That can be much more important than the average when the API is serving interactive users.

Measure Database Work Separately

Use SQL Server's diagnostic capabilities to inspect:

Logical Reads
CPU Time
Elapsed Time
Execution Plan

This helps determine whether a slow request comes from:

EF Core

or:

SQL Server

or:

Network

or:

Application Materialization

Without this separation, a benchmark can produce a latency number without explaining why it occurred.

Exact Search Baseline

Start with exact search.

The baseline query is:

var exactResults = await context.Articles
    .OrderBy(article =>
        EF.Functions.VectorDistance(
            "cosine",
            article.Embedding,
            queryEmbedding))
    .Take(10)
    .Select(article => new
    {
        article.Id,
        article.Title
    })
    .ToListAsync();

Record the result.

Then run the equivalent approximate search:

var approximateResults =
    await context.Articles
        .VectorSearch(
            article => article.Embedding,
            queryEmbedding,
            "cosine")
        .OrderBy(result => result.Distance)
        .Take(10)
        .WithApproximate()
        .Select(result => new
        {
            result.Value.Id,
            result.Value.Title,
            result.Distance
        })
        .ToListAsync();

Now you have two measurements:

Exact
vs
Approximate

Measure Recall, Not Just Latency

An approximate search benchmark should not only ask:

How fast is it?

It should also ask:

How often does it return the same relevant neighbors as exact search?

For example:

Exact Top 10
      |
      v
Ground Truth

Approximate Top 10
      |
      v
Compare

Calculate a metric such as recall@10:

Recall@10 =
Relevant Results Retrieved
--------------------------
Relevant Results Expected

The exact formula should match the evaluation methodology.

The important principle is that approximate search involves a performance-versus-result-quality trade-off.

Example Result Table

A benchmark report can use:

DatasetSearchTop Kp50p95RPSRecall@K
50KExact10MeasureMeasureMeasure100% baseline
50KApproximate10MeasureMeasureMeasureMeasure
500KExact10MeasureMeasureMeasure100% baseline
500KApproximate10MeasureMeasureMeasureMeasure
1MExact10MeasureMeasureMeasure100% baseline
1MApproximate10MeasureMeasureMeasureMeasure

Do not populate this table with assumed numbers.

The exact results are dependent on the dataset, hardware, vector distribution, SQL Server configuration, and index configuration.

Benchmark Cold and Warm Queries

Database benchmarks should distinguish between:

Cold

and:

Warm

execution.

A cold query may involve:

Disk IO
Cache Population
Plan Compilation

A warm query may benefit from:

Buffer Cache
Existing Query Plan
Already-Loaded Data

Run both where the distinction matters.

Benchmark Concurrent Searches

A single query does not represent a production RAG system.

Run concurrent searches:

1 request
10 requests
50 requests
100 requests

Measure:

Throughput
p95
p99
CPU
Memory
Database waits
Connection pool behavior

The database may behave very differently under concurrent vector searches.

Avoid Creating One DbContext Per Vector Comparison

Use normal EF Core lifetime management.

For ASP.NET Core applications, a scoped DbContext is commonly used:

builder.Services.AddDbContext<AppDbContext>(
    options =>
        options.UseSqlServer(connectionString));

Do not create an unrealistic benchmark architecture simply to produce a faster number.

The benchmark should resemble the production application's database access pattern.

Use AsNoTracking for Read-Only Search

Vector search is often read-only.

For result queries that do not need change tracking:

var results = await context.Articles
    .AsNoTracking()
    .VectorSearch(
        x => x.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(x => x.Distance)
    .Take(10)
    .WithApproximate()
    .Select(x => new
    {
        x.Value.Id,
        x.Value.Title,
        x.Distance
    })
    .ToListAsync();

This avoids unnecessary EF Core change-tracking work.

Benchmark with and without AsNoTracking() if you want to understand its contribution in your application.

Do Not Load Embeddings Unnecessarily

Embeddings can be large.

Suppose:

1536 dimensions

are stored as single-precision floating-point values.

The vector contains many values that the application often does not need to send back to the API client.

EF Core 11 addresses this by not loading SqlVector<T> properties by default during entity materialization.

Explicit projections are still a good practice:

.Select(x => new
{
    x.Value.Id,
    x.Value.Title,
    x.Distance
})

Return only what the application needs.

Benchmark Hybrid Search

Vector search is not necessarily a replacement for traditional text search.

SQL Server also provides full-text search capabilities.

EF Core supports SQL Server full-text search functions, including table-valued functions that expose ranking information.

A hybrid search can combine:

Keyword Search
+
Vector Search

The resulting architecture is:

User Query
    |
    +----------+
    |          |
    v          v
Full Text   Vector Search
    |          |
    +----+-----+
         |
         v
     Rank Fusion
         |
         v
      Top Results

Reciprocal Rank Fusion

One documented approach is Reciprocal Rank Fusion, or RRF.

Conceptually:

RRF Score =
1 / (k + TextRank)
+
1 / (k + VectorRank)

EF Core's current SQL Server vector-search documentation demonstrates combining full-text and vector results through a full join and calculating an RRF score.

A simplified LINQ pattern is:

var results = await context.Articles
    .FreeTextTable<Article, int>(
        textualQuery,
        topN: 20)
    .Join(
        context.Articles,
        fts => fts.Key,
        article => article.Id,
        (fts, article) => new
        {
            Article = article,
            fts.Rank
        })
    .FullJoin(
        context.Articles
            .VectorSearch(
                article => article.Embedding,
                queryEmbedding,
                "cosine")
            .OrderBy(x => x.Distance)
            .Take(20)
            .WithApproximate(),
        fts => fts.Article.Id,
        vector => vector.Value.Id,
        (fts, vector) => new
        {
            Article =
                fts != null
                    ? fts.Article
                    : vector.Value,

            FullTextRank =
                fts == null
                    ? null
                    : (int?)fts.Rank,

            VectorDistance =
                vector == null
                    ? null
                    : (double?)vector.Distance
        })
    .ToListAsync();

The exact production query should be optimized and validated against the actual SQL generated by EF Core.

Why Hybrid Search Matters

Keyword search is strong when the user enters:

"EF Core 11"

while semantic search may be better for:

"How can I prevent large embeddings from being returned?"

A hybrid system can combine both signals.

However, hybrid search also introduces additional query work.

Therefore, benchmark:

Vector Only
Full Text Only
Hybrid

rather than assuming hybrid search is always worth its additional complexity.

Benchmark SQL Generation

Do not assume the LINQ query translates exactly as expected.

Inspect generated SQL:

var query = context.Articles
    .VectorSearch(
        x => x.Embedding,
        queryEmbedding,
        "cosine")
    .OrderBy(x => x.Distance)
    .Take(10)
    .WithApproximate();

Console.WriteLine(
    query.ToQueryString());

The generated SQL can reveal whether the expected SQL Server vector functionality is being used.

Microsoft documents that the approximate EF Core query translates to SQL Server's VECTOR_SEARCH() table-valued function and WITH APPROXIMATE syntax.

Benchmark the Database Directly

EF Core should not be the only benchmark.

Run the equivalent SQL directly against SQL Server.

This creates:

Benchmark A
SQL Server Direct

Benchmark B
EF Core

Difference
=
ORM + Application Overhead

If SQL Server takes 20 ms but the complete API takes 80 ms, vector search itself is probably not responsible for the entire latency.

This is one of the most useful diagnostic comparisons.

Common Mistakes

Comparing Different Embedding Dimensions

Keep dimensions consistent when comparing search strategies.

Measuring Only Average Latency

Include p95 and p99.

Ignoring Recall

Approximate search is a quality-performance trade-off.

Loading Complete Entities

Project only required fields.

Ignoring Generated SQL

Always inspect the SQL for important benchmark queries.

Benchmarking Only One Dataset Size

Scalability is the primary reason to compare exact and approximate approaches.

Ignoring Concurrent Requests

Single-user performance does not represent production load.

Mixing Embedding Generation Into Database Benchmarks

Embedding generation is a separate workload.

Measure:

Embedding Generation

and:

Vector Search

independently.

Treating Experimental APIs as Stable

Microsoft currently marks VECTOR_SEARCH() and vector indexes as experimental.

Returning Embeddings to the API

Applications generally need the search results, not the full vector data.

Troubleshooting

The Vector Column Does Not Exist

Check that the migration created the correct column type:

vector(1536)

and that the dimension matches the generated embeddings.

Vector Search Throws a Dimension Error

Verify:

Embedding Model
Vector Dimension
Database Column Dimension
Query Vector Dimension

All must be compatible.

Approximate Search Is Not Using the Vector Index

Verify:

Vector Index Exists
Correct Metric
Take() Present
WithApproximate() Present

Microsoft documents that WithApproximate() is required to request approximate nearest-neighbor behavior and that it should follow Take().

Results Are Slower Than Expected

Inspect:

Execution Plan
Logical Reads
CPU
Dataset Size
Vector Index
Concurrency

Do not assume the ORM is responsible.

EF Core Returns Unexpectedly Large Results

Use projection:

.Select(x => new
{
    x.Value.Id,
    x.Value.Title,
    x.Distance
})

This also makes the intended API payload explicit.

Exact Search Is Acceptable

That may be the correct architecture.

SQL Server's documentation recommends exact search for smaller vector sets, with a general recommendation around fewer than 50,000 vectors, while larger datasets may benefit from approximate search. The actual threshold should still be validated against the application's workload.

Production Benchmark Matrix

A serious evaluation can use:

DimensionValues
Dataset10K / 50K / 500K / 1M
DimensionsModel-dependent
SearchExact / Approximate
Top K1 / 5 / 10 / 50
MetricCosine / Euclidean / Dot
Concurrency1 / 10 / 50 / 100
Query stateCold / Warm
APIDirect DB / EF Core
Search modeVector / Full Text / Hybrid
Metricsp50 / p95 / p99 / RPS / CPU / IO

This produces a benchmark that can actually guide architecture decisions.

Recommended Architecture

A practical RAG architecture using SQL Server and EF Core can look like:

User Question
      |
      v
Embedding Generator
      |
      v
Query Embedding
      |
      v
EF Core
      |
      v
SQL Server Vector Search
      |
      +---- Vector Results
      |
      +---- Optional Full-Text Results
      |
      v
Rank / Filter
      |
      v
Top Documents
      |
      v
LLM
      |
      v
Response

The database remains responsible for retrieval.

The application remains responsible for orchestration.

The model remains responsible for generating the final response.

Best Practices

  1. Benchmark exact and approximate search separately.

  2. Use the same embedding dimensions when comparing algorithms.

  3. Measure recall alongside latency for approximate search.

  4. Test multiple dataset sizes.

  5. Test multiple Top-K values.

  6. Measure p50, p95, and p99 latency.

  7. Test concurrent requests.

  8. Inspect generated SQL.

  9. Compare EF Core execution with direct SQL.

  10. Use projections instead of loading unnecessary columns.

  11. Use AsNoTracking() for read-only workloads where appropriate.

  12. Do not regenerate embeddings during a database benchmark.

  13. Benchmark cold and warm database behavior separately.

  14. Evaluate vector-only and hybrid search independently.

  15. Keep vector-index metrics aligned with the intended similarity calculation.

  16. Treat current VECTOR_SEARCH() and vector-index APIs as experimental.

  17. Record exact .NET, EF Core, SQL Server, OS, and hardware versions.

  18. Do not publish benchmark numbers without the dataset and methodology.

Frequently Asked Questions

Does EF Core support SQL Server vector search?

Yes. EF Core 10 introduced support for SQL Server's vector type and VECTOR_DISTANCE(). EF Core 11 adds VectorSearch() and vector-index configuration.

What is the difference between VECTOR_DISTANCE and VECTOR_SEARCH?

VECTOR_DISTANCE() calculates the distance between vectors and can be used for exact nearest-neighbor queries.

VECTOR_SEARCH() searches a collection of vectors and can use a vector index for approximate nearest-neighbor search when WithApproximate() is specified.

Is approximate vector search always faster?

It is designed to improve performance for large datasets, but the actual result depends on the data, index configuration, hardware, concurrency, and query shape. Benchmark it rather than assuming a fixed improvement.

Does approximate search return the same results as exact search?

Not necessarily.

Approximate nearest-neighbor search trades some exactness for search efficiency. That is why recall should be measured alongside latency.

What vector metrics does SQL Server support?

The current EF Core documentation lists cosine, Euclidean, and dot-product metrics for vector indexes.

Should embeddings be returned from EF Core queries?

Usually not.

Applications generally need the matching document and similarity information rather than the complete embedding. EF Core 11 also changed vector-property loading so vectors are not included in normal entity materialization by default.

Can SQL Server vector search be combined with full-text search?

Yes.

EF Core's SQL Server provider supports combining full-text search and vector search, including a Reciprocal Rank Fusion approach for combining rankings.

Is VECTOR_SEARCH production-ready?

The current Microsoft documentation marks VECTOR_SEARCH() and vector indexes as experimental and subject to change. Teams should therefore validate the specific EF Core and SQL Server versions used in production.

How large should the vector dataset be before using approximate search?

There is no universal cutoff.

Microsoft's SQL Server documentation gives a general recommendation to use exact search when there are fewer than about 50,000 vectors, but the actual decision should be based on measured latency, resource usage, and relevance requirements.

Conclusion

SQL Server's native vector capabilities give .NET developers an opportunity to keep structured data, application data, and semantic search within the same database platform.

EF Core makes that capability accessible through LINQ:

C#
 |
 v
EF Core
 |
 v
SQL Server
 |
 +---- VECTOR_DISTANCE()
 |
 +---- VECTOR_SEARCH()
 |
 +---- Vector Index

The most important benchmarking distinction is between exact and approximate search.

Exact search provides a useful ground-truth baseline:

Query Vector
     |
     v
Compare Candidates
     |
     v
Exact Top K

Approximate search introduces an index-assisted retrieval path:

Query Vector
     |
     v
Vector Index
     |
     v
Approximate Top K

The correct choice depends on the workload.

A meaningful benchmark should therefore measure:

Dataset Size
+
Vector Dimensions
+
Top K
+
Search Metric
+
Concurrency
+
Latency
+
Throughput
+
CPU
+
IO
+
Recall

It should also separate EF Core overhead from SQL Server execution and separate vector-search latency from embedding-generation and LLM latency.

The most important lesson is:

Do not choose exact or approximate vector search based on a generic performance claim. Establish exact search as the relevance baseline, measure approximate search against it, and choose the architecture based on the latency, scalability, resource consumption, and retrieval quality your application actually requires.