Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue still exists with 32-bit identity seed values in migrations #26062

Closed
chrissainty opened this issue Sep 16, 2021 · 7 comments · Fixed by #26066
Closed

Issue still exists with 32-bit identity seed values in migrations #26062

chrissainty opened this issue Sep 16, 2021 · 7 comments · Fixed by #26066
Assignees
Labels
area-model-building area-sqlserver closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression type-bug
Milestone

Comments

@chrissainty
Copy link

I've been upgrading our solution from .NET 5 to .NET 6. After upgrading all package to the latest RC1 versions, our applications migrations no longer function. These migrations do work fine under .NET 5.

This is the exception being produced when running the application (migrations are applied during app startup during local development):

[10:13:10 INF] Applying migration '20210729135458_Initial'.
[10:13:10 FTL] Application startup exception
System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Nullable`1[System.Int64]'.
   at Microsoft.EntityFrameworkCore.SqlServerPropertyExtensions.GetIdentitySeed(IReadOnlyProperty property, StoreObjectIdentifier& storeObject)
   at Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal.SqlServerAnnotationProvider.For(IColumn column, Boolean designTime)+MoveNext()
   at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.AddAnnotations(AnnotatableBase annotatable, IEnumerable`1 annotations)
   at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.AddAnnotations(IEnumerable`1 annotations)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel.Create(IModel model, IRelationalAnnotationProvider relationalAnnotationProvider, Boolean designTime)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel.Add(IModel model, IRelationalAnnotationProvider relationalAnnotationProvider, Boolean designTime)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelRuntimeInitializer.InitializeModel(IModel model, Boolean designTime, Boolean prevalidation)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.<>c.<Initialize>b__4_0(ValueTuple`4 args)
   at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.<>c__30`2.<GetOrAddRuntimeAnnotationValue>b__30_0(String n, ValueTuple`3 t)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd[TArg](TKey key, Func`3 valueFactory, TArg factoryArgument)
   at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.GetOrAddRuntimeAnnotationValue[TValue,TArg](String name, Func`2 valueFactory, TArg factoryArgument)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.FinalizeModel(IModel model)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration, MigrationsSqlGenerationOptions options)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass16_2.<GetMigrationCommandLists>b__2()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
   at Deployed.Core.Api.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env, CoreContext context) in C:\Git\Deployed\Platform\src\Core\src\Api\Startup.cs:line 108
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
   at Microsoft.ApplicationInsights.AspNetCore.ApplicationInsightsStartupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Server.IIS.Core.IISServerSetupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.WebTools.BrowserLink.Net.HostingStartup.<>c__DisplayClass1_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Watch.BrowserRefresh.HostingStartup.<>c__DisplayClass1_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

Include provider and version information

EF Core version: 6.0.0-rc.1.21452.10
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 6.0 RC 1

@ajcvickers
Copy link
Member

Duplicate of #25589. Fixed for rc2.

@chrissainty
Copy link
Author

chrissainty commented Sep 16, 2021

@ajcvickers We don't have any of those method calls in our migrations...

.HasAnnotation("SqlServer:IdentitySeed", 1)

We do have

ClusterId = table.Column<int>(type: "int", nullable: false).Annotation("SqlServer:Identity", "1, 1"),

Is there a work around for this?

@ajcvickers
Copy link
Member

/cc @roji

@roji
Copy link
Member

roji commented Sep 16, 2021

This doesn't repro for me for a simple scenario:

  1. Create a simple model with SQL Server identity
  2. Generate a migration via EF Core 5.0
  3. Upgrade to EF Core 6.0 rc1
  4. Apply the migration, creating the database and schema

I have the exact same line in the migration (table.Column<int>(type: "int", nullable: false).Annotation("SqlServer:Identity", "1, 1")), but the migration seems to apply just fine under 6.0.

Can you help us out by providing more details on exactly what you're doing, ideally with some sort of repro?

Attempted repro

Console program

using var ctx = new ClusterContext();
ctx.Database.Migrate();

public class ClusterContext : DbContext
{
    public DbSet<Cluster> Blogs { get; set; }

    static ILoggerFactory ContextLoggerFactory
        => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(@"Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0")
            .EnableSensitiveDataLogging()
            .UseLoggerFactory(ContextLoggerFactory);
}

public class Cluster
{
    public int ClusterId { get; set; }
    public string Name { get; set; }
}

#### Generated migration

Generated up migration:

