I am new to EF5 Code First and I'm tinkering with a proof-of-concept before embarking on a project at work.
I have initially created a model that looked something like
public class Person {
public int Id { get; set; }
public string FirstName { get; set;}
public string Surname {get;set;}
public string Location {get;set;}
}
And I added a few records using a little MVC application I stuck on the top.
Now I want to change the Location column to an enum, something like:
public class Person {
public int Id { get; set; }
public string FirstName { get; set;}
public string Surname {get;set;}
public Locations Location {get;set;}
}
public enum Locations {
London = 1,
Edinburgh = 2,
Cardiff = 3
}
When I add the new migration I get:
AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));
but when I run update-database I get an error
Conversion failed when converting the nvarchar value 'London' to data type int.
Is there a way in the migration to truncate the table before it runs the alter statement?
I know I can open the database and manually do it, but is there a smarter way?
This can be achieved in several ways: setting the EntityState for the entity explicitly; using the DbContext. Update method (which is new in EF Core); using the DbContext. Attach method and then "walking the object graph" to set the state of individual properties within the graph explicitly.
State = EntityState. Modified; , you are not only attaching the entity to the DbContext , you are also marking the whole entity as dirty. This means that when you do context. SaveChanges() , EF will generate an update statement that will update all the fields of the entity.
Primary Key Convention Code First infers that a property is a primary key if a property on a class is named “ID” (not case sensitive), or the class name followed by "ID". If the type of the primary key property is numeric or GUID it will be configured as an identity column.
To update this entity we need to attach the Department to the context and inform it to mark its status as Modified . Now if we call the SaveChanges method, the context will send an update query to the database.
The smartest way is probably to not alter types. If you need to do this, I'd suggest you to do the following steps:
Sql()
to take over the data from the original column using an update statementThis can all be done in the same migration, the correct SQL script will be created. You can skip step 2 if you want your data to be discarded. If you want to take it over, add the appropriate statement (can also contain a switch statement).
Unfortunately Code First Migrations do not provide easier ways to accomplish this.
Here is the example code:
AddColumn("dbo.People", "LocationTmp", c => c.Int(nullable: false));
Sql(@"
UPDATE dbp.People
SET LocationTmp =
CASE Location
WHEN 'London' THEN 1
WHEN 'Edinburgh' THEN 2
WHEN 'Cardiff' THEN 3
ELSE 0
END
");
DropColumn("dbo.People", "Location");
RenameColumn("dbo.People", "LocationTmp", "Location");
Based on @JustAnotherUserYouMayKnow's answer, but easier.
Try firstly execute Sql()
command and then AlterColumn()
:
Sql(@"
UPDATE dbo.People
SET Location =
CASE Location
WHEN 'London' THEN 1
WHEN 'Edinburgh' THEN 2
WHEN 'Cardiff' THEN 3
ELSE 0
END
");
AlterColumn("dbo.People", "Location", c => c.Int(nullable: false));
I know this doesn't apply directly to the question but could be helpful to someone. In my problem, I accidentally made a year field a datetime and I was trying to figure out how to delete all the data and then switch the data type to an int.
When doing an add-migration, EF wanted to just update the column. I had to delete what they wanted to do and add my own code. I basically just dropped the column and added a new column. Here is what worked for me.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TestingPeriodYear",
table: "ControlActivityIssue");
migrationBuilder.AddColumn<int>(
name: "TestingPeriodYear",
table: "ControlActivityIssue",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TestingPeriodYear",
table: "ControlActivityIssue");
migrationBuilder.AddColumn<DateTime>(
name: "TestingPeriodYear",
table: "ControlActivityIssue",
nullable: true);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With