Entity Framework Migrations renaming tables and columns - c#

I renamed a a couple entities and their navigation properties and generated a new Migration in EF 5. As is usual with renames in EF migrations, by default it was going to drop objects and recreate them. That isn't what I wanted so I pretty much had to build the migration file from scratch.
public override void Up()
{
DropForeignKey("dbo.ReportSectionGroups", "Report_Id", "dbo.Reports");
DropForeignKey("dbo.ReportSections", "Group_Id", "dbo.ReportSectionGroups");
DropForeignKey("dbo.Editables", "Section_Id", "dbo.ReportSections");
DropIndex("dbo.ReportSectionGroups", new[] { "Report_Id" });
DropIndex("dbo.ReportSections", new[] { "Group_Id" });
DropIndex("dbo.Editables", new[] { "Section_Id" });
RenameTable("dbo.ReportSections", "dbo.ReportPages");
RenameTable("dbo.ReportSectionGroups", "dbo.ReportSections");
RenameColumn("dbo.ReportPages", "Group_Id", "Section_Id");
AddForeignKey("dbo.ReportSections", "Report_Id", "dbo.Reports", "Id");
AddForeignKey("dbo.ReportPages", "Section_Id", "dbo.ReportSections", "Id");
AddForeignKey("dbo.Editables", "Page_Id", "dbo.ReportPages", "Id");
CreateIndex("dbo.ReportSections", "Report_Id");
CreateIndex("dbo.ReportPages", "Section_Id");
CreateIndex("dbo.Editables", "Page_Id");
}
public override void Down()
{
DropIndex("dbo.Editables", "Page_Id");
DropIndex("dbo.ReportPages", "Section_Id");
DropIndex("dbo.ReportSections", "Report_Id");
DropForeignKey("dbo.Editables", "Page_Id", "dbo.ReportPages");
DropForeignKey("dbo.ReportPages", "Section_Id", "dbo.ReportSections");
DropForeignKey("dbo.ReportSections", "Report_Id", "dbo.Reports");
RenameColumn("dbo.ReportPages", "Section_Id", "Group_Id");
RenameTable("dbo.ReportSections", "dbo.ReportSectionGroups");
RenameTable("dbo.ReportPages", "dbo.ReportSections");
CreateIndex("dbo.Editables", "Section_Id");
CreateIndex("dbo.ReportSections", "Group_Id");
CreateIndex("dbo.ReportSectionGroups", "Report_Id");
AddForeignKey("dbo.Editables", "Section_Id", "dbo.ReportSections", "Id");
AddForeignKey("dbo.ReportSections", "Group_Id", "dbo.ReportSectionGroups", "Id");
AddForeignKey("dbo.ReportSectionGroups", "Report_Id", "dbo.Reports", "Id");
}
All I'm trying to do is rename dbo.ReportSections to dbo.ReportPages and then dbo.ReportSectionGroups to dbo.ReportSections. Then I need to rename the foreign key column on dbo.ReportPages from Group_Id to Section_Id.
I am dropping the foreign keys and indexes linking the tables together, then I am renaming the tables and the foreign key column, then I'm adding the indexes and foreign keys again. I assumed this was going to work but I am getting a SQL error.
Msg 15248, Level 11, State 1, Procedure sp_rename, Line 215
Either the parameter #objname is ambiguous or the claimed #objtype (COLUMN) is wrong.
Msg 4902, Level 16, State 1, Line 10
Cannot find the object "dbo.ReportSections" because it does not exist or you do not have permissions.
I'm not having an easy time figuring out what is wrong here. Any insight would be tremendously helpful.

Nevermind. I was making this way more complicated than it really needed to be.
This was all that I needed. The rename methods just generate a call to the sp_rename system stored procedure and I guess that took care of everything, including the foreign keys with the new column name.
public override void Up()
{
RenameTable("ReportSections", "ReportPages");
RenameTable("ReportSectionGroups", "ReportSections");
RenameColumn("ReportPages", "Group_Id", "Section_Id");
}
public override void Down()
{
RenameColumn("ReportPages", "Section_Id", "Group_Id");
RenameTable("ReportSections", "ReportSectionGroups");
RenameTable("ReportPages", "ReportSections");
}

