ChatGPT解决这个技术问题 Extra ChatGPT

Change data in migration Up method - Entity Framework

I have added a new property to my existing model. It's a bool property with a default value of true. There are existing data in this table and I would like to set one specific row's new property to false right after creating the new field, in the Up method.

public override void Up()
{
    AddColumn("dbo.RequestValidationErrors", "IsBreaking",
         c => c.Boolean(nullable: false));
    using (Context ctx = new Context())
    {
        var validation = 
            ctx.RequestValidationErrorSet
            .FirstOrDefault
                (x => x.WordCode == "RequestValidationError.MoreThanOneItemFound");
            if (validation != null)
            {
                validation.IsBreaking = false;
                ctx.SaveChanges();
            }
        }
    }
}

This way EF throws an error during saying

System.InvalidOperationException: The model backing the 'DbContext' context has changed since the database was created. Consider using Code First Migrations to update the database

Is it possible to change the database here or should I do it elsewhere?


m
mehrandvd

In the middle of a migration, it's better to use Sql() method to update database data.

Sql("UPDATE dbo.RequestValidationErrors SET IsBreaking = 0 WHERE WordCode = 'RequestValidationError.MoreThanOneItemFound'");

Also you should define the default value for the new column. So the solution should be something like this:

public override void Up()
{
    AddColumn("dbo.RequestValidationErrors", "IsBreaking", c => c.Boolean(nullable: false, default: true));
    Sql("UPDATE dbo.RequestValidationErrors SET IsBreaking = 0 WHERE WordCode = \"RequestValidationError.MoreThanOneItemFound\"");
}

Using a DbContext in the middle of its migration is very ambiguous. What do you expect from the context? It has the after migration state in its models, but the database has the before migration state in the tables. So the model and database do not match. If you still insist on using DbContext in your code, disabling the model checking might be the solution. You can disable model checking using:

Database.SetInitializer<Log4ProContext>(null);

UPDATE:

As of EF Core 2.1 you can use UpdateData instead of Sql method for simpler cases just as @ntfrex mentioned in an answer:

migrationBuilder.UpdateData(
    table: "RequestValidationErrors", 
    keyColumn: "WordCode", 
    keyValue: "RequestValidationError.MoreThanOneItemFound", 
    column: "IsBreaking", 
    value: false);

My advice is NOT TO USE nameof operator for table and column names nowhere in migrations at all. As renaming these classes later would cause the old migrations to fail in production as the table names in the database are still with the old name.


Thank you for the solution and for the missing defaultValue, too.
It feels very good when you find what you're looking for, answered by your friend :)
What do you mean " it's better to use Sql()"? Better than using Context? Better than using UpdateData method?
@TomasKubes You're right. The UpdateData wasn't available at the time I was writing this answer. I've just updated the answer :)
b
bbodenmiller

Instead of using the Sql method you could also use the UpdateData method.

migrationBuilder.UpdateData(
    table: "RequestValidationErrors", 
    keyColumn: "WordCode", 
    keyValue: "RequestValidationError.MoreThanOneItemFound", 
    column: "IsBreaking", 
    value: false);

(I don't know if only ef core supports this method)


EF Core supports this as of v2.1
This is working as expected in ASP.NET Core 3.1. A minor improvement would be to use nameof whenever possible (e.g. nameof(RequestValidationErrors), nameof(RequestValidationErrors.WordCode), nameof(RequestValidationErrors,IsBreaking).
I am new to EF Core but I think using nameof is not a good idea. Each migration is just a description on how to update (or downgrade) the schema based on the current state of the db. This can be applied recursively in order to upgrade over multiple schema versions. Using nameof within migrations will couple your migration logic to the state of the code and thus may fail because a change of the name of a property now influences all the past migration logic that uses nameof(property).
u
user868386

If you want to use the framework for changes like this, you should separate Database changes from Data changes.

Create a migration for just the Database changes, and execute.

Then create a new migration (the Up() and Down() methods will be empty). You can now instantiate your DatabaseContext and there will be no error. This way you can use the Framework for these changes, and properly implement a Down() method.


This "solution" is very dangerous as it looks like it is working at first, but it is not. As soon as you create another migration afterwards that changes the database model, it doesn't work anymore, because then you cant create a database context, as the model doesn't the database anymore
@NoConnection You should never use your DbContext Instance in Migrations because of this reason. Use migrationBuilder.Sql or .UpdateData method to migrate your data as there should not be a problem.
C
Chris Moschini

Writing DataMigrations for EF6 can be a chore. We put together a library I'm just open sourcing here for others to use, that adds in this long-promised, missing feature to EF6. Just write classes using regular EF queries etc.

https://github.com/b9chris/Brass9.Data