Add-Migration makes a mistake - wrong constraint column name? - entity-framework

I'm trying to create a database migration by
PM> Add-Migration
...
PM> Update-Database
...
but all I get is an error message telling me that an invalid column is referenced. It gets clear when you look at the generated migration which contains the following Up() method:
public override void Up()
{
RenameColumn(table: "dbo.Bestelllistes", name: "Ersteller_Id", newName: "PodioUser_Id");
AddColumn("dbo.Bestelllistes", "ErstelltDatum", c => c.DateTime(nullable: false));
AddColumn("dbo.Bestelllistes", "ZuletztModifiziertDatum", c => c.DateTime(nullable: false));
AddColumn("dbo.Bestelllistes", "ZuletztModifiziertDurch_Id", c => c.Int());
AddForeignKey("dbo.Bestelllistes", "Ersteller_Id", "dbo.PodioUsers", "Id");
AddForeignKey("dbo.Bestelllistes", "ZuletztModifiziertDurch_Id", "dbo.PodioUsers", "Id");
CreateIndex("dbo.Bestelllistes", "Ersteller_Id");
CreateIndex("dbo.Bestelllistes", "ZuletztModifiziertDurch_Id");
}
RenameColumn contradicts AddForeignKey which still uses the old constraint column name. I could manually change this, but I'm afraid that's not the way it's ought to work.
Is this usual behaviour or is it a bug? Looks like one to me.
//edit: Ah, just to explain what I did before that: I added another column property referencing the dbo.PodioUsers table to the "Bestellliste" model. So now "Bestellliste" has got two properties referencing the "PodioUser" model.

This happens to me, too. It seems to happen when I add the [Required] attribute to a field that didn't have it before.
To work around the issue I use update-database -force -script, edit the SQL and run it against the DB. In my case that's MSSQL and I use the Management Studio for that.

Related

Entity Framework Code First post migration step?

Is there some way to add a post migration method to Code First EF migration?
All the stored proc's are in the Visual Studio project. Right now there is an approach to load the stored proc resource from the file and put it into it's own migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
var script = ScriptMgr.LoadStoredProc( "StoredProcThatChanged.sql" );
migrationBuilder.Sql( script );
}
There is a weak link in this process: Each time the script changes (StoredProcThatChanged.sql) a new migration needs to be created to make sure it executes again. The problem is the previous migration is also loading the same file. When generating a new script, the process reads in the one file both times, effectively changing the previous migration. Which is a classic no-no.
This would be resolved if there is a post migration method where ALL stored proc's can be reapplied to the DB. Is such a step possible? If so, now is it done?
I have been digging into the efcore source code and it looks like it is possible, not ideal, but there might be a way...
It looks like efcore has an interface called IMigrator. It contains the method string GenerateScript(...). The implementation of it, class Migrator, has comments all over the place saying that it's implementation of GenerateScript is internal and subject to change. But... It looks to me like I can achieve my end goal:
class MyMigrator : Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator
{
public string GenerateScript(
string? fromMigration = null,
string? toMigration = null,
MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default)
{
var result = base.GenerateScript( fromMigration, toMigration, options);
results += MyPostSteps(...);
return results;
}
}
Will this work and does anyone know how I might go about replacing the default Migrator with the MyMigrator?

How to edit previously applied migration without adding another migration in EF code first