If you don't like writing/changing the required code in the Migration class manually, you can follow a two-step approach which automatically make the RenameColumn code which is required:
Step One Use the ColumnAttribute to introduce the new column name and then add-migration (e.g. Add-Migration ColumnChanged)
public class ReportPages
{
[Column("Section_Id")] //Section_Id
public int Group_Id{get;set}
}
Step-Two change the property name and again apply to same migration (e.g. Add-Migration ColumnChanged -force) in the Package Manager Console
public class ReportPages
{
[Column("Section_Id")] //Section_Id
public int Section_Id{get;set}
}
If you look at the Migration class you can see the automatically code generated is RenameColumn.

In EF Core, I use the following statements to rename tables and columns:
As for renaming tables:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameTable(
name: "OldTableName",
schema: "dbo",
newName: "NewTableName",
newSchema: "dbo");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameTable(
name: "NewTableName",
schema: "dbo",
newName: "OldTableName",
newSchema: "dbo");
}
As for renaming columns:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "OldColumnName",
table: "TableName",
newName: "NewColumnName",
schema: "dbo");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "NewColumnName",
table: "TableName",
newName: "OldColumnName",
schema: "dbo");
}

To expand a bit on Hossein Narimani Rad's answer, you can rename both a table and columns using System.ComponentModel.DataAnnotations.Schema.TableAttribute and System.ComponentModel.DataAnnotations.Schema.ColumnAttribute respectively.
This has a couple benefits:
Not only will this create the the name migrations automatically, but
it will also deliciously delete any foreign keys and recreate them against the new table and column names, giving the foreign keys and constaints proper names.
All this without losing any table data
For example, adding [Table("Staffs")]:
[Table("Staffs")]
public class AccountUser
{
public long Id { get; set; }
public long AccountId { get; set; }
public string ApplicationUserId { get; set; }
public virtual Account Account { get; set; }
public virtual ApplicationUser User { get; set; }
}
Will generate the migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AccountUsers_Accounts_AccountId",
table: "AccountUsers");
migrationBuilder.DropForeignKey(
name: "FK_AccountUsers_AspNetUsers_ApplicationUserId",
table: "AccountUsers");
migrationBuilder.DropPrimaryKey(
name: "PK_AccountUsers",
table: "AccountUsers");
migrationBuilder.RenameTable(
name: "AccountUsers",
newName: "Staffs");
migrationBuilder.RenameIndex(
name: "IX_AccountUsers_ApplicationUserId",
table: "Staffs",
newName: "IX_Staffs_ApplicationUserId");
migrationBuilder.RenameIndex(
name: "IX_AccountUsers_AccountId",
table: "Staffs",
newName: "IX_Staffs_AccountId");
migrationBuilder.AddPrimaryKey(
name: "PK_Staffs",
table: "Staffs",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Staffs_Accounts_AccountId",
table: "Staffs",
column: "AccountId",
principalTable: "Accounts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Staffs_AspNetUsers_ApplicationUserId",
table: "Staffs",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Staffs_Accounts_AccountId",
table: "Staffs");
migrationBuilder.DropForeignKey(
name: "FK_Staffs_AspNetUsers_ApplicationUserId",
table: "Staffs");
migrationBuilder.DropPrimaryKey(
name: "PK_Staffs",
table: "Staffs");
migrationBuilder.RenameTable(
name: "Staffs",
newName: "AccountUsers");
migrationBuilder.RenameIndex(
name: "IX_Staffs_ApplicationUserId",
table: "AccountUsers",
newName: "IX_AccountUsers_ApplicationUserId");
migrationBuilder.RenameIndex(
name: "IX_Staffs_AccountId",
table: "AccountUsers",
newName: "IX_AccountUsers_AccountId");
migrationBuilder.AddPrimaryKey(
name: "PK_AccountUsers",
table: "AccountUsers",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_AccountUsers_Accounts_AccountId",
table: "AccountUsers",
column: "AccountId",
principalTable: "Accounts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AccountUsers_AspNetUsers_ApplicationUserId",
table: "AccountUsers",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}

