From 1e974b30b7661120cfef96c55114e92751d47a4d Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Thu, 3 Jun 2021 14:00:10 -0700 Subject: [PATCH] Switch to long identity seed Fixes #24840 --- .../SqlServerAnnotationCodeGenerator.cs | 6 +-- .../SqlServerModelBuilderExtensions.cs | 6 +-- .../Extensions/SqlServerModelExtensions.cs | 8 +-- .../SqlServerPropertyBuilderExtensions.cs | 8 +-- .../Extensions/SqlServerPropertyExtensions.cs | 28 +++++++---- .../Migrations/ModelSnapshotSqlServerTest.cs | 50 +++++++++++++++---- .../SqlServerAnnotationCodeGeneratorTest.cs | 4 +- 7 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs index 78e99f0d9ca..12472c0bfda 100644 --- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs +++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs @@ -152,12 +152,12 @@ protected override bool IsHandledByConvention(IModel model, IAnnotation annotati switch (strategy) { case SqlServerValueGenerationStrategy.IdentityColumn: - var seed = GetAndRemove(annotations, SqlServerAnnotationNames.IdentitySeed) ?? 1; + var seed = GetAndRemove(annotations, SqlServerAnnotationNames.IdentitySeed) ?? 1; var increment = GetAndRemove(annotations, SqlServerAnnotationNames.IdentityIncrement) ?? 1; return new( onModel - ? nameof(SqlServerModelBuilderExtensions.UseIdentityColumns) - : nameof(SqlServerPropertyBuilderExtensions.UseIdentityColumn), + ? "UseIdentityColumns" + : "UseIdentityColumn", (seed, increment) switch { (1, 1) => Array.Empty(), diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs index 934b461c874..1fb5155c2f2 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelBuilderExtensions.cs @@ -108,7 +108,7 @@ public static bool CanSetHiLoSequence( /// The same builder instance so that multiple calls can be chained. public static ModelBuilder UseIdentityColumns( this ModelBuilder modelBuilder, - int seed = 1, + long seed = 1, int increment = 1) { Check.NotNull(modelBuilder, nameof(modelBuilder)); @@ -136,7 +136,7 @@ public static ModelBuilder UseIdentityColumns( /// public static IConventionModelBuilder? HasIdentityColumnSeed( this IConventionModelBuilder modelBuilder, - int? seed, + long? seed, bool fromDataAnnotation = false) { if (modelBuilder.CanSetIdentityColumnSeed(seed, fromDataAnnotation)) @@ -157,7 +157,7 @@ public static ModelBuilder UseIdentityColumns( /// if the given value can be set as the seed for SQL Server IDENTITY. public static bool CanSetIdentityColumnSeed( this IConventionModelBuilder modelBuilder, - int? seed, + long? seed, bool fromDataAnnotation = false) { Check.NotNull(modelBuilder, nameof(modelBuilder)); diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs index bf84229df04..c3c86bceb3d 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs @@ -121,17 +121,17 @@ public static void SetHiLoSequenceSchema(this IMutableModel model, string? value /// /// The model. /// The default identity seed. - public static int GetIdentitySeed(this IReadOnlyModel model) + public static long GetIdentitySeed(this IReadOnlyModel model) => model is RuntimeModel ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData) - : (int?)model[SqlServerAnnotationNames.IdentitySeed] ?? 1; + : (long?)model[SqlServerAnnotationNames.IdentitySeed] ?? 1; /// /// Sets the default identity seed. /// /// The model. /// The value to set. - public static void SetIdentitySeed(this IMutableModel model, int? seed) + public static void SetIdentitySeed(this IMutableModel model, long? seed) => model.SetOrRemoveAnnotation( SqlServerAnnotationNames.IdentitySeed, seed); @@ -143,7 +143,7 @@ public static void SetIdentitySeed(this IMutableModel model, int? seed) /// The value to set. /// Indicates whether the configuration was specified using a data annotation. /// The configured value. - public static int? SetIdentitySeed(this IConventionModel model, int? seed, bool fromDataAnnotation = false) + public static long? SetIdentitySeed(this IConventionModel model, long? seed, bool fromDataAnnotation = false) { model.SetOrRemoveAnnotation( SqlServerAnnotationNames.IdentitySeed, diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs index aadbc9a9e4e..bfa1fe1383b 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs @@ -126,7 +126,7 @@ public static bool CanSetHiLoSequence( /// The same builder instance so that multiple calls can be chained. public static PropertyBuilder UseIdentityColumn( this PropertyBuilder propertyBuilder, - int seed = 1, + long seed = 1, int increment = 1) { Check.NotNull(propertyBuilder, nameof(propertyBuilder)); @@ -152,7 +152,7 @@ public static PropertyBuilder UseIdentityColumn( /// The same builder instance so that multiple calls can be chained. public static PropertyBuilder UseIdentityColumn( this PropertyBuilder propertyBuilder, - int seed = 1, + long seed = 1, int increment = 1) => (PropertyBuilder)UseIdentityColumn((PropertyBuilder)propertyBuilder, seed, increment); @@ -168,7 +168,7 @@ public static PropertyBuilder UseIdentityColumn( /// public static IConventionPropertyBuilder? HasIdentityColumnSeed( this IConventionPropertyBuilder propertyBuilder, - int? seed, + long? seed, bool fromDataAnnotation = false) { if (propertyBuilder.CanSetIdentityColumnSeed(seed, fromDataAnnotation)) @@ -189,7 +189,7 @@ public static PropertyBuilder UseIdentityColumn( /// if the given value can be set as the seed for SQL Server IDENTITY. public static bool CanSetIdentityColumnSeed( this IConventionPropertyBuilder propertyBuilder, - int? seed, + long? seed, bool fromDataAnnotation = false) { Check.NotNull(propertyBuilder, nameof(propertyBuilder)); diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs index 9ca720f18be..6a69f138766 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs @@ -204,10 +204,11 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// /// The property. /// The identity seed. - public static int? GetIdentitySeed(this IReadOnlyProperty property) + public static long? GetIdentitySeed(this IReadOnlyProperty property) => property is RuntimeProperty ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData) - : (int?)property[SqlServerAnnotationNames.IdentitySeed]; + : (long?)property[SqlServerAnnotationNames.IdentitySeed] + ?? property.DeclaringEntityType.Model.GetIdentitySeed(); /// /// Returns the identity seed. @@ -215,7 +216,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// The property. /// The identifier of the store object. /// The identity seed. - public static int? GetIdentitySeed(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) + public static long? GetIdentitySeed(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { if (property is RuntimeProperty) { @@ -225,10 +226,13 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? var annotation = property.FindAnnotation(SqlServerAnnotationNames.IdentitySeed); if (annotation != null) { - return (int?)annotation.Value; + return (long?)annotation.Value; } - return property.FindSharedStoreObjectRootProperty(storeObject)?.GetIdentitySeed(storeObject); + var sharedProperty = property.FindSharedStoreObjectRootProperty(storeObject); + return sharedProperty == null + ? property.DeclaringEntityType.Model.GetIdentitySeed() + : sharedProperty.GetIdentitySeed(storeObject); } /// @@ -236,7 +240,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string? /// /// The property. /// The value to set. - public static void SetIdentitySeed(this IMutableProperty property, int? seed) + public static void SetIdentitySeed(this IMutableProperty property, long? seed) => property.SetOrRemoveAnnotation( SqlServerAnnotationNames.IdentitySeed, seed); @@ -248,9 +252,9 @@ public static void SetIdentitySeed(this IMutableProperty property, int? seed) /// The value to set. /// Indicates whether the configuration was specified using a data annotation. /// The configured value. - public static int? SetIdentitySeed( + public static long? SetIdentitySeed( this IConventionProperty property, - int? seed, + long? seed, bool fromDataAnnotation = false) { property.SetOrRemoveAnnotation( @@ -277,7 +281,8 @@ public static void SetIdentitySeed(this IMutableProperty property, int? seed) public static int? GetIdentityIncrement(this IReadOnlyProperty property) => property is RuntimeProperty ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData) - : (int?)property[SqlServerAnnotationNames.IdentityIncrement]; + : (int?)property[SqlServerAnnotationNames.IdentityIncrement] + ?? property.DeclaringEntityType.Model.GetIdentityIncrement(); /// /// Returns the identity increment. @@ -298,7 +303,10 @@ public static void SetIdentitySeed(this IMutableProperty property, int? seed) return (int?)annotation.Value; } - return property.FindSharedStoreObjectRootProperty(storeObject)?.GetIdentityIncrement(storeObject); + var sharedProperty = property.FindSharedStoreObjectRootProperty(storeObject); + return sharedProperty == null + ? property.DeclaringEntityType.Model.GetIdentityIncrement() + : sharedProperty.GetIdentityIncrement(storeObject); } /// diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index f7a73609acf..6eac43f0675 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -706,7 +706,7 @@ public virtual void Model_use_identity_columns() modelBuilder .HasAnnotation(""Relational:MaxIdentifierLength"", 128) .HasAnnotation(""SqlServer:IdentityIncrement"", 1) - .HasAnnotation(""SqlServer:IdentitySeed"", 1) + .HasAnnotation(""SqlServer:IdentitySeed"", 1L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);"), o => { @@ -727,7 +727,7 @@ public virtual void Model_use_identity_columns_custom_seed() modelBuilder .HasAnnotation(""Relational:MaxIdentifierLength"", 128) .HasAnnotation(""SqlServer:IdentityIncrement"", 1) - .HasAnnotation(""SqlServer:IdentitySeed"", 5) + .HasAnnotation(""SqlServer:IdentitySeed"", 5L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);"), o => { @@ -748,7 +748,7 @@ public virtual void Model_use_identity_columns_custom_increment() modelBuilder .HasAnnotation(""Relational:MaxIdentifierLength"", 128) .HasAnnotation(""SqlServer:IdentityIncrement"", 5) - .HasAnnotation(""SqlServer:IdentitySeed"", 1) + .HasAnnotation(""SqlServer:IdentitySeed"", 1L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);"), o => { @@ -763,20 +763,48 @@ public virtual void Model_use_identity_columns_custom_increment() public virtual void Model_use_identity_columns_custom_seed_increment() { Test( - builder => builder.UseIdentityColumns(5, 5), + builder => { + builder.UseIdentityColumns(long.MaxValue, 5); + builder.Entity( + "Building", b => + { + b.Property("Id"); + + b.HasKey("Id"); + + b.ToTable("Buildings"); + }); + }, AddBoilerPlate( @" modelBuilder .HasAnnotation(""Relational:MaxIdentifierLength"", 128) .HasAnnotation(""SqlServer:IdentityIncrement"", 5) - .HasAnnotation(""SqlServer:IdentitySeed"", 5) - .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn);"), + .HasAnnotation(""SqlServer:IdentitySeed"", 9223372036854775807L) + .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity(""Building"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd() + .HasColumnType(""int"") + .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); + + b.HasKey(""Id""); + + b.ToTable(""Buildings""); + });"), o => { Assert.Equal(4, o.GetAnnotations().Count()); Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, o.GetValueGenerationStrategy()); - Assert.Equal(5, o.GetIdentitySeed()); + Assert.Equal(long.MaxValue, o.GetIdentitySeed()); Assert.Equal(5, o.GetIdentityIncrement()); + + var property = o.FindEntityType("Building").FindProperty("Id"); + Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, property.GetValueGenerationStrategy()); + Assert.Equal(long.MaxValue, property.GetIdentitySeed()); + Assert.Equal(5, property.GetIdentityIncrement()); }); } @@ -3314,7 +3342,7 @@ public virtual void Property_with_identity_column() .ValueGeneratedOnAdd() .HasColumnType(""int"") .HasAnnotation(""SqlServer:IdentityIncrement"", 1) - .HasAnnotation(""SqlServer:IdentitySeed"", 1) + .HasAnnotation(""SqlServer:IdentitySeed"", 1L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); b.HasKey(""Id""); @@ -3354,7 +3382,7 @@ public virtual void Property_with_identity_column_custom_seed() .ValueGeneratedOnAdd() .HasColumnType(""int"") .HasAnnotation(""SqlServer:IdentityIncrement"", 1) - .HasAnnotation(""SqlServer:IdentitySeed"", 5) + .HasAnnotation(""SqlServer:IdentitySeed"", 5L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); b.HasKey(""Id""); @@ -3394,7 +3422,7 @@ public virtual void Property_with_identity_column_custom_increment() .ValueGeneratedOnAdd() .HasColumnType(""int"") .HasAnnotation(""SqlServer:IdentityIncrement"", 5) - .HasAnnotation(""SqlServer:IdentitySeed"", 1) + .HasAnnotation(""SqlServer:IdentitySeed"", 1L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); b.HasKey(""Id""); @@ -3433,7 +3461,7 @@ public virtual void Property_with_identity_column_custom_seed_increment() .ValueGeneratedOnAdd() .HasColumnType(""int"") .HasAnnotation(""SqlServer:IdentityIncrement"", 5) - .HasAnnotation(""SqlServer:IdentitySeed"", 5) + .HasAnnotation(""SqlServer:IdentitySeed"", 5L) .HasAnnotation(""SqlServer:ValueGenerationStrategy"", SqlServerValueGenerationStrategy.IdentityColumn); b.HasKey(""Id""); diff --git a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs index ff286a59a48..a5e002f3b68 100644 --- a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs @@ -172,7 +172,7 @@ public void GenerateFluentApi_IModel_works_with_identity() Assert.Collection( result.Arguments, - seed => Assert.Equal(5, seed), + seed => Assert.Equal(5L, seed), increment => Assert.Equal(10, increment)); } @@ -191,7 +191,7 @@ public void GenerateFluentApi_IProperty_works_with_identity() Assert.Collection( result.Arguments, - seed => Assert.Equal(5, seed), + seed => Assert.Equal(5L, seed), increment => Assert.Equal(10, increment)); }