Skip to content

Commit 3d7c966

Browse files
committed
WIP
1 parent ff9db2e commit 3d7c966

File tree

8 files changed

+114
-68
lines changed

8 files changed

+114
-68
lines changed

src/EFCore.SqlServer/Extensions/SqlServerEntityTypeBuilderExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using Microsoft.EntityFrameworkCore.Metadata.Builders;
56
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
67

@@ -328,6 +329,7 @@ public static bool CanSetIsTemporal(
328329
/// </param>
329330
/// <param name="name">The name to assign to the index.</param>
330331
/// <returns>A builder to further configure the vector index.</returns>
332+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
331333
public static VectorIndexBuilder<TEntity> HasVectorIndex<TEntity>(
332334
this EntityTypeBuilder<TEntity> entityTypeBuilder,
333335
Expression<Func<TEntity, object?>> indexExpression,
@@ -377,6 +379,7 @@ public static VectorIndexBuilder<TEntity> HasVectorIndex<TEntity>(
377379
/// </param>
378380
/// <param name="name">The name to assign to the index.</param>
379381
/// <returns>A builder to further configure the vector index.</returns>
382+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
380383
public static VectorIndexBuilder HasVectorIndex(
381384
this EntityTypeBuilder entityTypeBuilder,
382385
string propertyName,

src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
56

67
// ReSharper disable once CheckNamespace
@@ -437,6 +438,7 @@ public static void SetDataCompression(this IMutableIndex index, DataCompressionT
437438
/// </summary>
438439
/// <param name="index">The index.</param>
439440
/// <returns>Whether the index is a vector index.</returns>
441+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
440442
public static bool IsVectorIndex(this IReadOnlyIndex index)
441443
=> index is RuntimeIndex
442444
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
@@ -447,6 +449,7 @@ public static bool IsVectorIndex(this IReadOnlyIndex index)
447449
/// </summary>
448450
/// <param name="index">The index.</param>
449451
/// <returns>The similarity metric for the vector index.</returns>
452+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
450453
public static string? GetVectorMetric(this IReadOnlyIndex index)
451454
=> index is RuntimeIndex
452455
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
@@ -458,6 +461,7 @@ public static bool IsVectorIndex(this IReadOnlyIndex index)
458461
/// <param name="index">The index.</param>
459462
/// <param name="storeObject">The identifier of the store object.</param>
460463
/// <returns>The similarity metric for the vector index.</returns>
464+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
461465
public static string? GetVectorMetric(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
462466
{
463467
if (index is RuntimeIndex)
@@ -480,6 +484,7 @@ public static bool IsVectorIndex(this IReadOnlyIndex index)
480484
/// </summary>
481485
/// <param name="index">The index.</param>
482486
/// <param name="metric">The value to set.</param>
487+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
483488
public static void SetVectorMetric(this IMutableIndex index, string? metric)
484489
=> index.SetAnnotation(SqlServerAnnotationNames.VectorIndexMetric, metric);
485490

@@ -490,6 +495,7 @@ public static void SetVectorMetric(this IMutableIndex index, string? metric)
490495
/// <param name="metric">The value to set.</param>
491496
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
492497
/// <returns>The configured value.</returns>
498+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
493499
public static string? SetVectorMetric(
494500
this IConventionIndex index,
495501
string? metric,
@@ -504,6 +510,7 @@ public static void SetVectorMetric(this IMutableIndex index, string? metric)
504510
/// </summary>
505511
/// <param name="index">The index.</param>
506512
/// <returns>The <see cref="ConfigurationSource" /> for the similarity metric of the vector index.</returns>
513+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
507514
public static ConfigurationSource? GetVectorMetricConfigurationSource(this IConventionIndex index)
508515
=> index.FindAnnotation(SqlServerAnnotationNames.VectorIndexMetric)?.GetConfigurationSource();
509516

@@ -512,6 +519,7 @@ public static void SetVectorMetric(this IMutableIndex index, string? metric)
512519
/// </summary>
513520
/// <param name="index">The index.</param>
514521
/// <returns>The type of the vector index.</returns>
522+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
515523
public static string? GetVectorIndexType(this IReadOnlyIndex index)
516524
=> (index is RuntimeIndex)
517525
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
@@ -523,6 +531,7 @@ public static void SetVectorMetric(this IMutableIndex index, string? metric)
523531
/// <param name="index">The index.</param>
524532
/// <param name="storeObject">The identifier of the store object.</param>
525533
/// <returns>The type of the vector index.</returns>
534+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
526535
public static string? GetVectorIndexType(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
527536
{
528537
if (index is RuntimeIndex)
@@ -545,6 +554,7 @@ public static void SetVectorMetric(this IMutableIndex index, string? metric)
545554
/// </summary>
546555
/// <param name="index">The index.</param>
547556
/// <param name="type">The value to set.</param>
557+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
548558
public static void SetVectorIndexType(this IMutableIndex index, string? type)
549559
=> index.SetAnnotation(SqlServerAnnotationNames.VectorIndexType, type);
550560

@@ -555,6 +565,7 @@ public static void SetVectorIndexType(this IMutableIndex index, string? type)
555565
/// <param name="type">The value to set.</param>
556566
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
557567
/// <returns>The configured value.</returns>
568+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
558569
public static string? SetVectorIndexType(
559570
this IConventionIndex index,
560571
string? type,
@@ -569,6 +580,7 @@ public static void SetVectorIndexType(this IMutableIndex index, string? type)
569580
/// </summary>
570581
/// <param name="index">The index.</param>
571582
/// <returns>The <see cref="ConfigurationSource" /> for the type of the vector index.</returns>
583+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
572584
public static ConfigurationSource? GetVectorIndexTypeConfigurationSource(this IConventionIndex index)
573585
=> index.FindAnnotation(SqlServerAnnotationNames.VectorIndexType)?.GetConfigurationSource();
574586
}

src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ protected virtual void ValidateIndexIncludeProperties(
274274
/// any release. You should only use it directly in your code with extreme caution and knowing that
275275
/// doing so can result in application failures when updating to a new Entity Framework Core release.
276276
/// </summary>
277+
#pragma warning disable EF9105 // Vector indexes are experimental
277278
protected virtual void ValidateVectorIndexes(
278279
IModel model,
279280
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
@@ -289,6 +290,7 @@ protected virtual void ValidateVectorIndexes(
289290
}
290291
}
291292
}
293+
#pragma warning restore EF9105
292294

293295
/// <summary>
294296
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to

src/EFCore.SqlServer/Metadata/Builders/VectorIndexBuilder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
5+
46
namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
57

68
/// <summary>
@@ -12,6 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
1214
/// for more information and examples.
1315
/// </remarks>
1416
/// <param name="indexBuilder">The index builder.</param>
17+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
1518
public class VectorIndexBuilder(IndexBuilder indexBuilder)
1619
{
1720
/// <summary>

src/EFCore.SqlServer/Metadata/Builders/VectorIndexBuilder`1.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
5+
46
namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
57

68
/// <summary>
@@ -13,6 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
1315
/// </remarks>
1416
/// <typeparam name="TEntity">The entity type being configured.</typeparam>
1517
/// <param name="indexBuilder">The index builder.</param>
18+
[Experimental(EFDiagnostics.SqlServerVectorSearch)]
1619
public class VectorIndexBuilder<TEntity>(IndexBuilder<TEntity> indexBuilder)
1720
{
1821
/// <summary>

src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public override IEnumerable<IAnnotation> For(ITableIndex index, bool designTime)
182182
var modelIndex = index.MappedIndexes.First();
183183
var table = StoreObjectIdentifier.Table(index.Table.Name, index.Table.Schema);
184184

185+
#pragma warning disable EF9105 // Vector indexes are experimental
185186
if (modelIndex.GetVectorMetric(table) is { } vectorMetric)
186187
{
187188
yield return new Annotation(SqlServerAnnotationNames.VectorIndexMetric, vectorMetric);
@@ -191,6 +192,7 @@ public override IEnumerable<IAnnotation> For(ITableIndex index, bool designTime)
191192
yield return new Annotation(SqlServerAnnotationNames.VectorIndexType, vectorType);
192193
}
193194
}
195+
#pragma warning restore EF9105
194196

195197
if (modelIndex.IsClustered(table) is { } isClustered)
196198
{

src/EFCore.SqlServer/Scaffolding/Internal/SqlServerDatabaseModelFactory.cs

Lines changed: 86 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,12 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Scaffolding.Internal;
2020
/// any release. You should only use it directly in your code with extreme caution and knowing that
2121
/// doing so can result in application failures when updating to a new Entity Framework Core release.
2222
/// </summary>
23-
public class SqlServerDatabaseModelFactory : DatabaseModelFactory
23+
public class SqlServerDatabaseModelFactory(
24+
IDiagnosticsLogger<DbLoggerCategory.Scaffolding> logger,
25+
IRelationalTypeMappingSource typeMappingSource) : DatabaseModelFactory
2426
{
25-
private readonly IDiagnosticsLogger<DbLoggerCategory.Scaffolding> _logger;
26-
private readonly IRelationalTypeMappingSource _typeMappingSource;
27-
28-
private static readonly ISet<string> DateTimePrecisionTypes = new HashSet<string>
29-
{
30-
"datetimeoffset",
31-
"datetime2",
32-
"time"
33-
};
34-
35-
private static readonly ISet<string> MaxLengthRequiredTypes
36-
= new HashSet<string>
37-
{
38-
"binary",
39-
"varbinary",
40-
"char",
41-
"varchar",
42-
"nchar",
43-
"nvarchar"
44-
};
45-
46-
private enum EngineEdition
47-
{
48-
SqlDataWarehouse = 6,
49-
SqlOnDemand = 11,
50-
DynamicsTdsEndpoint = 1000,
51-
}
27+
private readonly IDiagnosticsLogger<DbLoggerCategory.Scaffolding> _logger = logger;
28+
private readonly IRelationalTypeMappingSource _typeMappingSource = typeMappingSource;
5229

5330
private const string NamePartRegex
5431
= @"(?:(?:\[(?<part{0}>(?:(?:\]\])|[^\]])+)\])|(?<part{0}>[^\.\[\]]+))";
@@ -63,35 +40,10 @@ private static readonly Regex PartExtractor
6340
RegexOptions.Compiled,
6441
TimeSpan.FromMilliseconds(1000));
6542

66-
// see https://msdn.microsoft.com/en-us/library/ff878091.aspx
67-
// decimal/numeric are excluded because default value varies based on the precision.
68-
private static readonly Dictionary<string, long[]> DefaultSequenceMinMax =
69-
new(StringComparer.OrdinalIgnoreCase)
70-
{
71-
{ "tinyint", [0L, 255L] },
72-
{ "smallint", [-32768L, 32767L] },
73-
{ "int", [-2147483648L, 2147483647L] },
74-
{ "bigint", [-9223372036854775808L, 9223372036854775807L] }
75-
};
76-
7743
private byte? _compatibilityLevel;
7844
private EngineEdition? _engineEdition;
7945
private string? _version;
8046

81-
/// <summary>
82-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
83-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
84-
/// any release. You should only use it directly in your code with extreme caution and knowing that
85-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
86-
/// </summary>
87-
public SqlServerDatabaseModelFactory(
88-
IDiagnosticsLogger<DbLoggerCategory.Scaffolding> logger,
89-
IRelationalTypeMappingSource typeMappingSource)
90-
{
91-
_logger = logger;
92-
_typeMappingSource = typeMappingSource;
93-
}
94-
9547
/// <summary>
9648
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
9749
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -488,13 +440,24 @@ FROM [sys].[sequences] AS [s]
488440
MaxValue = maxValue
489441
};
490442

491-
if (DefaultSequenceMinMax.TryGetValue(storeType, out var defaultMinMax))
443+
// See https://msdn.microsoft.com/en-us/library/ff878091.aspx
444+
// decimal/numeric are excluded because the default value varies based on the precision.
445+
var (defaultMin, defaultMax) = storeType.ToLowerInvariant() switch
446+
{
447+
"tinyint" => (0L, 255L),
448+
"smallint" => (-32768L, 32767L),
449+
"int" => (-2147483648L, 2147483647L),
450+
"bigint" => (-9223372036854775808L, 9223372036854775807L),
451+
452+
_ => ((long?)null, (long?)null)
453+
};
454+
455+
if (defaultMin is not null && defaultMax is not null)
492456
{
493-
var defaultMin = defaultMinMax[0];
494457
sequence.MinValue = sequence.MinValue == defaultMin ? null : sequence.MinValue;
495458
sequence.StartValue = sequence.StartValue == defaultMin ? null : sequence.StartValue;
496459

497-
sequence.MaxValue = sequence.MaxValue == defaultMinMax[1]
460+
sequence.MaxValue = sequence.MaxValue == defaultMax
498461
? null
499462
: sequence.MaxValue;
500463
}
@@ -1016,13 +979,14 @@ private static string GetStoreType(string dataTypeName, int maxLength, int preci
1016979
return $"vector({vectorDimensions})";
1017980
}
1018981

1019-
if (DateTimePrecisionTypes.Contains(dataTypeName)
1020-
&& scale != 7)
982+
// date/time types with scale facet
983+
if (dataTypeName is "time" or "datetime2" or "datetimeoffset" && scale != 7)
1021984
{
1022985
return $"{dataTypeName}({scale})";
1023986
}
1024987

1025-
if (MaxLengthRequiredTypes.Contains(dataTypeName))
988+
// These types require a length facet
989+
if (dataTypeName is "binary" or "varbinary" or "char" or "varchar" or "nchar" or "nvarchar")
1026990
{
1027991
if (maxLength == -1)
1028992
{
@@ -1513,19 +1477,19 @@ private bool SupportsSequences()
15131477
=> _compatibilityLevel >= 110 && IsFullFeaturedEngineEdition();
15141478

15151479
private bool SupportsIndexes()
1516-
=> _engineEdition != EngineEdition.DynamicsTdsEndpoint;
1480+
=> _engineEdition != EngineEdition.DynamicsCrm;
15171481

15181482
private bool SupportsVectorIndexes()
1519-
=> _compatibilityLevel >= 170;
1483+
=> _compatibilityLevel >= 170 && IsFullFeaturedEngineEdition();
15201484

15211485
private bool SupportsViews()
1522-
=> _engineEdition != EngineEdition.DynamicsTdsEndpoint;
1486+
=> _engineEdition != EngineEdition.DynamicsCrm;
15231487

15241488
private bool SupportsTriggers()
15251489
=> IsFullFeaturedEngineEdition();
15261490

15271491
private bool IsFullFeaturedEngineEdition()
1528-
=> _engineEdition is not EngineEdition.SqlDataWarehouse and not EngineEdition.SqlOnDemand and not EngineEdition.DynamicsTdsEndpoint
1492+
=> _engineEdition is not EngineEdition.AzureSynapseAnalytics and not EngineEdition.AzureSynapseServerlessOrFabric and not EngineEdition.DynamicsCrm
15291493
&& _version != "Microsoft SQL Kusto";
15301494

15311495
private static string DisplayName(string? schema, string name)
@@ -1540,4 +1504,63 @@ private static string DisplayName(string? schema, string name)
15401504
"SET_DEFAULT" => ReferentialAction.SetDefault,
15411505
_ => null
15421506
};
1507+
1508+
// See https://learn.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql
1509+
private enum EngineEdition
1510+
{
1511+
/// <summary>
1512+
/// Personal or Desktop Engine (Not available in SQL Server 2005 (9.x) and later versions.)
1513+
/// </summary>
1514+
PersonalOrDesktop = 1,
1515+
1516+
/// <summary>
1517+
/// Standard (For Standard, Standard Developer, Web, and Business Intelligence.)
1518+
/// </summary>
1519+
Standard = 2,
1520+
1521+
/// <summary>
1522+
/// Enterprise (For Enterprise, Enterprise Developer, Developer, and Evaluation editions.)
1523+
/// </summary>
1524+
Enterprise = 3,
1525+
1526+
/// <summary>
1527+
/// Express (For Express, Express with Tools, and Express with Advanced Services)
1528+
/// </summary>
1529+
Express = 4,
1530+
1531+
/// <summary>
1532+
/// SQL Database (Azure SQL Database)
1533+
/// </summary>
1534+
SqlDatabase = 5,
1535+
1536+
/// <summary>
1537+
/// Azure Synapse Analytics
1538+
/// </summary>
1539+
AzureSynapseAnalytics = 6,
1540+
1541+
/// <summary>
1542+
/// Azure SQL Managed Instance
1543+
/// </summary>
1544+
AzureSqlManaged = 8,
1545+
1546+
/// <summary>
1547+
/// Azure SQL Edge (For all editions of Azure SQL Edge)
1548+
/// </summary>
1549+
AzureSqlEdge = 9,
1550+
1551+
/// <summary>
1552+
/// Azure Synapse serverless SQL pool, or Microsoft Fabric
1553+
/// </summary>
1554+
AzureSynapseServerlessOrFabric = 11,
1555+
1556+
/// <summary>
1557+
/// Microsoft Fabric SQL database in Microsoft Fabric.
1558+
/// </summary>
1559+
FabricSql = 12,
1560+
1561+
/// <summary>
1562+
/// Dynamics CRM TDS endpoint
1563+
/// </summary>
1564+
DynamicsCrm = 1000,
1565+
}
15431566
}

0 commit comments

Comments
 (0)