In ef core, you can change the migration that was created after add migration. And then do update-database. A sample has given below:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(name: "Type", table: "Users", newName: "Discriminator", schema: "dbo");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(name: "Discriminator", table: "Users", newName: "Type", schema: "dbo");
}

For EF Core migrationBuilder.RenameColumn usually works fine but sometimes you have to handle indexes as well.
migrationBuilder.RenameColumn(name: "Identifier", table: "Questions", newName: "ChangedIdentifier", schema: "dbo");
Example error message when updating database:
Microsoft.Data.SqlClient.SqlException (0x80131904): The index
'IX_Questions_Identifier' is dependent on column 'Identifier'.
The index 'IX_Questions_Identifier' is dependent on column 'Identifier'.
RENAME COLUMN Identifier failed because one or more objects access
this column.
In this case you have to do the rename like this:
migrationBuilder.DropIndex(
name: "IX_Questions_Identifier",
table: "Questions");
migrationBuilder.RenameColumn(name: "Identifier", table: "Questions", newName: "ChangedIdentifier", schema: "dbo");
migrationBuilder.CreateIndex(
name: "IX_Questions_ChangedIdentifier",
table: "Questions",
column: "ChangedIdentifier",
unique: true,
filter: "[ChangedIdentifier] IS NOT NULL");

I just tried the same in EF6 (code first entity rename). I simply renamed the class and added a migration using the package manager console and voila, a migration using RenameTable(...) was automatically generated for me. I have to admit that I made sure the only change to the entity was renaming it so no new columns or renamed columns so I cannot be certain if this is an EF6 thing or just that EF was (always) able to detect such simple migrations.

Table names and column names can be specified as part of the mapping of DbContext. Then there is no need to do it in migrations.
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Restaurant>()
.HasMany(p => p.Cuisines)
.WithMany(r => r.Restaurants)
.Map(mc =>
{
mc.MapLeftKey("RestaurantId");
mc.MapRightKey("CuisineId");
mc.ToTable("RestaurantCuisines");
});
}
}

Hossein Narimani Rad answer is really nice and straightforward. But it doesn't work for the EF core. because dotnet ef migration add doesn't have the --force option.
You have to do it this way.
1- add [Column("NewColumnName")]
2- create a migration dotnet ef migration add RenameSomeColumn
3- copy all the code in RenameSomeColumn.cs
4- remove migration dotnet ef migrations remove
5- remove [Column("NewColumnName")] and rename Property to NewColumnName
6- again create migration dotnet ef migration add RenameSomeColumn
7- past all the code copied from removed migration into new RenameSomeColumn.cs

Related

Entity Framework Apply Migrations Programatically

I am trying to apply migrations through c# code using entity framework.
I want to drop certain tables(not all of them) and recreate programatically.
I have something like this:
try
{
await dbContext.Database.ExecuteSqlRawAsync("drop table if exists results");
await dbContext.GetInfrastructure().GetService<IMigrator().MigrateAsync("20230104163600_AfterSync");
}
catch (Exception e)
{
throw;
}
I try to run specific custom migration which contains only some of the entities of the DbContext.
Here's the migration:
[DbContext(typeof(TempDbContext))]
[Migration("20230104163600_AfterSync")]
public partial class AfterSync : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "results",
columns: table => new
{
id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
groupId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("pK_results", x => x.id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "results");
}
}
If I remove the line
await dbContext.Database.ExecuteSqlRawAsync("drop table if exists results");
It says:
Microsoft.Data.SqlClient.SqlException: 'There is already an object named 'results' in the database.'
If I remove the override of Down method in the migration and leave the previous line, the application just crashes.
Can someone help how I can drop and recreate only certain tables not all of them withtout using the database.Migrate() method. Thanks

How can I 'disable' a computed column so that I can apply a migration?

I have this migration
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<bool>(
name: "CancelledVisit",
table: "Activities",
type: "bit",
nullable: false,
defaultValue: false,
oldClrType: typeof(bool),
oldType: "bit",
oldNullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<bool>(
name: "CancelledVisit",
table: "Activities",
type: "bit",
nullable: true,
oldClrType: typeof(bool),
oldType: "bit");
}
Where IsComplete is a computed column, and it uses CancelledVisit in its calculation.
When running this migration I get the error
The column 'IsComplete' is dependent on column 'CancelledVisit'.
ALTER TABLE ALTER COLUMN CancelledVisit failed because one or more objects access this column.
I thought about removing the record from sys.computed_columns and then re-adding it after the migration is done, but I get an error when trying to insert to that table:
Ad hoc updates to system catalogs are not allowed.
So I'm wondering what I need to do in order to simply run the above migration, which essentially just makes the CancelledVisit column non-nullable
I updated my migration to this and it seems to work
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn("IsComplete", "Activities");
migrationBuilder.AlterColumn<bool>(
name: "CancelledVisit",
table: "Activities",
type: "bit",
nullable: false,
defaultValue: false,
oldClrType: typeof(bool),
oldType: "bit",
oldNullable: true);
migrationBuilder.AddColumn<bool>(
name: "IsComplete",
table: "Activities",
computedColumnSql: comp);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn("IsComplete", "Activities");
migrationBuilder.AlterColumn<bool>(
name: "CancelledVisit",
table: "Activities",
type: "bit",
nullable: true,
oldClrType: typeof(bool),
oldType: "bit");
migrationBuilder.AddColumn<bool>(
name: "IsComplete",
table: "Activities",
computedColumnSql: comp);
}
Where comp is the SQL string used to generate the computed value

EF-Core: Table "name" already exists - when trying to update database

ASP Core 3.1 - API. I'm using the latest version of Entity Framework Core.
I have created a table ToDoItem and a ToDoItemContext. After creating the initial migration, and running update-database. I now have that table in my database. I now added a new model called: ToDoItemDescription.
When I try to update the database after creating a new migration, I get the error:
Table 'todoitems' already exists
Further details: I have two contexts, and this is the command I ran:
update-database -context todoitemscontext
I also tried:
update-database -context todoitemscontext -migration AddDescription
Here is my full code:
Models:
public class TodoItem : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
bool IsComplete { get; set; }
}
public class ToDoItemDescription
{
public int id { get; set; }
public string Description { get; set; }
//public int ToDoItemId { get; set; }
public TodoItem TodoItem { get; set; }
}
Context:
public class TodoItemsContext : DbContext
{
public TodoItemsContext(DbContextOptions<TodoItemsContext> options) : base(options) { }
public DbSet<TodoItem> TodoItems { get; set; }
public DbSet<ToDoItemDescription> TodoItemsDescription { get; set; }
}
Migrations:
[DbContext(typeof(TodoItemsContext))]
partial class TodoItemsContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder) {
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("project.Models.ToDoItemDescription", b => {
b.Property<int>("id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Description")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<long?>("TodoItemId")
.HasColumnType("bigint");
b.HasKey("id");
b.HasIndex("TodoItemId");
b.ToTable("TodoItemsDescription");
});
modelBuilder.Entity("project.Models.TodoItem", b => {
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<bool>("IsComplete")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.HasKey("Id");
b.ToTable("TodoItems");
});
modelBuilder.Entity("project.Models.ToDoItemDescription", b =>
{
b.HasOne("project.Models.TodoItem", "TodoItem")
.WithMany()
.HasForeignKey("TodoItemId");
});
#pragma warning restore 612, 618
}
public partial class TodoItems_Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TodoItems",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(nullable: true),
IsComplete = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoItems", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TodoItems");
}
}
public partial class AddDescription : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TodoItemsDescription",
columns: table => new
{
id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Description = table.Column<string>(nullable: true),
TodoItemId = table.Column<long>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoItemsDescription", x => x.id);
table.ForeignKey(
name: "FK_TodoItemsDescription_TodoItems_TodoItemId",
column: x => x.TodoItemId,
principalTable: "TodoItems",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_TodoItemsDescription_TodoItemId",
table: "TodoItemsDescription",
column: "TodoItemId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TodoItemsDescription");
}
}
Thank you.
This happens if you have created the database upfront without migrations, for example by using DbContext.Database.EnsureCreated();.
This usually happens when you have a migration that creates a table and the required table is already present in your database so, when you update the database from classes in Migration, it will try to create a table and will fail because the Create command will not be executed as it already has that specific table.
So, in order to avoid the error, you might want to remove the migration class or comment the code in Up() method of that class so it doesn't execute that specific create command.
It could possible help people working with MySQL databases either on Linux and Windows
TL;DR;
I had to rename the table
__efmigrationshistory (note the lowercase) to
__EFMigrationsHistory (note the case)
so the command-line dotnet-ef database update managed to verify all the migrations present on the table __EFMigrationsHistory, and therefore, creating the new field on the table, say Tenant
More
I have to work on Linux, Windows, MacOs boxes. Primarily using Visual Studio code and .net core 3.1.xxx
I use the code-first approach. The MySQL database was firstly, create on the Windows box, where all the tables were created lower cased
Switching to the Linux box, I realized the case was important, so, say, table "tenant" was renamed to "Tenant", by hand.
Once I had to create a new field on the Tenant's c# class, I ran:
dotnet-ef migrations add new-ftpSettings-field and dotnet-ef database update, I got table "Order" already exists. Note I was trying to insert a new field to the "Tenant" table
After a lot of investigation and search, I decided to refresh the database again, and I saw "two suspicious tables" __efmigrationshistory and __EFMigrationsHistory.
I renamed the empty table __EFMigrationsHistory to like Table1 (as a backup), and thus renamed the table __efmigrationshistory to __EFMigrationsHistory
I ran the dotnet-ef database update and the field was properly added to the MySQL database.
*** Like you might have figured this out, running the command-line dotnet-ef database update on Linux was creating a new (and) empty table __EFMigrationsHistory to MySQL database while it had already, a lower cased table on __efmigrationshistory (the good one, created on my Windows box, with all the migrations).
*** This is my first contribution. Any advice is welcome!
Keep safe! Tchau/Au revoir!
I was working through the migration tutorial and had made a mistake sometimes around these steps
dotnet ef migrations add AddBlogCreatedTimestamp
dotnet ef database update
I did the following
deleted the files AddBlogCreatedTimestamp.Designer.cs and AddBlogCreatedTimestamp.cs
inside blogging.db in the table __EFMigrationsHistory i deleted the row that contains 2023__***__AddBlogCreatedTimestamp this was the migration step that failed.
I repeated the migration step dotnet ef migrations add ...
then manually added DropTable(...) to AddBlogCreatedTimestamp.Up()
only then i ran dotnet ef database update
This made sure that in an up-migration the tables would be deleted
Code manually changed
public partial class AddBlogCreatedTimestamp : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// manually added
migrationBuilder.DropTable(name: "Posts");
migrationBuilder.DropTable(name: "Blogs");
// ... other lines that were created
}
// more other code ...
}
What i still not get is why this is needed. I am not aware to have used anything like EnsureCreated

EFCore AlterColumn MaxLength in Code First Migration

Using Visual Studio 2019 C#.
I'm very new to a code first development approach.
I've made a working ToDo.cs data model and ApplicationDbContext.cs. I ran PM>add-migration to auto-generate the data migration below and then PM>database-update to commit the table to my SQL Server successfully.
FILENAME: 20200815211807_AddedToDoTable.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BlazorApp.Data.Migrations
{
public partial class AddedToDoTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ToDoList",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(maxLength: 15, nullable: false),
Status = table.Column<string>(nullable: false),
DueDate = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ToDoList", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ToDoList");
}
}
}
Now I need to change the string length of the [Name] column from 15 to 25. So, I created a new migration like this:
using Microsoft.EntityFrameworkCore.Migrations;
namespace BlazorApp.Data.Migrations
{
public partial class ChangeNameLength : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("ALTER TABLE ToDoList ALTER COLUMN Name nvarchar(25);");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
But the Package Manager says: "Object reference not set to an instance of an object." And doesn't make any changes.
What am I doing wrong? Can you help me understand this simple change?
Thanks, Jason
I think my problem had to do with my usage of the Package Manager Console.
I started over using the same migration script and everything worked fine!
PM> add-migration "ChangedNameLength"
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Done.
PM>
Why don't you use the correct migration syntax:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "ToDoList",
maxLength: 25);
}