```c#
migrationBuilder.CreateTable(
    name: "Blogs",
    columns: table => new
    {
        ClusterId = table.Column<int>(type: "int", nullable: false)
            .Annotation("SqlServer:Identity", "1, 1"),
        Name = table.Column<string>(type: "nvarchar(max)", nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Blogs", x => x.ClusterId);
    });

@chrissainty
Copy link
Author

chrissainty commented Sep 16, 2021

@roji I've managed to repro this issue with the following.

  1. Create a .NET 5 console app with the following code:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Logging;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            using var ctx = new ClusterContext();
            ctx.Database.Migrate();
        }
    }

    public class ClusterContext : DbContext
    {
        public DbSet<Cluster> Blogs { get; set; }

        static ILoggerFactory ContextLoggerFactory
            => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.ApplyConfiguration(new ClusterEntityConfiguration());
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .UseSqlServer(@"Server=localhost;Database=test;User=SA;Password=6U6UhlRj3wNvidt8O3AEDAdB;Connect Timeout=60;ConnectRetryCount=0")
                .EnableSensitiveDataLogging()
                .UseLoggerFactory(ContextLoggerFactory);
    }

    public class Cluster
    {
        public int ClusterId { get; set; }
        public string Name { get; set; }
    }

    public class ClusterEntityConfiguration : IEntityTypeConfiguration<Cluster>
    {
        public void Configure(EntityTypeBuilder<Cluster> builder)
        {
            builder.HasKey(x => x.ClusterId).IsClustered(false);
            builder.Property(x => x.ClusterId).UseIdentityColumn();
            builder.HasIndex(x => x.ClusterId).IsUnique().IsClustered();
        }
    }
}
  1. Generate migrations. At this point they can be applied to the DB and everything works fine.
  2. Upgrade project TFM to .NET6.0 and update packages to .NET6 RC1 versions and attempt to apply migrations results in:
Unable to cast object of type 'System.Int32' to type 'System.Nullable`1[System.Int64]'.

I believe the issue is in the .Designer file. When I generated it with .NET 6, this is what was produced:

namespace ConsoleApp1.Migrations
{
    [DbContext(typeof(ClusterContext))]
    [Migration("20210916102727_Repro")]
    partial class Repro
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "6.0.0-rc.1.21452.10")
                .HasAnnotation("Relational:MaxIdentifierLength", 128);

            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);  // Notice this line!!

            modelBuilder.Entity("Cluster", b =>
                {
                    b.Property<int>("ClusterId")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ClusterId"), 1L, 1);

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("nvarchar(max)");

                    b.HasKey("ClusterId");

                    SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("ClusterId"), false);

                    b.HasIndex("ClusterId")
                        .IsUnique();

                    SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("ClusterId"));

                    b.ToTable("Blogs");
                });
#pragma warning restore 612, 618
        }
    }
}

Notice the presence of this line:

SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);

However, when generated with .NET 5 this is what the designer file looks like:

namespace ConsoleApp2.Migrations
{
    [DbContext(typeof(ClusterContext))]
    [Migration("20210916103836_Repro")]
    partial class Repro
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("Relational:MaxIdentifierLength", 128)
                .HasAnnotation("ProductVersion", "5.0.10")
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("ConsoleApp2.Cluster", b =>
                {
                    b.Property<int>("ClusterId")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int")
                        .HasAnnotation("SqlServer:IdentityIncrement", 1)
                        .HasAnnotation("SqlServer:IdentitySeed", 1)
                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

                    b.Property<string>("Name")
                        .HasColumnType("nvarchar(max)");

                    b.HasKey("ClusterId")
                        .IsClustered(false);

                    b.HasIndex("ClusterId")
                        .IsUnique()
                        .IsClustered();

                    b.ToTable("Blogs");
                });
#pragma warning restore 612, 618
        }
    }
}

Notice this line: SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); doesn't exist


The workaround seem to be to update the following which I now realise is what the suggested workaround was in the linked issue 🤦‍♂️

b.Property<int>("ClusterId")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int")
                        .HasAnnotation("SqlServer:IdentityIncrement", 1)
                        .HasAnnotation("SqlServer:IdentitySeed", 1)
                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

to

b.Property<int>("ClusterId")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int")
                        .HasAnnotation("SqlServer:IdentityIncrement", 1)
                        .HasAnnotation("SqlServer:IdentitySeed", 1L)  // Added the "L" here
                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

@roji
Copy link
Member

roji commented Sep 16, 2021

Thanks, this repros for me now, will investigate 👍

@roji
Copy link
Member

roji commented Sep 16, 2021

This does seem to be the same as #25589, but the fix there is in the snapshot model processor, which only seems to get invoked when scaffolding or removing a migration; when applying migrations we don't go through the processor.

@AndriySvyryd I've submitted #26066 which adds the proper int->long conversions in the metadata API and the annotation code generator, plus a test - take a look.

@chrissainty you can indeed work around this by editing your model snapshots and adding the L to make the value a long.

roji added a commit that referenced this issue Sep 16, 2021
@ajcvickers ajcvickers added this to the 6.0.0-rc2 milestone Sep 17, 2021
@ajcvickers ajcvickers added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug labels Sep 17, 2021
roji added a commit that referenced this issue Sep 18, 2021
@roji roji closed this as completed Sep 18, 2021
@roji roji changed the title Upgrading packages from .NET 5 to .NET 6 RC 1 seems to break existing migrations Issue still exists with 32-bit identity seed values in migrations Sep 19, 2021
@ajcvickers ajcvickers modified the milestones: 6.0.0-rc2, 6.0.0 Nov 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building area-sqlserver closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants