From a5119e05f789a1732c1d07a49d891a2f4e6fa677 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 25 Jul 2022 20:59:21 +0100 Subject: [PATCH] Change from sequence-per-model to sequence-per-hierarchy when using sequences for key generation (#28501) --- .../SqlServerAnnotationCodeGenerator.cs | 13 +++-- .../SqlServerModelBuilderExtensions.cs | 43 +++++++------- .../Extensions/SqlServerModelExtensions.cs | 48 ++++++++-------- .../SqlServerPropertyBuilderExtensions.cs | 14 +---- .../Extensions/SqlServerPropertyExtensions.cs | 28 ++++----- ...ServerValueGenerationStrategyConvention.cs | 9 ++- .../Internal/SqlServerAnnotationNames.cs | 12 +++- .../Migrations/ModelSnapshotSqlServerTest.cs | 6 +- .../CSharpRuntimeModelCodeGeneratorTest.cs | 4 +- ...TPCFiltersInheritanceQuerySqlServerTest.cs | 2 +- .../Query/TPCInheritanceQuerySqlServerTest.cs | 2 +- ...TPTFiltersInheritanceQuerySqlServerTest.cs | 2 +- .../SqlServerModelValidatorTest.cs | 7 ++- ...erValueGenerationStrategyConventionTest.cs | 15 ++--- .../SqlServerBuilderExtensionsTest.cs | 57 +++++++++---------- .../Migrations/SqlServerModelDifferTest.cs | 6 +- .../SqlServerValueGeneratorSelectorTest.cs | 1 - 17 files changed, 132 insertions(+), 137 deletions(-) diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs index 655e755a1e6..49a7f8ecdd1 100644 --- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs +++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs @@ -401,15 +401,18 @@ protected override bool IsHandledByConvention(IModel model, IAnnotation annotati case SqlServerValueGenerationStrategy.Sequence: { - var name = GetAndRemove(annotations, SqlServerAnnotationNames.KeySequenceName); - var schema = GetAndRemove(annotations, SqlServerAnnotationNames.KeySequenceSchema); + var nameOrSuffix = GetAndRemove( + annotations, + onModel ? SqlServerAnnotationNames.SequenceNameSuffix : SqlServerAnnotationNames.SequenceName); + + var schema = GetAndRemove(annotations, SqlServerAnnotationNames.SequenceSchema); return new MethodCallCodeFragment( onModel ? ModelUseKeySequencesMethodInfo : PropertyUseSequenceMethodInfo, - (name, schema) switch + (name: nameOrSuffix, schema) switch { (null, null) => Array.Empty(), - (_, null) => new object[] { name }, - _ => new object[] { name!, schema } + (_, null) => new object[] { nameOrSuffix }, + _ => new object[] { nameOrSuffix!, schema } }); } diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs index cab521f215d..e49b24a14e8 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs @@ -49,8 +49,8 @@ public static ModelBuilder UseHiLo( model.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.SequenceHiLo); model.SetHiLoSequenceName(name); model.SetHiLoSequenceSchema(schema); - model.SetKeySequenceName(null); - model.SetKeySequenceSchema(null); + model.SetSequenceNameSuffix(null); + model.SetSequenceSchema(null); model.SetIdentitySeed(null); model.SetIdentityIncrement(null); @@ -115,7 +115,7 @@ public static bool CanSetHiLoSequence( } /// - /// Configures the model to use a sequence to generate values for key properties + /// Configures the model to use a sequence per hierarchy to generate values for key properties /// marked as , when targeting SQL Server. /// /// @@ -124,29 +124,24 @@ public static bool CanSetHiLoSequence( /// for more information and examples. /// /// The model builder. - /// The name of the sequence. + /// The name that will suffix the table name for each sequence created automatically. /// The schema of the sequence. /// The same builder instance so that multiple calls can be chained. public static ModelBuilder UseKeySequences( this ModelBuilder modelBuilder, - string? name = null, + string? nameSuffix = null, string? schema = null) { - Check.NullButNotEmpty(name, nameof(name)); + Check.NullButNotEmpty(nameSuffix, nameof(nameSuffix)); Check.NullButNotEmpty(schema, nameof(schema)); var model = modelBuilder.Model; - name ??= SqlServerModelExtensions.DefaultKeySequenceName; - - if (model.FindSequence(name, schema) == null) - { - modelBuilder.HasSequence(name, schema); - } + nameSuffix ??= SqlServerModelExtensions.DefaultSequenceNameSuffix; model.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.Sequence); - model.SetKeySequenceName(name); - model.SetKeySequenceSchema(schema); + model.SetSequenceNameSuffix(nameSuffix); + model.SetSequenceSchema(schema); model.SetHiLoSequenceName(null); model.SetHiLoSequenceSchema(null); model.SetIdentitySeed(null); @@ -180,10 +175,10 @@ public static ModelBuilder UseKeySequences( return null; } - modelBuilder.Metadata.SetKeySequenceName(name, fromDataAnnotation); - modelBuilder.Metadata.SetKeySequenceSchema(schema, fromDataAnnotation); + modelBuilder.Metadata.SetSequenceNameSuffix(name, fromDataAnnotation); + modelBuilder.Metadata.SetSequenceSchema(schema, fromDataAnnotation); - return name == null ? null : modelBuilder.HasSequence(name, schema, fromDataAnnotation); + return null; } /// @@ -195,21 +190,21 @@ public static ModelBuilder UseKeySequences( /// for more information and examples. /// /// The model builder. - /// The name of the sequence. + /// The name of the sequence. /// The schema of the sequence. /// Indicates whether the configuration was specified using a data annotation. /// if the given name and schema can be set for the hi-lo sequence. public static bool CanSetKeySequences( this IConventionModelBuilder modelBuilder, - string? name, + string? nameSuffix, string? schema, bool fromDataAnnotation = false) { - Check.NullButNotEmpty(name, nameof(name)); + Check.NullButNotEmpty(nameSuffix, nameof(nameSuffix)); Check.NullButNotEmpty(schema, nameof(schema)); - return modelBuilder.CanSetAnnotation(SqlServerAnnotationNames.KeySequenceName, name, fromDataAnnotation) - && modelBuilder.CanSetAnnotation(SqlServerAnnotationNames.KeySequenceSchema, schema, fromDataAnnotation); + return modelBuilder.CanSetAnnotation(SqlServerAnnotationNames.SequenceNameSuffix, nameSuffix, fromDataAnnotation) + && modelBuilder.CanSetAnnotation(SqlServerAnnotationNames.SequenceSchema, schema, fromDataAnnotation); } /// @@ -236,8 +231,8 @@ public static ModelBuilder UseIdentityColumns( model.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.IdentityColumn); model.SetIdentitySeed(seed); model.SetIdentityIncrement(increment); - model.SetKeySequenceName(null); - model.SetKeySequenceSchema(null); + model.SetSequenceNameSuffix(null); + model.SetSequenceSchema(null); model.SetHiLoSequenceName(null); model.SetHiLoSequenceSchema(null); diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs index 2211a6bb262..aa050ef0aad 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs @@ -22,9 +22,9 @@ public static class SqlServerModelExtensions public const string DefaultHiLoSequenceName = "EntityFrameworkHiLoSequence"; /// - /// The default name for the hi-lo sequence. + /// The default prefix for sequences applied to properties. /// - public const string DefaultKeySequenceName = "EntityFrameworkKeySequence"; + public const string DefaultSequenceNameSuffix = "Sequence"; /// /// Returns the name to use for the default hi-lo sequence. @@ -124,72 +124,72 @@ public static void SetHiLoSequenceSchema(this IMutableModel model, string? value /// - /// Returns the name to use for the default key value generation sequence. + /// Returns the suffix to append to the name of automatically created sequences. /// /// The model. /// The name to use for the default key value generation sequence. - public static string GetKeySequenceName(this IReadOnlyModel model) - => (string?)model[SqlServerAnnotationNames.KeySequenceName] - ?? DefaultKeySequenceName; + public static string GetSequenceNameSuffix(this IReadOnlyModel model) + => (string?)model[SqlServerAnnotationNames.SequenceNameSuffix] + ?? DefaultSequenceNameSuffix; /// - /// Sets the name to use for the default key value generation sequence. + /// Sets the suffix to append to the name of automatically created sequences. /// /// The model. /// The value to set. - public static void SetKeySequenceName(this IMutableModel model, string? name) + public static void SetSequenceNameSuffix(this IMutableModel model, string? name) { Check.NullButNotEmpty(name, nameof(name)); - model.SetOrRemoveAnnotation(SqlServerAnnotationNames.KeySequenceName, name); + model.SetOrRemoveAnnotation(SqlServerAnnotationNames.SequenceNameSuffix, name); } /// - /// Sets the name to use for the default key value generation sequence. + /// Sets the suffix to append to the name of automatically created sequences. /// /// The model. /// The value to set. /// Indicates whether the configuration was specified using a data annotation. /// The configured value. - public static string? SetKeySequenceName( + public static string? SetSequenceNameSuffix( this IConventionModel model, string? name, bool fromDataAnnotation = false) { Check.NullButNotEmpty(name, nameof(name)); - model.SetOrRemoveAnnotation(SqlServerAnnotationNames.KeySequenceName, name, fromDataAnnotation); + model.SetOrRemoveAnnotation(SqlServerAnnotationNames.SequenceNameSuffix, name, fromDataAnnotation); return name; } /// - /// Returns the for the default key value generation sequence name. + /// Returns the for the default value generation sequence name suffix. /// /// The model. /// The for the default key value generation sequence name. - public static ConfigurationSource? GetKeySequenceNameConfigurationSource(this IConventionModel model) - => model.FindAnnotation(SqlServerAnnotationNames.KeySequenceName)?.GetConfigurationSource(); + public static ConfigurationSource? GetSequenceNameSuffixConfigurationSource(this IConventionModel model) + => model.FindAnnotation(SqlServerAnnotationNames.SequenceNameSuffix)?.GetConfigurationSource(); /// - /// Returns the schema to use for the default hi-lo sequence. + /// Returns the schema to use for the default value generation sequence. /// /// /// The model. /// The schema to use for the default key value generation sequence. - public static string? GetKeySequenceSchema(this IReadOnlyModel model) - => (string?)model[SqlServerAnnotationNames.KeySequenceSchema]; + public static string? GetSequenceSchema(this IReadOnlyModel model) + => (string?)model[SqlServerAnnotationNames.SequenceSchema]; /// /// Sets the schema to use for the default key value generation sequence. /// /// The model. /// The value to set. - public static void SetKeySequenceSchema(this IMutableModel model, string? value) + public static void SetSequenceSchema(this IMutableModel model, string? value) { Check.NullButNotEmpty(value, nameof(value)); - model.SetOrRemoveAnnotation(SqlServerAnnotationNames.KeySequenceSchema, value); + model.SetOrRemoveAnnotation(SqlServerAnnotationNames.SequenceSchema, value); } /// @@ -199,14 +199,14 @@ public static void SetKeySequenceSchema(this IMutableModel model, string? value) /// The value to set. /// Indicates whether the configuration was specified using a data annotation. /// The configured value. - public static string? SetKeySequenceSchema( + public static string? SetSequenceSchema( this IConventionModel model, string? value, bool fromDataAnnotation = false) { Check.NullButNotEmpty(value, nameof(value)); - model.SetOrRemoveAnnotation(SqlServerAnnotationNames.KeySequenceSchema, value, fromDataAnnotation); + model.SetOrRemoveAnnotation(SqlServerAnnotationNames.SequenceSchema, value, fromDataAnnotation); return value; } @@ -216,8 +216,8 @@ public static void SetKeySequenceSchema(this IMutableModel model, string? value) /// /// The model. /// The for the default key value generation sequence schema. - public static ConfigurationSource? GetKeySequenceSchemaConfigurationSource(this IConventionModel model) - => model.FindAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema)?.GetConfigurationSource(); + public static ConfigurationSource? GetSequenceSchemaConfigurationSource(this IConventionModel model) + => model.FindAnnotation(SqlServerAnnotationNames.SequenceSchema)?.GetConfigurationSource(); /// /// Returns the default identity seed. diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs index bfa03d43646..eaa63b24eb7 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; // ReSharper disable once CheckNamespace @@ -159,15 +160,6 @@ public static PropertyBuilder UseSequence( var property = propertyBuilder.Metadata; - name ??= SqlServerModelExtensions.DefaultKeySequenceName; - - var model = property.DeclaringEntityType.Model; - - if (model.FindSequence(name, schema) == null) - { - model.AddSequence(name, schema).IncrementBy = 1; - } - property.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.Sequence); property.SetSequenceName(name); property.SetSequenceSchema(schema); @@ -254,8 +246,8 @@ public static bool CanSetSequence( Check.NullButNotEmpty(name, nameof(name)); Check.NullButNotEmpty(schema, nameof(schema)); - return propertyBuilder.CanSetAnnotation(SqlServerAnnotationNames.KeySequenceName, name, fromDataAnnotation) - && propertyBuilder.CanSetAnnotation(SqlServerAnnotationNames.KeySequenceSchema, schema, fromDataAnnotation); + return propertyBuilder.CanSetAnnotation(SqlServerAnnotationNames.SequenceName, name, fromDataAnnotation) + && propertyBuilder.CanSetAnnotation(SqlServerAnnotationNames.SequenceSchema, schema, fromDataAnnotation); } /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs index 75d095c83bb..3ed0c97d50e 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs @@ -203,7 +203,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// The property. /// The name to use for the key value generation sequence. public static string? GetSequenceName(this IReadOnlyProperty property) - => (string?)property[SqlServerAnnotationNames.KeySequenceName]; + => (string?)property[SqlServerAnnotationNames.SequenceName]; /// /// Returns the name to use for the key value generation sequence. @@ -213,7 +213,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// The name to use for the key value generation sequence. public static string? GetSequenceName(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { - var annotation = property.FindAnnotation(SqlServerAnnotationNames.KeySequenceName); + var annotation = property.FindAnnotation(SqlServerAnnotationNames.SequenceName); if (annotation != null) { return (string?)annotation.Value; @@ -229,7 +229,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// The sequence name to use. public static void SetSequenceName(this IMutableProperty property, string? name) => property.SetOrRemoveAnnotation( - SqlServerAnnotationNames.KeySequenceName, + SqlServerAnnotationNames.SequenceName, Check.NullButNotEmpty(name, nameof(name))); /// @@ -245,7 +245,7 @@ public static void SetSequenceName(this IMutableProperty property, string? name) bool fromDataAnnotation = false) { property.SetOrRemoveAnnotation( - SqlServerAnnotationNames.KeySequenceName, + SqlServerAnnotationNames.SequenceName, Check.NullButNotEmpty(name, nameof(name)), fromDataAnnotation); @@ -258,7 +258,7 @@ public static void SetSequenceName(this IMutableProperty property, string? name) /// The property. /// The for the key value generation sequence name. public static ConfigurationSource? GetSequenceNameConfigurationSource(this IConventionProperty property) - => property.FindAnnotation(SqlServerAnnotationNames.KeySequenceName)?.GetConfigurationSource(); + => property.FindAnnotation(SqlServerAnnotationNames.SequenceName)?.GetConfigurationSource(); /// /// Returns the schema to use for the key value generation sequence. @@ -266,7 +266,7 @@ public static void SetSequenceName(this IMutableProperty property, string? name) /// The property. /// The schema to use for the key value generation sequence. public static string? GetSequenceSchema(this IReadOnlyProperty property) - => (string?)property[SqlServerAnnotationNames.KeySequenceSchema]; + => (string?)property[SqlServerAnnotationNames.SequenceSchema]; /// /// Returns the schema to use for the key value generation sequence. @@ -276,7 +276,7 @@ public static void SetSequenceName(this IMutableProperty property, string? name) /// The schema to use for the key value generation sequence. public static string? GetSequenceSchema(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { - var annotation = property.FindAnnotation(SqlServerAnnotationNames.KeySequenceSchema); + var annotation = property.FindAnnotation(SqlServerAnnotationNames.SequenceSchema); if (annotation != null) { return (string?)annotation.Value; @@ -292,7 +292,7 @@ public static void SetSequenceName(this IMutableProperty property, string? name) /// The schema to use. public static void SetSequenceSchema(this IMutableProperty property, string? schema) => property.SetOrRemoveAnnotation( - SqlServerAnnotationNames.KeySequenceSchema, + SqlServerAnnotationNames.SequenceSchema, Check.NullButNotEmpty(schema, nameof(schema))); /// @@ -308,7 +308,7 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch bool fromDataAnnotation = false) { property.SetOrRemoveAnnotation( - SqlServerAnnotationNames.KeySequenceSchema, + SqlServerAnnotationNames.SequenceSchema, Check.NullButNotEmpty(schema, nameof(schema)), fromDataAnnotation); @@ -321,7 +321,7 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch /// The property. /// The for the key value generation sequence schema. public static ConfigurationSource? GetSequenceSchemaConfigurationSource(this IConventionProperty property) - => property.FindAnnotation(SqlServerAnnotationNames.KeySequenceSchema)?.GetConfigurationSource(); + => property.FindAnnotation(SqlServerAnnotationNames.SequenceSchema)?.GetConfigurationSource(); /// /// Finds the in the model to use for the key value generation pattern. @@ -333,10 +333,10 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch var model = property.DeclaringEntityType.Model; var sequenceName = property.GetSequenceName() - ?? model.GetKeySequenceName(); + ?? model.GetSequenceNameSuffix(); var sequenceSchema = property.GetSequenceSchema() - ?? model.GetKeySequenceSchema(); + ?? model.GetSequenceSchema(); return model.FindSequence(sequenceName, sequenceSchema); } @@ -352,10 +352,10 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch var model = property.DeclaringEntityType.Model; var sequenceName = property.GetSequenceName(storeObject) - ?? model.GetKeySequenceName(); + ?? model.GetSequenceNameSuffix(); var sequenceSchema = property.GetSequenceSchema(storeObject) - ?? model.GetKeySequenceSchema(); + ?? model.GetSequenceSchema(); return model.FindSequence(sequenceName, sequenceSchema); } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs index 2fdf42e99de..518f4705f5b 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // ReSharper disable once CheckNamespace + +using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; + namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// @@ -90,7 +93,11 @@ public virtual void ProcessModelFinalizing( if (strategy == SqlServerValueGenerationStrategy.Sequence) { - var sequence = property.FindSequence(declaringTable)!; + var sequence = modelBuilder.HasSequence( + property.GetSequenceName(declaringTable) + ?? entityType.GetRootType().ShortName() + modelBuilder.Metadata.GetSequenceNameSuffix(), + property.GetSequenceSchema(declaringTable) + ?? modelBuilder.Metadata.GetSequenceSchema()).Metadata; property.Builder.HasDefaultValueSql( RelationalDependencies.UpdateSqlGenerator.GenerateObtainNextSequenceValueOperation( diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs index 04d80910cf3..291c07d5bbc 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationNames.cs @@ -73,7 +73,7 @@ public static class SqlServerAnnotationNames /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public const string KeySequenceName = Prefix + "KeySequenceName"; + public const string SequenceNameSuffix = Prefix + "SequenceNameSuffix"; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -81,7 +81,15 @@ public static class SqlServerAnnotationNames /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public const string KeySequenceSchema = Prefix + "KeySequenceSchema"; + public const string SequenceName = Prefix + "SequenceName"; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public const string SequenceSchema = Prefix + "SequenceSchema"; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index ff002ef928a..cab37fa5b3f 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -390,16 +390,16 @@ public virtual void Model_fluent_APIs_for_sequence_key_are_properly_generated() @" modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); - SqlServerModelBuilderExtensions.UseKeySequences(modelBuilder, ""EntityFrameworkKeySequence""); + SqlServerModelBuilderExtensions.UseKeySequences(modelBuilder, ""Sequence""); - modelBuilder.HasSequence(""EntityFrameworkKeySequence""); + modelBuilder.HasSequence(""EntityWithOnePropertySequence""); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => { b.Property(""Id"") .ValueGeneratedOnAdd() .HasColumnType(""int"") - .HasDefaultValueSql(""NEXT VALUE FOR [EntityFrameworkKeySequence]""); + .HasDefaultValueSql(""NEXT VALUE FOR [EntityWithOnePropertySequence]""); SqlServerPropertyBuilderExtensions.UseSequence(b.Property(""Id"")); diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs index 96e284e5eda..f2b7a58063a 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs @@ -3794,8 +3794,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas valueGenerated: ValueGenerated.OnAdd, afterSaveBehavior: PropertySaveBehavior.Throw); id.AddAnnotation(""Relational:DefaultValueSql"", ""NEXT VALUE FOR [KeySeqSchema].[KeySeq]""); - id.AddAnnotation(""SqlServer:KeySequenceName"", ""KeySeq""); - id.AddAnnotation(""SqlServer:KeySequenceSchema"", ""KeySeqSchema""); + id.AddAnnotation(""SqlServer:SequenceName"", ""KeySeq""); + id.AddAnnotation(""SqlServer:SequenceSchema"", ""KeySeqSchema""); id.AddAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.Sequence); var blob = runtimeEntityType.AddProperty( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCFiltersInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCFiltersInheritanceQuerySqlServerTest.cs index 37d0d5184b1..d7f8c805062 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCFiltersInheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCFiltersInheritanceQuerySqlServerTest.cs @@ -181,7 +181,7 @@ public override async Task Can_use_IgnoreQueryFilters_and_GetDatabaseValues(bool @"SELECT TOP(2) [e].[Id], [e].[CountryId], [e].[Name], [e].[Species], [e].[EagleId], [e].[IsFlightless], [e].[Group] FROM [Eagle] AS [e]", // - @"@__p_0='4' + @"@__p_0='1' SELECT TOP(1) [e].[Id], [e].[CountryId], [e].[Name], [e].[Species], [e].[EagleId], [e].[IsFlightless], [e].[Group] FROM [Eagle] AS [e] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCInheritanceQuerySqlServerTest.cs index 0011891fd09..6133c96879a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCInheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCInheritanceQuerySqlServerTest.cs @@ -418,7 +418,7 @@ public override void Setting_foreign_key_to_a_different_type_throws() FROM [Kiwi] AS [k]", // @"@p0='0' -@p1='5' (Nullable = true) +@p1='2' (Nullable = true) @p2='1' @p3='False' @p4='Bald eagle' (Size = 4000) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTFiltersInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTFiltersInheritanceQuerySqlServerTest.cs index 2b4be50b2d7..cba033ec1c4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTFiltersInheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTFiltersInheritanceQuerySqlServerTest.cs @@ -180,7 +180,7 @@ FROM [Animals] AS [a] INNER JOIN [Birds] AS [b] ON [a].[Id] = [b].[Id] INNER JOIN [Eagle] AS [e] ON [a].[Id] = [e].[Id]", // - @"@__p_0='4' + @"@__p_0='1' SELECT TOP(1) [a].[Id], [a].[CountryId], [a].[Name], [a].[Species], [b].[EagleId], [b].[IsFlightless], [e].[Group] FROM [Animals] AS [a] diff --git a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs index 123839bcb23..097746dfe35 100644 --- a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs +++ b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs @@ -199,6 +199,7 @@ public virtual void Detects_duplicate_column_name_with_different_HiLoSequence_sc public virtual void Passes_for_duplicate_column_names_with_KeySequence() { var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity(); modelBuilder.Entity( cb => { @@ -224,13 +225,13 @@ public virtual void Detects_duplicate_column_names_with_different_KeySequence_na cb => { cb.ToTable("Animal"); - cb.Property(c => c.Id).UseSequence("foo"); + cb.Property(c => c.Id).HasColumnName("Id").UseSequence("foo"); }); modelBuilder.Entity( db => { db.ToTable("Animal"); - db.Property(d => d.Id).UseSequence(); + db.Property(d => d.Id).HasColumnName("Id").UseSequence("bar"); db.HasOne().WithOne().HasForeignKey(d => d.Id); }); @@ -238,7 +239,7 @@ public virtual void Detects_duplicate_column_names_with_different_KeySequence_na RelationalStrings.DuplicateColumnNameDefaultSqlMismatch( nameof(Cat), nameof(Cat.Id), nameof(Dog), nameof(Dog.Id), nameof(Cat.Id), nameof(Animal), "NEXT VALUE FOR [foo]", - "NEXT VALUE FOR [EntityFrameworkKeySequence]"), + "NEXT VALUE FOR [bar]"), modelBuilder); } diff --git a/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerValueGenerationStrategyConventionTest.cs b/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerValueGenerationStrategyConventionTest.cs index 1602f2ff1a2..4503944ac98 100644 --- a/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerValueGenerationStrategyConventionTest.cs +++ b/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerValueGenerationStrategyConventionTest.cs @@ -58,19 +58,14 @@ public void Annotations_are_added_when_conventional_model_builder_is_used_with_k model.RemoveAnnotation(CoreAnnotationNames.ProductVersion); var annotations = model.GetAnnotations().OrderBy(a => a.Name).ToList(); - Assert.Equal(4, annotations.Count); + Assert.Equal(3, annotations.Count); Assert.Equal(RelationalAnnotationNames.MaxIdentifierLength, annotations[0].Name); - Assert.Equal( - RelationalAnnotationNames.Sequences, - annotations[1].Name); - Assert.NotNull(annotations[1].Value); - - Assert.Equal(SqlServerAnnotationNames.KeySequenceName, annotations[2].Name); - Assert.Equal(SqlServerModelExtensions.DefaultKeySequenceName, annotations[2].Value); + Assert.Equal(SqlServerAnnotationNames.SequenceNameSuffix, annotations[1].Name); + Assert.Equal(SqlServerModelExtensions.DefaultSequenceNameSuffix, annotations[1].Value); - Assert.Equal(SqlServerAnnotationNames.ValueGenerationStrategy, annotations[3].Name); - Assert.Equal(SqlServerValueGenerationStrategy.Sequence, annotations[3].Value); + Assert.Equal(SqlServerAnnotationNames.ValueGenerationStrategy, annotations[2].Name); + Assert.Equal(SqlServerValueGenerationStrategy.Sequence, annotations[2].Value); } } diff --git a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs index 971c4735205..250e98708a9 100644 --- a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs +++ b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs @@ -370,11 +370,8 @@ public void Can_set_key_sequences_for_model() var sqlServerExtensions = modelBuilder.Model; Assert.Equal(SqlServerValueGenerationStrategy.Sequence, sqlServerExtensions.GetValueGenerationStrategy()); - Assert.Equal(SqlServerModelExtensions.DefaultKeySequenceName, sqlServerExtensions.GetKeySequenceName()); - Assert.Null(sqlServerExtensions.GetKeySequenceSchema()); - - Assert.NotNull(relationalExtensions.FindSequence(SqlServerModelExtensions.DefaultKeySequenceName)); - Assert.NotNull(sqlServerExtensions.FindSequence(SqlServerModelExtensions.DefaultKeySequenceName)); + Assert.Equal(SqlServerModelExtensions.DefaultSequenceNameSuffix, sqlServerExtensions.GetSequenceNameSuffix()); + Assert.Null(sqlServerExtensions.GetSequenceSchema()); } [ConditionalFact] @@ -388,20 +385,8 @@ public void Can_set_key_sequences_with_name_for_model() var sqlServerExtensions = modelBuilder.Model; Assert.Equal(SqlServerValueGenerationStrategy.Sequence, sqlServerExtensions.GetValueGenerationStrategy()); - Assert.Equal("Snook", sqlServerExtensions.GetKeySequenceName()); - Assert.Null(sqlServerExtensions.GetKeySequenceSchema()); - - Assert.NotNull(relationalExtensions.FindSequence("Snook")); - - var sequence = sqlServerExtensions.FindSequence("Snook"); - - Assert.Equal("Snook", sequence.Name); - Assert.Null(sequence.Schema); - Assert.Equal(1, sequence.IncrementBy); - Assert.Equal(1, sequence.StartValue); - Assert.Null(sequence.MinValue); - Assert.Null(sequence.MaxValue); - Assert.Same(typeof(long), sequence.Type); + Assert.Equal("Snook", sqlServerExtensions.GetSequenceNameSuffix()); + Assert.Null(sqlServerExtensions.GetSequenceSchema()); } [ConditionalFact] @@ -411,17 +396,23 @@ public void Can_set_key_sequences_with_schema_and_name_for_model() modelBuilder.UseKeySequences("Snook", "Tasty"); + modelBuilder + .Entity() + .Property(e => e.Id); + + modelBuilder.FinalizeModel(); + var relationalExtensions = modelBuilder.Model; var sqlServerExtensions = modelBuilder.Model; Assert.Equal(SqlServerValueGenerationStrategy.Sequence, sqlServerExtensions.GetValueGenerationStrategy()); - Assert.Equal("Snook", sqlServerExtensions.GetKeySequenceName()); - Assert.Equal("Tasty", sqlServerExtensions.GetKeySequenceSchema()); + Assert.Equal("Snook", sqlServerExtensions.GetSequenceNameSuffix()); + Assert.Equal("Tasty", sqlServerExtensions.GetSequenceSchema()); - Assert.NotNull(relationalExtensions.FindSequence("Snook", "Tasty")); + Assert.NotNull(relationalExtensions.FindSequence("CustomerSnook", "Tasty")); - var sequence = sqlServerExtensions.FindSequence("Snook", "Tasty"); - Assert.Equal("Snook", sequence.Name); + var sequence = sqlServerExtensions.FindSequence("CustomerSnook", "Tasty"); + Assert.Equal("CustomerSnook", sequence.Name); Assert.Equal("Tasty", sequence.Schema); Assert.Equal(1, sequence.IncrementBy); Assert.Equal(1, sequence.StartValue); @@ -448,8 +439,8 @@ public void Can_set_use_of_existing_relational_key_sequence_for_model() var sqlServerExtensions = modelBuilder.Model; Assert.Equal(SqlServerValueGenerationStrategy.Sequence, sqlServerExtensions.GetValueGenerationStrategy()); - Assert.Equal("Snook", sqlServerExtensions.GetKeySequenceName()); - Assert.Equal("Tasty", sqlServerExtensions.GetKeySequenceSchema()); + Assert.Equal("Snook", sqlServerExtensions.GetSequenceNameSuffix()); + Assert.Equal("Tasty", sqlServerExtensions.GetSequenceSchema()); ValidateSchemaNamedSpecificSequence(relationalExtensions.FindSequence("Snook", "Tasty")); ValidateSchemaNamedSpecificSequence(sqlServerExtensions.FindSequence("Snook", "Tasty")); @@ -473,8 +464,8 @@ public void Can_set_use_of_existing_SQL_key_sequence_for_model() var sqlServerExtensions = modelBuilder.Model; Assert.Equal(SqlServerValueGenerationStrategy.Sequence, sqlServerExtensions.GetValueGenerationStrategy()); - Assert.Equal("Snook", sqlServerExtensions.GetKeySequenceName()); - Assert.Equal("Tasty", sqlServerExtensions.GetKeySequenceSchema()); + Assert.Equal("Snook", sqlServerExtensions.GetSequenceNameSuffix()); + Assert.Equal("Tasty", sqlServerExtensions.GetSequenceSchema()); ValidateSchemaNamedSpecificSequence(relationalExtensions.FindSequence("Snook", "Tasty")); ValidateSchemaNamedSpecificSequence(sqlServerExtensions.FindSequence("Snook", "Tasty")); @@ -746,15 +737,15 @@ public void Can_set_key_sequence_for_property() .Property(e => e.Id) .UseSequence(); + modelBuilder.FinalizeModel(); + var model = modelBuilder.Model; var property = model.FindEntityType(typeof(Customer)).FindProperty("Id"); Assert.Equal(SqlServerValueGenerationStrategy.Sequence, property.GetValueGenerationStrategy()); Assert.Equal(ValueGenerated.OnAdd, property.ValueGenerated); - Assert.Equal(SqlServerModelExtensions.DefaultKeySequenceName, property.GetSequenceName()); - Assert.NotNull(model.FindSequence(SqlServerModelExtensions.DefaultKeySequenceName)); - Assert.NotNull(model.FindSequence(SqlServerModelExtensions.DefaultKeySequenceName)); + Assert.NotNull(model.FindSequence(nameof(Customer) + SqlServerModelExtensions.DefaultSequenceNameSuffix)); } [ConditionalFact] @@ -767,6 +758,8 @@ public void Can_set_key_sequences_with_name_for_property() .Property(e => e.Id) .UseSequence("Snook"); + modelBuilder.FinalizeModel(); + var model = modelBuilder.Model; var property = model.FindEntityType(typeof(Customer)).FindProperty("Id"); @@ -798,6 +791,8 @@ public void Can_set_key_sequences_with_schema_and_name_for_property() .Property(e => e.Id) .UseSequence("Snook", "Tasty"); + modelBuilder.FinalizeModel(); + var model = modelBuilder.Model; var property = model.FindEntityType(typeof(Customer)).FindProperty("Id"); diff --git a/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs b/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs index 70d642bf645..9b9ccbe02c3 100644 --- a/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs +++ b/test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs @@ -462,12 +462,12 @@ public void Add_KeySequence_with_seed_data() { var operation = Assert.IsType(o); Assert.Equal("dbo", operation.Schema); - Assert.Equal("EntityFrameworkKeySequence", operation.Name); + Assert.Equal("FireflySequence", operation.Name); }, o => { var operation = Assert.IsType(o); - Assert.Equal("NEXT VALUE FOR [dbo].[EntityFrameworkKeySequence]", operation.DefaultValueSql); + Assert.Equal("NEXT VALUE FOR [dbo].[FireflySequence]", operation.DefaultValueSql); }, o => { @@ -489,7 +489,7 @@ public void Add_KeySequence_with_seed_data() { var operation = Assert.IsType(o); Assert.Equal("dbo", operation.Schema); - Assert.Equal("EntityFrameworkKeySequence", operation.Name); + Assert.Equal("FireflySequence", operation.Name); }, o => { diff --git a/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorSelectorTest.cs b/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorSelectorTest.cs index da17db4b69f..2edf4651e2b 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorSelectorTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorSelectorTest.cs @@ -48,7 +48,6 @@ private void AssertGenerator(string propertyName, bool useHiLo = fals if (useKeySequence) { builder.UseKeySequences(); - Assert.NotNull(builder.Model.FindSequence(SqlServerModelExtensions.DefaultKeySequenceName)); } var model = builder.FinalizeModel();