EFCore roll back migration error: column of invalid type to be used as key column

I'm running into an issue where I'm trying to rollback a migration on a database that already has the migration applied.
This is the error I get:
Failed executing DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE [EventStaffRequest] ADD CONSTRAINT [PK_EventStaffRequest] PRIMARY KEY ([Id]);
System.Data.SqlClient.SqlException (0x80131904): Column 'Id' in table 'EventStaffRequest' is of a type that is invalid for use as a key column in an index.
Could not create constraint or index. See previous errors.
ClientConnectionId:29574816-2b1a-4490-a216-a54cd7a2d33b
Error Number:1919,State:1,Class:16
Column 'Id' in table 'EventStaffRequest' is of a type that is invalid for use as a key column in an index.
Could not create constraint or index. See previous errors.
This is the migration I'm trying to rollback:
public partial class AddedCompositeKeyToEventStaffRequest : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_EventStaffRequest",
table: "EventStaffRequest");
migrationBuilder.DropIndex(
name: "IX_EventStaffRequest_EventId",
table: "EventStaffRequest");
migrationBuilder.DropColumn(
name: "Id",
table: "EventStaffRequest");
migrationBuilder.AddPrimaryKey(
name: "PK_EventStaffRequest",
table: "EventStaffRequest",
columns: new[] { "EventId", "QualityTypeId" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_EventStaffRequest",
table: "EventStaffRequest");
migrationBuilder.AddColumn<string>(
name: "Id",
table: "EventStaffRequest",
nullable: false,
defaultValue: "");
migrationBuilder.AddPrimaryKey(
name: "PK_EventStaffRequest",
table: "EventStaffRequest",
column: "Id");
migrationBuilder.CreateIndex(
name: "IX_EventStaffRequest_EventId",
table: "EventStaffRequest",
column: "EventId");
}
}
If it is relevant, this is my model code:
public class EventStaffRequest
{
[Required]
public string EventId { get; set; }
public virtual Event Event { get; set; }
[Required]
public string QualityTypeId { get; set; }
public virtual QualityType QualityType { get; set; }
[Required]
public int AmountRequired { get; set; }
[Required]
public int MinimumRating { get; set; }
}
This migration was created because I decided I needed to change the primary key to be a composite key. I have applied a composite primary key like so in my DbContext (this is what you see in the migration's Up() I guess):
builder.Entity<EventStaffRequest>()
.HasKey(esr => new { esr.EventId, esr.QualityTypeId });
Why is my rollback not succeeding? I don't understand why string is not an appropriate type for key index (I use GUIDs as keys).
There seem to be an issue with migration system. The problem is not the string (of course it can be used for PK), but the maxLength. By default, the string columns have unlimited length, but PK require applying some limit.
Normally when you use string column as PK, even if you don't specify maxLength, EF automatically applies some limit (from what I see, at least for SqlServer it is 450). Interestingly, doing the same what you did in reverse order generates similar migrations with Up and Down content swapped, and the exact same code for AddColumn works. But not when executed in Down method, so there must be a difference (hence a problem) in that path. You might consider posting it in EF Core issue tracker so they know (and eventually fix it).
Anyway, the solution is to explicitly add maxLength parameter to AddColumn call:
migrationBuilder.AddColumn<string>(
name: "Id",
table: "EventStaffRequest",
maxLength: 450,
nullable: false,
defaultValue: "");

Categories