dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.72k stars 3.17k forks source link

Null exception on AlterColumn in 2.1 #12504

Closed christamlyn-bridge closed 1 year ago

christamlyn-bridge commented 6 years ago

After upgrading to EF 2.1, an existing migration throws an null reference exception on a migration to Alter Column type.

{
    "Timestamp": "2018-06-28T16:12:10.7220699+01:00",
    "Level": "Fatal",
    "MessageTemplate": "Failed to migrate database.",
    "RenderedMessage": "Failed to migrate database.",
    "Exception": "System.NullReferenceException: Object reference not set to an instance of an object.\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.GetColumnType(String schema, String table, String name, Type clrType, Nullable`1 unicode, Nullable`1 maxLength, Nullable`1 fixedLength, Boolean rowVersion, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.<>c.<.cctor>b__72_4(MigrationsSqlGenerator g, MigrationOperation o, IModel m, MigrationCommandListBuilder b)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass13_2.<GetMigrationCommandLists>b__2()\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateAsync(String targetMigration, CancellationToken cancellationToken)\r\n   at Bridge.Framework.EntityFramework.WebHostExtensions.MigrateDatabase[T](T dbContext, ILogger`1 logger)",
    "Properties": {
        "SourceContext": "Bridge.EmployeeAttendance.Api.Repositories.EntityFramework.IApplicationDbContext",
        "EnvironmentUserName": "DESKTOP-0EKMJ8N\\daniel.campos",
        "MachineName": "DESKTOP-0EKMJ8N",
        "ProcessId": 6768,
        "ThreadId": 4,
        "Format": "json",
        "ExceptionDetail": {
            "Type": "System.NullReferenceException",
            "HResult": -2147467261,
            "Message": "Object reference not set to an instance of an object.",
            "Source": "Microsoft.EntityFrameworkCore.Relational",
            "StackTrace": "   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.GetColumnType(String schema, String table, String name, Type clrType, Nullable`1 unicode, Nullable`1 maxLength, Nullable`1 fixedLength, Boolean rowVersion, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.<>c.<.cctor>b__72_4(MigrationsSqlGenerator g, MigrationOperation o, IModel m, MigrationCommandListBuilder b)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)\r\n   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass13_2.<GetMigrationCommandLists>b__2()\r\n   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateAsync(String targetMigration, CancellationToken cancellationToken)\r\n   at Bridge.Framework.EntityFramework.WebHostExtensions.MigrateDatabase[T](T dbContext, ILogger`1 logger)"
        },
        "Environment": "Development",
        "Country": "Global",
        "Source": "Bridge Employee Attendance API"
    }
}

Steps to reproduce

protected override void Up(MigrationBuilder migrationBuilder) =>
            migrationBuilder.AlterColumn<DateTimeOffset>(
                name: "Date",
                schema: "schema",
                table: "ColumnName",
                type: "DATE",
                nullable: false,
                oldClrType: typeof(DateTimeOffset));

        protected override void Down(MigrationBuilder migrationBuilder) =>
            migrationBuilder.AlterColumn<DateTimeOffset>(
                name: "Date",
                schema: "schema",
                table: "ColumnName",
                nullable: false,
                oldClrType: typeof(DateTimeOffset),
                oldType: "DATE");

To get the migration to work in 2.1 required to explicitly define oldType in the Up. Is this now mandatory? It seems to have worked in previous versions without.

The following migrates successfully:

protected override void Up(MigrationBuilder migrationBuilder) => migrationBuilder.AlterColumn( name: "Date", schema: "schema", table: "ColumnName", type: "DATE", nullable: false, oldType: "DATETIMEOFFSET", oldClrType: typeof(DateTimeOffset));

    protected override void Down(MigrationBuilder migrationBuilder) =>
        migrationBuilder.AlterColumn<DateTimeOffset>(
            name: "Date",
            schema: "schema",
            table: "ColumnName",
            nullable: false,
            oldClrType: typeof(DateTimeOffset),
            oldType: "DATE");

### Further technical details
EF Core version: 2.1
Database Provider: SQL Server
Operating system:  Windows

The documentation suggests 
ajcvickers commented 6 years ago

@christamlyn-bridge Can you post the full stack trace?

bricelam commented 6 years ago
System.NullReferenceException: Object reference not set to an instance of an object.
  at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.GetColumnType(String schema, String table, String name, Type clrType, Nullable`1 unicode, Nullable`1 maxLength, Nullable`1 fixedLength, Boolean rowVersion, IModel model)
  at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
  at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.<>c.<.cctor>b__72_4(MigrationsSqlGenerator g, MigrationOperation o, IModel m, MigrationCommandListBuilder b)
  at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
  at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)
  at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)
  at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)
  at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass13_2.<GetMigrationCommandLists>b__2()
  at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateAsync(String targetMigration, CancellationToken cancellationToken)
  at Bridge.Framework.EntityFramework.WebHostExtensions.MigrateDatabase[T](T dbContext, ILogger`1 logger)"
bricelam commented 6 years ago

@christamlyn-bridge What version of EF was used to generate this migration? (Look for modelBuilder.HasAnnotation("ProductVersion", "X.X.X") in the designer file.) Or was is handcrafted?

Parmie commented 5 years ago

I got this error with ProductVersion "2.2.3-servicing-35854". My situation looks the same. I'm trying to change a DateTime to DateTimeOffset, as advised in #4711. After changing the type on one property, I generated this migration:

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AlterColumn<DateTimeOffset>(
                name: "TimeStamp",
                table: "StorageWeightChanges",
                nullable: false,
                oldClrType: typeof(DateTime));
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AlterColumn<DateTime>(
                name: "TimeStamp",
                table: "StorageWeightChanges",
                nullable: false,
                oldClrType: typeof(DateTimeOffset));
        }

Stack trace looks the same as the one above:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.GetColumnType(String schema, String table, String name, Type clrType, Nullable`1 unicode, Nullable`1 maxLength, Nullable`1 fixedLength, Boolean rowVersion, IModel model)
   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.<>c.<.cctor>b__73_4(MigrationsSqlGenerator g, MigrationOperation o, IModel m, MigrationCommandListBuilder b)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)   at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass13_2.<GetMigrationCommandLists>b__2()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
bricelam commented 5 years ago

I'm not able to reproduce either case on 2.1.11 or 2.2.6. If anyone still has a project they can share, I can investigate further...

Kolky commented 4 years ago

I had the same issue (on a migration to 3.1.2, coming from 2.2.6); I fixed it by specifying on the mapping .HasColumnType("datetimeoffset(7)"). This added type: "datetimeoffset(7)" to each AlterColumn statement, which the code seemed to expect. As you can see in @Parmie's code he doesn't have that!