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:
| Characteristic | Exact Search | Approximate Search |
|---|---|---|
| EF Core API | VectorDistance() | VectorSearch() + WithApproximate() |
| Index required | No | Yes |
| Accuracy | Exact nearest neighbors | Approximate |
| Large datasets | Can become expensive | Designed for scalable search |
| Query behavior | Distance calculation | Vector index search |
| Complexity | Lower | Higher |
| Benchmark focus | Accuracy and baseline | Latency 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:
Join the conversation! Your thoughts help the community grow.