I have an applied migration using "haward" db schema.
public partial class CreateCourseCategoryTable : DbMigration
{
public override void Up()
{
CreateTable(
"haward.CourseCategories",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
Code = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropTable("haward.CourseCategories");
}
}
with this mapping
public class CourseCategoryMapping : EntityTypeConfiguration<CourseCategory>
{
public CourseCategoryMapping()
{
ToTable("CourseCategories", "haward");
}
}
now I want to change the schema from "haward" to "tr"
I dont want to add migration with this one so I thought of just editing directly the source code of the Migration and Mapping.
public partial class CreateCourseCategoryTable : DbMigration
{
public override void Up()
{
CreateTable(
"tr.CourseCategories",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
Code = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropTable("tr.CourseCategories");
}
}
public class CourseCategoryMapping : EntityTypeConfiguration<CourseCategory>
{
public CourseCategoryMapping()
{
ToTable("CourseCategories", "tr");
}
}
then recreate empty database and issued the command update-database
but its telling that I still have pending changes.
so what I did was issued the add-migration command to check which changes are those. and it seems like it still detects my edits(from "haward" to "tr" schema) even without the migrations table.
where do the model changes being saved? and how to edit directly the source code and reapply the migration? I know its not advisable because thats what migration is for. but I dont want to get my history dirty with just those changes specially if I am only at early development stage.
TL;DR: This is very complicated - it's a lot easier just to add a new migration later to correct the problem.
An Entity Framework migration consists of two parts - the code, and a hash of the model. The hash of the model is used to determine whether the model has changed, and hence whether any new migrations are required.
If you change the model, you change the hash. This hash is stored in the MigrationName.designer.cs file. You cannot just edit the model and change the migration.cs code, as the model no longer matches the model hash. You also need to regenerate the hash for the model.
The only way to do this is to roll your database back, and update the hash.
Consider you have 3 migrations applied:
Migration1
Migration2
Migration3
If you want to apply a change from Migration2 onwards...
Roll back to Migration1: Update-Database -TargetMigration Migration1 -Force (NB - This may cause data loss, so work on a development copy of your database)
Make your model code match what you wanted for Migration 2, and update the code for Migration 2 by hand
Regenerate the designer file for Migration2: Add-Migration xxxxxxxxxxx_Migration2 (use the full name of the migration including the date). This will only update the designer.cs file
Apply Migration2: Update-Database -TargetMigration Migration2
Reapply any model changes to your code for Migration3
Regenerate the designer file for Migration3: Add-Migration xxxxxxxxxxx_Migration3
Update the database to latest: Update-Database
I'm not sure if this helps anyone else, but I was able to edit and re-apply a migration by performing the following:
Delete the record of the migration from the _MigrationHistory table. Find the one with a "migrationId" that matches your migration class file name and delete it.
Manually undo all of the previous migration's changes (which would have been everything in the Down() section of your migration class file before you added the new stuff).
Update-Database (or whatever you do to apply your migrations. I have auto-migration turned on so I just run my app).
My migration was pretty simple so I'm not sure if this will work for everyone.
Enjoy!
There's no easy way of doing this but, if you're using a version control system (Git, Subversion, etc.), there's another option. It's a bit more toilsome but was the only one that worked for me.
Considering you have the following migrations:
Migration1
Migration2
Migration3
Assuming you want to change Migration2, you could follow the steps below:
Create a patch with the changes you want to be applied to Migration2
Check out the commit immediately before the migration you want to avoid (in this case, Migration3 - It will look like you have just created Migration2)
Update the DB to Migration1 - Update-Database -TargetMigration Migration1 -Force
Apply the patch you created on step 1
Recreate Migration2 - Add-Migration Migration2 (it will now contain exactly the changes you want)
Copy the migration files created to a different directory
Revert all the uncommitted changes
Check out the HEAD commit again
Delete the files for Migration2 and Migration3
Copy the files from step 6 back to their original directory
Update the database - Update-Database
Recreate Migration3 - Add-Migration Migration3
Update the database again - Update-Database
I assume, this will be the simple one to followed.
Remove the migration from the code base
and Add the same migration after updating the model class.
Then to update-database command.
This will work.

How to recreate migrations from scratch

I recently upgraded from EF6 alpha 1-3 to EF6 beta 1. This meant that I had to recreate all the migrations created using the alpha version.
So I tried to roll back to a migration created using EF5. But I hit the error Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. I figure this is because I had neglected to fix Down migrations when I was fixing Up migrations for exactly the same problem. (Should have read this before)
Anyway, rather than try to fix it all up I am trying to reset all the migrations - as described here. I deleted my migrations table in the database and all migration .cs files, then in package manager Enable-Migrations -EnableAutomaticMigrations -Force and Add-Migration Initial
When I tried to run my application with my existing database initialiser (which has automatic migrations false) it failed because it tried to create tables that were already there. So I changed my initialiser to Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>())
This time I got the Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths problem again during initialisation
So I changed ALL the cascadeDelete: true to cascadeDelete: false in the migration file
But I still get the same error!
Update 1 I removed all but the creation of 1 table in the migration file but I got the same error. There must be some kind of cache somewhere or it's picking up a file I don't know about or it's generating its own migration in the background
Update 2 I figured that when using DropCreateDatabaseAlways that EF must always generate the migrations and also that changing cascadeDelete to false in the migration file is the wrong place to do it. It should be done in the FluentAPI. So I added this line modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); to onmodelcreating. And I also deleted the Initial migration file. Then I ran the application and it correctly generated a database. I thought I'd cracked it but....
I changed initialisation to use my original configuration file:
internal sealed class Configuration : DbMigrationsConfiguration<SID2013Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(etc..
}
Then I ran the application and it reported that the model had changed. So I did Add-Migration Update-Database and a migration file to create the database was created.
The problem now is that when I run the application it tries to run another update (even though AutomaticMigrationsEnabled = false). I get the "There is already an object named 'xxx' in the database" problem again. There is an entry in the migrations table that does not match the name of the configuration file.
If you would like to start work "code first migration" using an existing database, you can do the following:
I run the add-migration "InitialMigrations".
It explores the existing database and make it a migration step.
temporarily delete the contents of the "InitialMigrations" step:
public partial class InitialMigrations : DbMigration {
public override void Up()
{
//...
//...
}
public override void Down()
{
//...
//...
}
}
I run the update-database
This creates the appropriate entries in the table __MigrationHistory.
Restores the contents of the "InitialMigration" Go to work properly on an empty database.
That's it.
update: initializer
As I understand it, the 'DropCreateDatabaseAlways' always delete and create the database. This can only be used for the very first installation. If you have a working installation launchpad, you erase all data. That is why I hazardous.
I based on this article: coding.abel.nu/2012/03/… my own Initializer I'm using. I'll write.
The seed is definitely executed, so this method yields the same result, but it works even if you do not run installation, but run upgrade.
public partial class MyEntities : DbContext
{
static MyEntities()
{
Database.SetInitializer<MyEntities>(new ValidateDatabase<MyEntities>());
}
}
/// <summary>
/// http://coding.abel.nu/2012/03/prevent-ef-migrations-from-creating-or-changing-the-database/
/// </summary>
/// <typeparam name="TContext"></typeparam>
public class ValidateDatabase<TContext> : IDatabaseInitializer<TContext>
where TContext : DbContext
{
public void InitializeDatabase(TContext context)
{
if (!context.Database.Exists())
{
throw new ConfigurationErrorsException(
"Database does not exist");
}
else
{
if (!context.Database.CompatibleWithModel(true))
{
//from:
//http://stackoverflow.com/questions/11611322/ef-4-3-code-first-migrations-seed-per-migration
var cfg = new Migrations.Configuration();
var migrator = new DbMigrator(cfg);
///Last in the db, (first, the reverse order)
var dbLast = migrator.GetDatabaseMigrations().FirstOrDefault();
///last in the app
var appLast = migrator.GetLocalMigrations().LastOrDefault();
///what is missing
var pendings = string.Join(", ", migrator.GetPendingMigrations());
throw new InvalidOperationException(
string.Format("The database ({0}) is not compatible with the entity model ({1}). Pending migrations: {2}",
dbLast, appLast, pendings));
}
}
}
}
update: setup
Installation I'm using something like this:
http://coding.abel.nu/2012/04/update-database-msi-custom-action/
I finally fixed this by dropping the database manually then running the application with the original MigrateDatabaseToLatestVersion initializer. I had not realised that this would create the database if it did not exist and there was no need to use a DropCreateDatabaseAlways initializer then change to MigrateDatabaseToLatestVersion

