Skip to content

Commit f583fe2

Browse files
authored
Document SQL Server vector search and JSON data type (#5082)
Closes #5081 Closes #4818
1 parent 1e6c840 commit f583fe2

File tree

6 files changed

+273
-117
lines changed

6 files changed

+273
-117
lines changed

entity-framework/core/providers/sql-server/columns.md

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ This page details column configuration options that are specific to the SQL Serv
1111

1212
## Unicode and UTF-8
1313

14-
SQL Server 2019 introduced [introduced UTF-8](/sql/relational-databases/collations/collation-and-unicode-support#utf8) support, which allows storing UTF-8 data in `char` and `varchar` columns by configuring them with special UTF-8 collations.
15-
16-
### [EF Core 7.0](#tab/ef-core-7)
17-
18-
EF Core 7.0 includes first-class support for UTF-8 columns. To configure them, simply configure the column's type to `char` or `varchar`, specify a UTF-8 collation (ending with `_UTF8`), and specify that the column should be Unicode:
14+
SQL Server 2019 introduced [introduced UTF-8](/sql/relational-databases/collations/collation-and-unicode-support#utf8) support, which allows storing UTF-8 data in `char` and `varchar` columns by configuring them with special UTF-8 collations. You can use UTF-8 columns with EF simply by configuring the column's type to `char` or `varchar`, specify a UTF-8 collation (ending with `_UTF8`), and specifying that the column should be Unicode:
1915

2016
```c#
2117
protected override void OnModelCreating(ModelBuilder modelBuilder)
@@ -28,25 +24,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
2824
}
2925
```
3026

31-
#### [Older versions](#tab/older-versions)
32-
33-
In EF Core versions prior to 7.0, UTF-8 columns do not work out-of-the-box with EF Core's SQL Server provider. To map a string property to a `varchar(x)` column, the Fluent or Data Annotation API is typically used to disable Unicode ([see these docs](xref:core/modeling/entity-properties#unicode)). While this causes the correct column type to be created in the database, it also makes EF Core send database parameters in a way which is incompatible with UTF-8 data: `DbType.AnsiString` is used (signifying non-Unicode data), but `DbType.String` is needed to properly send Unicode data.
34-
35-
As a result, you'll have to configure the EF property as a regular `nvarchar` column, ensuring that parameters are sent with the correct `DbType`:
36-
37-
```c#
38-
protected override void OnModelCreating(ModelBuilder modelBuilder)
39-
{
40-
modelBuilder.Entity<Blog>()
41-
.Property(b => b.Name)
42-
.UseCollation("LATIN1_GENERAL_100_CI_AS_SC_UTF8");
43-
}
44-
```
45-
46-
Once you've created the migration for the column, edit it and manually change the column's type from `nvarchar` to `varchar`.
47-
48-
***
49-
5027
## Sparse columns
5128

5229
Sparse columns are ordinary columns that have an optimized storage for null values, reducing the space requirements for null values at the cost of more overhead to retrieve non-null values.

entity-framework/core/providers/sql-server/functions.md

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,25 @@ This page shows which .NET members are translated into which SQL functions when
1313

1414
.NET | SQL | Added in
1515
----------------------------------------------------------------------- | -------------------------------- | --------
16-
EF.Functions.StandardDeviationSample(group.Select(x => x.Property)) | STDEV(Property) | EF Core 7.0
17-
EF.Functions.StandardDeviationPopulation(group.Select(x => x.Property)) | STDEVP(Property) | EF Core 7.0
18-
EF.Functions.VarianceSample(group.Select(x => x.Property)) | VAR(Property) | EF Core 7.0
19-
EF.Functions.VariancePopulation(group.Select(x => x.Property)) | VARP(Property) | EF Core 7.0
16+
EF.Functions.StandardDeviationSample(group.Select(x => x.Property)) | STDEV(Property)
17+
EF.Functions.StandardDeviationPopulation(group.Select(x => x.Property)) | STDEVP(Property)
18+
EF.Functions.VarianceSample(group.Select(x => x.Property)) | VAR(Property)
19+
EF.Functions.VariancePopulation(group.Select(x => x.Property)) | VARP(Property)
2020
group.Average(x => x.Property) | AVG(Property)
2121
group.Count() | COUNT(*)
2222
group.LongCount() | COUNT_BIG(*)
2323
group.Max(x => x.Property) | MAX(Property)
2424
group.Min(x => x.Property) | MIN(Property)
2525
group.Sum(x => x.Property) | SUM(Property)
26-
string.Concat(group.Select(x => x.Property)) | STRING_AGG(Property, N'') | EF Core 7.0
27-
string.Join(separator, group.Select(x => x.Property)) | STRING_AGG(Property, @separator) | EF Core 7.0
26+
string.Concat(group.Select(x => x.Property)) | STRING_AGG(Property, N'')
27+
string.Join(separator, group.Select(x => x.Property)) | STRING_AGG(Property, @separator)
2828

2929
## Binary functions
3030

3131
.NET | SQL | Added in
3232
---------------------------- | ----------------------------- | --------
3333
bytes.Contains(value) | CHARINDEX(@value, @bytes) > 0
34-
bytes.ElementAt(i) | SUBSTRING(@bytes, @i + 1, 1) | EF Core 8.0
34+
bytes.ElementAt(i) | SUBSTRING(@bytes, @i + 1, 1)
3535
bytes.First() | SUBSTRING(@bytes, 1, 1)
3636
bytes.Length | DATALENGTH(@bytes)
3737
bytes.SequenceEqual(second) | @bytes = @second
@@ -53,7 +53,7 @@ Convert.ToInt16(value) | CONVERT(smallint, @value)
5353
Convert.ToInt32(value) | CONVERT(int, @value)
5454
Convert.ToInt64(value) | CONVERT(bigint, @value)
5555
Convert.ToString(value) | CONVERT(nvarchar(max), @value)
56-
dateOnly.ToString() | CONVERT(varchar(100), @dateOnly) | EF Core 8.0
56+
dateOnly.ToString() | CONVERT(varchar(100), @dateOnly)
5757
dateTime.ToString() | CONVERT(varchar(100), @dateTime)
5858
dateTimeOffset.ToString() | CONVERT(varchar(100), @dateTimeOffset)
5959
decimalValue.ToString() | CONVERT(varchar(100), @decimalValue)
@@ -64,7 +64,7 @@ intValue.ToString() | CONVERT(varchar(11), @intValue)
6464
longValue.ToString() | CONVERT(varchar(20), @longValue)
6565
sbyteValue.ToString() | CONVERT(varchar(4), @sbyteValue)
6666
shortValue.ToString() | CONVERT(varchar(6), @shortValue)
67-
timeOnly.ToString() | CONVERT(varchar(100), @timeOnly) | EF Core 8.0
67+
timeOnly.ToString() | CONVERT(varchar(100), @timeOnly)
6868
timeSpan.ToString() | CONVERT(varchar(100), @timeSpan)
6969
uintValue.ToString() | CONVERT(varchar(10), @uintValue)
7070
ulongValue.ToString() | CONVERT(varchar(19), @ulongValue)
@@ -116,19 +116,19 @@ dateTimeOffset.Month | DATEPART(month, @d
116116
dateTimeOffset.Nanosecond | DATEPART(nanosecond, @dateTimeOffset) % 1000 | EF Core 10.0
117117
dateTimeOffset.Second | DATEPART(second, @dateTimeOffset)
118118
dateTimeOffset.TimeOfDay | CONVERT(time, @dateTimeOffset)
119-
dateTimeOffset.ToUnixTimeSeconds() | DATEDIFF_BIG(second, '1970-01-01T00:00:00.0000000+00:00', @dateTimeOffset) | EF Core 8.0
120-
dateTimeOffset.ToUnixTimeMilliseconds() | DATEDIFF_BIG(millisecond, '1970-01-01T00:00:00.0000000+00:00', @dateTimeOffset) | EF Core 8.0
119+
dateTimeOffset.ToUnixTimeSeconds() | DATEDIFF_BIG(second, '1970-01-01T00:00:00.0000000+00:00', @dateTimeOffset)
120+
dateTimeOffset.ToUnixTimeMilliseconds() | DATEDIFF_BIG(millisecond, '1970-01-01T00:00:00.0000000+00:00', @dateTimeOffset)
121121
dateTimeOffset.Year | DATEPART(year, @dateTimeOffset)
122-
DateOnly.FromDateTime(dateTime) | CONVERT(date, @dateTime) | EF Core 8.0
123-
dateOnly.AddDays(value) | DATEADD(day, @value, @dateOnly) | EF Core 8.0
124-
dateOnly.AddMonths(months) | DATEADD(month, @months, @dateOnly) | EF Core 8.0
125-
dateOnly.AddYears(value) | DATEADD(year, @value, @dateOnly) | EF Core 8.0
126-
dateOnly.Day | DATEPART(day, @dateOnly) | EF Core 8.0
127-
dateOnly.DayOfYear | DATEPART(dayofyear, @dateOnly) | EF Core 8.0
128-
dateOnly.Month | DATEPART(month, @dateOnly) | EF Core 8.0
129-
dateOnly.Year | DATEPART(year, @dateOnly) | EF Core 8.0
122+
DateOnly.FromDateTime(dateTime) | CONVERT(date, @dateTime)
123+
dateOnly.AddDays(value) | DATEADD(day, @value, @dateOnly)
124+
dateOnly.AddMonths(months) | DATEADD(month, @months, @dateOnly)
125+
dateOnly.AddYears(value) | DATEADD(year, @value, @dateOnly)
126+
dateOnly.Day | DATEPART(day, @dateOnly)
127+
dateOnly.DayOfYear | DATEPART(dayofyear, @dateOnly)
128+
dateOnly.Month | DATEPART(month, @dateOnly)
129+
dateOnly.Year | DATEPART(year, @dateOnly)
130130
dateOnly.DayNumber | DATEDIFF(day, '0001-01-01', @dateOnly) | EF Core 10.0
131-
EF.Functions.AtTimeZone(dateTime, timeZone) | @dateTime AT TIME ZONE @timeZone | EF Core 7.0
131+
EF.Functions.AtTimeZone(dateTime, timeZone) | @dateTime AT TIME ZONE @timeZone
132132
EF.Functions.DateDiffDay(start, end) | DATEDIFF(day, @start, @end)
133133
EF.Functions.DateDiffHour(start, end) | DATEDIFF(hour, @start, @end)
134134
EF.Functions.DateDiffMicrosecond(start, end) | DATEDIFF(microsecond, @start, @end)
@@ -146,15 +146,15 @@ EF.Functions.DateTimeOffsetFromParts(year, month, day, ...) | DATETIMEOFFSETFROM
146146
EF.Functions.IsDate(expression) | ISDATE(@expression)
147147
EF.Functions.SmallDateTimeFromParts(year, month, day, ...) | SMALLDATETIMEFROMPARTS(@year, @month, @day, ...)
148148
EF.Functions.TimeFromParts(hour, minute, second, ...) | TIMEFROMPARTS(@hour, @minute, @second, ...)
149-
timeOnly.AddHours(value) | DATEADD(hour, @value, @timeOnly) | EF Core 8.0
150-
timeOnly.AddMinutes(value) | DATEADD(minute, @value, @timeOnly) | EF Core 8.0
151-
timeOnly.Hour | DATEPART(hour, @timeOnly) | EF Core 8.0
152-
timeOnly.IsBetween(start, end) | @timeOnly >= @start AND @timeOnly < @end | EF Core 8.0
149+
timeOnly.AddHours(value) | DATEADD(hour, @value, @timeOnly)
150+
timeOnly.AddMinutes(value) | DATEADD(minute, @value, @timeOnly)
151+
timeOnly.Hour | DATEPART(hour, @timeOnly)
152+
timeOnly.IsBetween(start, end) | @timeOnly >= @start AND @timeOnly < @end
153153
timeOnly.Microsecond | DATEPART(microsecond, @timeOnly) % 1000 | EF Core 10.0
154-
timeOnly.Millisecond | DATEPART(millisecond, @timeOnly) | EF Core 8.0
155-
timeOnly.Minute | DATEPART(minute, @timeOnly) | EF Core 8.0
154+
timeOnly.Millisecond | DATEPART(millisecond, @timeOnly)
155+
timeOnly.Minute | DATEPART(minute, @timeOnly)
156156
timeOnly.Nanosecond | DATEPART(nanosecond, @timeOnly) % 1000 | EF Core 10.0
157-
timeOnly.Second | DATEPART(second, @timeOnly) | EF Core 8.0
157+
timeOnly.Second | DATEPART(second, @timeOnly)
158158
timeSpan.Hours | DATEPART(hour, @timeSpan)
159159
timeSpan.Microsecond | DATEPART(microsecond, @timeSpan) % 1000 | EF Core 10.0
160160
timeSpan.Milliseconds | DATEPART(millisecond, @timeSpan)
@@ -166,8 +166,8 @@ timeSpan.Seconds | DATEPART(second, @
166166

167167
.NET | SQL | Added in
168168
-------------------------- | -------------------- | --------
169-
double.DegreesToRadians(x) | RADIANS(@x) | EF Core 8.0
170-
double.RadiansToDegrees(x) | DEGREES(@x) | EF Core 8.0
169+
double.DegreesToRadians(x) | RADIANS(@x)
170+
double.RadiansToDegrees(x) | DEGREES(@x)
171171
EF.Functions.Random() | RAND()
172172
Math.Abs(value) | ABS(@value)
173173
Math.Acos(d) | ACOS(@d)
@@ -219,7 +219,7 @@ stringValue.Contains(value) | @strin
219219
stringValue.EndsWith(value) | @stringValue LIKE N'%' + @value
220220
stringValue.FirstOrDefault() | SUBSTRING(@stringValue, 1, 1)
221221
stringValue.IndexOf(value) | CHARINDEX(@value, @stringValue) - 1
222-
stringValue.IndexOf(value, startIndex) | CHARINDEX(@value, @stringValue, @startIndex) - 1 | EF Core 7.0
222+
stringValue.IndexOf(value, startIndex) | CHARINDEX(@value, @stringValue, @startIndex) - 1
223223
stringValue.LastOrDefault() | SUBSTRING(@stringValue, LEN(@stringValue), 1)
224224
stringValue.Length | LEN(@stringValue)
225225
stringValue.Replace(@oldValue, @newValue) | REPLACE(@stringValue, @oldValue, @newValue)
@@ -242,9 +242,10 @@ nullable.GetValueOrDefault() | COALESCE(@nullable, 0)
242242
nullable.GetValueOrDefault(defaultValue) | COALESCE(@nullable, @defaultValue)
243243

244244
> [!NOTE]
245-
> Some SQL has been simplified for illustration purposes. The actual SQL is more complex to handle a wider range of values.
245+
> Some SQL translations have been simplified for illustration purposes. The actual SQL is more complex to handle a wider range of values.
246246
247247
## See also
248248

249+
* [Vector Search Function Mappings](xref:core/providers/sql-server/vector-search)
249250
* [Spatial Function Mappings](xref:core/providers/sql-server/spatial#spatial-function-mappings)
250251
* [HierarchyId Function Mappings](xref:core/providers/sql-server/hierarchyid#function-mappings)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: Microsoft SQL Server Database Provider - Vector Search - EF Core
3+
description: Using vectors and embeddings to perform similarity search with the Entity Framework Core Microsoft SQL Server database provider
4+
author: roji
5+
ms.date: 08/17/2025
6+
uid: core/providers/sql-server/vector-and-full-text-search
7+
---
8+
# Vector search in the SQL Server EF Core Provider
9+
10+
## Vector search
11+
12+
> [!NOTE]
13+
> Vector support was introduced in EF Core 10.0, and is only supported with SQL Server 2025 and above.
14+
15+
The SQL Server vector data type allows storing *embeddings*, which are representation of meaning that can be efficiently searched over for similarity, powering AI workloads such as semantic search and retrieval-augmented generation (RAG).
16+
17+
To use the `vector` data type, simply add a .NET property of type `SqlVector<float>` to your entity type, specifying the dimensions as follows:
18+
19+
### [Data Annotations](#tab/data-annotations)
20+
21+
```c#
22+
public class Blog
23+
{
24+
// ...
25+
26+
[Column(TypeName = "vector(1536)")]
27+
public SqlVector<float> Embedding { get; set; }
28+
}
29+
```
30+
31+
### [Fluent API](#tab/fluent-api)
32+
33+
```c#
34+
public class Blog
35+
{
36+
// ...
37+
38+
public SqlVector<float> Embedding { get; set; }
39+
}
40+
41+
protected override void OnModelCreating(ModelBuilder modelBuilder)
42+
{
43+
modelBuilder.Entity<Blog>()
44+
.Property(b => b.Embedding)
45+
.HasColumnType("vector(1536)");
46+
}
47+
```
48+
49+
***
50+
51+
Once your property is added and the corresponding column created in the database, you can start inserting embeddings. Embedding generation is done outside of the database, usually via a service, and the details for doing this are out of scope for this documentation. However, [the .NET Microsoft.Extensions.AI libraries](/dotnet/ai/microsoft-extensions-ai) contains [`IEmbeddingGenerator`](/dotnet/ai/microsoft-extensions-ai#create-embeddings), which is an abstraction over embedding generators that has implementations for the major providers.
52+
53+
Once you've chosen your embedding generator and set it up, use it to generate embeddings and insert them as follows
54+
55+
```c#
56+
IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = /* Set up your preferred embedding generator */;
57+
58+
var embedding = await embeddingGenerator.GenerateVectorAsync("Some text to be vectorized");
59+
context.Blogs.Add(new Blog
60+
{
61+
Name = "Some blog",
62+
Embedding = new SqlVector<float>(embedding)
63+
});
64+
await context.SaveChangesAsync();
65+
```
66+
67+
Finally, use the [`EF.Functions.VectorDistance()`](/sql/t-sql/functions/vector-distance-transact-sql) function to perform similarity search for a given user query:
68+
69+
```c#
70+
var sqlVector = new SqlVector<float>(await embeddingGenerator.GenerateVectorAsync("Some user query to be vectorized"));
71+
var topSimilarBlogs = context.Blogs
72+
.OrderBy(b => EF.Functions.VectorDistance("cosine", b.Embedding, sqlVector))
73+
.Take(3)
74+
.ToListAsync();
75+
```
76+
77+
> [!NOTE]
78+
> The built-in support in EF 10 replaces the previous [EFCore.SqlServer.VectorSearch](https://github.com/efcore/EFCore.SqlServer.VectorSearch) extension, which allowed performing vector search before the `vector` data type was introduced. As part of upgrading to EF 10, remove the extension from your projects.
79+
>
80+
> The [`VECTOR_SEARCH()`](/sql/t-sql/functions/vector-search-transact-sql) function (in preview) for approximate search with DiskANN is currently unsupported.

0 commit comments

Comments
 (0)