stuck in EF migration limbo

i have somehow gotten my EF5 project into a state where I can't proceed.
When I do an 'update-database' i get:
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
You can use the Add-Migration command to write the pending model changes to a code-based migration.
ok, fine, so i try to 'add-migration', and i get:
Unable to generate an explicit migration because the following explicit migrations are pending: [ ]. Apply the pending explicit migrations before attempting to generate a new explicit migration.
20 GOTO 10 ??
what am i supposed to do at this point? (beyond switching to NHibernate?)
What worked for me was:
Reverting all my changes (making a backup first) back to a known good state.
Doing add-migration DummyMigration. That produced some spurious changes that I commented
Called update-database to add the migration + metadata to the [__MigrationHistory] table.
Make the changes I needed (from the backup of the code I took earlier).
Do the normal add-migration/update-database.
Not ideal, and would be cool to see if there's a better solution, but that worked for me.
For me the issue was that we had renamed the namespace of the migration 2014123456_Initial.cs.
But VS had not regenerated the namespace in its associated 2014123456_Initial.Designer.cs.
Once the Designer.cs was changed to use the same namespace, it all started working again.
(also posted this as an answer here because both questions are so similar)
I changed the following value in my Configuration.cs class from false to true.
AutomaticMigrationsEnabled = true;
In App_Data folder I renamed my project's database - the file with .mdf ending (so a new one will be created), and in Package Manager Console I entered the following command:
update-database
after which the pending migrations ran smoothly.
Note this worked for me, but I'm not 100% if this is the best practice. In any case this Entity Framework Code First guide for migrations says:
"If you get an error that indicates a table already exists and can't be created, it is probably because you ran the application after you deleted the database and before you executed update-database. In that case, delete the Movies.mdf file again and retry the update-database command. If you still get an error, delete the migrations folder.."
Also about AutomaticMigrationsEnabled = true; this MSDN article, Data Points : A Code First Migrations Mystery: Solved tells "Migrations can run automatically, meaning that model changes will be discovered and migrations corresponding to changes will be created and executed on the database. All of this happens at run time during database initialization. Automatic migrations are handy for simple apps, but you have very little control over them and I typically don’t recommend enabling them. I was happy when Code First switched the default to false."
Update-Database –TargetMigration <second_last_migration>
Add-Migration <full_name_including_timestamp_of_last_migration>
You need to include the timestamp so that migrations knows you want to edit the existing migration rather than scaffolding a new one. This will update the metadata for the last migration to match the current model.
Update-Database
Source https://learn.microsoft.com/en-gb/ef/ef6/modeling/code-first/migrations/teams#resolving-the-merge-conflict
I did mistake in creation database
using System.ComponentModel.DataAnnotations;
using System.Globalization;
namespace ProductsManager.Models
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Production { get; set; }
public string Size { get; set; }
public decimal<--- Price { get; set; }
public string Barcode { get; set; }
}
}
after add-migration Initial I realized and changed code to public int Price { get; set; }
did same add-migration DummyMigration and its created in migration folder
080372472_dummyMigration
namespace IdetityBeta.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DummyMigration : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Products", "Price", c => c.String());
}
public override void Down()
{
AlterColumn("dbo.Products", "Price", c => c.Decimal(nullable: false, precision: 18, scale: 2));
}
}
}
So then update-database and problem was solved
I got around this by
Run "Update-Database -Script -Force"
note the last explicit migration attempted before the error "Unable to update database..."
Run "Update-Database -Script -TargetMigration [lastmigration]" using the last migration noted from the previous attempt
I could then run in the script and add a new migration. This is on EF6 and Nuget 2.8 - it may not have worked when the question was posted.
This can happen when you are trying to merge with migration which has the same base as your migration because of which there is model difference in one of migration schema even though that table exists
To solve this problem what you can do is create merge migration by command
Add-Migration -IgnoreChanges
followed by migration name
This creates an empty migration with the current model as a snapshot. Thus this will solve your model difference problem and your tables and models will be in sync

Unique Constraint with Entity Framework using Code First Migrations

I am doing entity framework (v 5) code first migrations, on an MVC 4 application. I would like to add a unique constraint at the database level.
I know this can be done when creating the table, but I already have a table.
http://msdn.microsoft.com/en-US/data/jj591621
I have tried the following, marked as answer answer: Unique constraint with EFCodeFirst and SqlCe4
My database context differes slightly, I supply the connection name is as follows
public AppDatabaseContext() : base("MyConnectionDBContext")
When I use the Package Management Console to update the database, the overridden seed method is not called:
protected override void Seed(AppDatabaseContext context)
I have also tried the following: http://romiller.com/2010/07/31/ef-ctp4-tips-tricks-running-additional-ddl/
I did not use a nested class, this is because it seemed as if I had to registere the initializer via the app.config. I could not get it working while initializing it in code. The InitializeDatabase is called, but the following condition is never true:
(!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
This is because this happens after the migrations have been run...
I also tried this at one stage: Unique Constraint in Entity Framework Code First, it was the same problem as before, this condition was never returning true.
Ideally, I would like to include some standard SQL in my migration file. Is there a way to do that? If not, where can I see how to achieve this with using code first migrations?
Thanks!
UPDATE:
Is there any reason why I can't use the SQL function?
public override void Up()
{
AddColumn("Posts", "Abstract", c => c.String());
Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
Obviously using the correct SQL...
With code first migrations, I've just used this in the Up() method to add a unique index on a single column:
CreateIndex(table: "Organisations",
column: "Name",
unique: true, // unique index
name: "MyIndex");
...and then in the Down() method:
DropIndex(table: "Organisations",
name: "MyIndex");
Is that what you're after?
Since EF 6.1 you can now do this in your model with either
an attribute
[Index("IX_UniqueName", IsUnique = true)]
public string Name {get;set;}
or fluent
Property(s => s.Name).HasColumnAnnotation(IndexAnnotation.AnnotationName,
new IndexAnnotation(
new IndexAttribute("IX_UniqueName") { IsUnique = true }));
The fluent method isn't perfect as its crazy verbose IMO but at least its possible now.
More deets on Arthur Vickers blog http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
You can just scaffold a new migration using the Add-Migration command from your package manager console. Once the empty migration is created, in the Up method, you can use the CreateIndex method of the DbMigration class to create your new index. It would look like something like this: CreateIndex("dbo.Accounts", "AccessKey", unique: true);