I know we cannot manually update code inside DbMigration method because this can lead us to an inconsistent state for the next DbMigration step.
So, for example, if I change nullable: false to nullable: true in the code of the DBMigration step below (and at the same time do the same modification in the entity class) this will certainly gives problems. The right way is to apply modification only inside the entity class and to add an new DBMigration step in the Package Manager Console (Add-Migration).
public partial class AddDisabledOnUser : DbMigration
{
public override void Up()
{
AddColumn("dbo.Users", "Disabledc => c.Boolean(nullable: false));
}
public override void Down()
{
DropColumn("dbo.Users", "Disabled");
}
}
But what about DBMigration step with SQL commands like below:
public partial class UpdateSomeUsers : DbMigration
{
public override void Up()
{
Sql("UPDATE Users SET Remark = 'Blah blah' WHERE UserName like 'ABC'");
}
public override void Down()
{
}
}
Can I modify the sql code inside the SQL command without affecting the DBMigration steps?
Can I add more sql commands inside an existing DBMigration step without affecting the DBMigration steps?
Do you confirm I can only do thing that did not modify entity models ?
Thanks.
Changing a migration which has already been applied to the DB won't have any effect, therefore I assume you want to change a migration before running it against the DB. If this is the case, then your problem is not really a problem:
If you want to change a property or another aspect of the model, do it, then add the same migration again with the -Force switch, or delete the migration and add it again. Now the migration will match your model just fine
If you want to add extra SQL statements, just do it as you mention adding one or more Sql("...") to the migration file. It doesn't make a difference how many times you change them.
The only problem with manually modifying the migration file is that if you have to recreate the migration, you have to be careful not to forget to add the manual changes again. To avoid that, what I do is to customize the migrations SQL generation so that I can specify my custom SQL code in the mapping files.
Related
I have a database that I have inherited and would like to turn the database itself into migrations that I can use in an ASP.NET Core 3 API. I would like to, if I can, to not have to write the migrations by hand and rather, turn the database into migrations.
I understand that this is very easy with Entity Framework Core, but I wish to use dapper, not EF due to performance reasons as well as personal preference.
I have explored and have come into contact with two libraries for migration management compatible with Postgres: Fluent Migrator and Evolve.
Does anyone have any experience with either of these in the generation of migrations from an existing database?
Thanks,
I am the maintainer of FluentMigrator. I've never heard of Evolve, but other frameworks I've seen include db-up, RoundhousE, and SharpMigrations. Since FluentMigrator has been around for over a decade, it likely has the most complete out-of-the-box feature set of any database migration framework. I previously used RoundhousE but found it caused more problems than it solved.
FluentMigrator does not "generate" migrations for you. You have to write them yourself. By comparison, Entity Framework Core can use its fluent model building syntax and automatically infer up/down migrations, and you can focus primarily on writing data migrations.
That said, here is how Microsoft recommends writing migrations with EFCore, compared with the same logic in FluentMigrator:
EFCore
Source: https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/migrations?view=aspnetcore-3.1#examine-up-and-down-methods
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Course",
columns: table => new
{
CourseID = table.Column<int>(nullable: false),
Credits = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Course", x => x.CourseID);
});
// Additional code not shown
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Enrollment");
// Additional code not shown
}
}
FluentMigrator
[Migration(1, "InitialCreate")]
public class InitialCreate : Migration
{
public void Up()
{
Create.Table("Course")
.WithColumn("CourseID").AsInt32().NotNullable().PrimaryKey()
.WithColumn("Credits").AsInt32().NotNullable()
.WithColumn("Title").AsString().Nullable();
// Additional code not shown
}
protected override void Down(MigrationBuilder migrationBuilder)
{
Delete.Table("Enrollment");
// Additional code not shown
}
}
In the above example, you don't need to type out PK_Course in your primary key definition, because FluentMigrator supports "Don't Repeat Yourself" principle via naming conventions and "knows" that is the correct name for your primary key. You just define your naming conventions once.
Also, because of the fluent syntax, you'll find you have to type a lot less than you do with EFCore migrations. If you find this to not be the case, please provide feedback and we will make it more efficient.
In my project I have the following setup.
EF6 installed
Code first migration running
Views pre-generated using Entity Framework Power Tools
MVC project
This has been working fine up until now where after adding a new code first migration, and then regenerating the views, I can see the following error:
The current model no longer matches the model used to pre-generate the mapping views, as indicated by the fooClass.MappingHashValue property.
Pre-generated mapping views must be either regenerated using the current model or removed if mapping views generated at runtime should be used instead. See http://go.microsoft.com/fwlink/?LinkId=318050 for more information on Entity Framework mapping views.
There was no error generated at all when creating the migration, which is properly formed, and the database does manage to migrate perfectly well. The exception arises when running the MVC site.
For completeness sake, the model change is as following:
public class MyClass
{
[Key]
public int MyClassId
//various other properties
public int Version //newly added property
}
and the migration generated is:
public partial class AddingVersionNumber : DbMigration
{
public override void Up()
{
AddColumn("dbo.MyClass", "Version", c => c.Int(nullable: false));
}
public override void Down()
{
DropColumn("dbo.MyClass", "Version");
}
}
Reverting back to the previous migration, removing the migration changes and then regenerating the views works perfectly fine as well.
I have also tried just adding in a random model change to see if it was anything wrong with the specific table I was changing, but this test change failed with the same error. Seems that no matter how I change the model, the same error re-appears.
Another test I followed was to deleted the pre-generated views file from the project, but still the same exception is thrown.
Anyone else ever experienced anything similar?
New to EF. Following along with DBContext by Lerman/Miller.
When I start a new project, adding EF6 (Database First), the DBContext seems to be added as a default (ie I don't have to add the DBContext separately with T4). Also, for Lazy Loading, the "virtual" needed in the class definitions also seems to be there by default (I don't have to add it like in the book). Is this what is expected?
When you use Database First approach and use EF x DbContext Generator it creates the DbContext for you automatically and set the navigation properties, virtual. If you want to disable lazy loading you can simply use following code
public class MyContext : DbContext
{
public MyContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Most probably the books you are reading are for code first development. If you use database first(especially with the designer) you don't need to make changes.
In my app i have two DataContexts, ApplicationDbContext created by default ,which inherits from IdentityDbContext and DomainContext for my model. Now i am getting some issues when updating the database using Migrations, when i execute
Add-Migration to DomainContext in the migration file created is included the code to Drop all the tables related to ApplicationDbContext. I have googled and i have not found any satisfactory answer yet, the only rasonable solution suggest to mingle both DBContexts but that sound weird to me because ApplicationDbContext inherit from IdentityDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DBContextConnString" )
{
}
}
public class DomainContext:DbContext {
public DomainContext() : base( "DBContextConnString" ) { }
public DbSet<State> States { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder ) {
modelBuilder.HasDefaultSchema( "Workflow" );
base.OnModelCreating( modelBuilder );
}
}
One of the The new fetures exposed by EF6 state "Multiple Contexts per Database removes the previous limitation of one Code First model per database when using Migrations or when Code First automatically created the database for you." (Read it here). But in practice is a pain to implement this or maybe i am missing something very very obvious.
My question in short, what is the way to go to keep this two different DBContexts living in armony?
After some experimentation i conclude that the more pragmatic way is to mingle both DbContext into one, so i decide to move my domain entities to IdentityDbContext.
I am not worried about rollbacks (ie: Down() method), and would like to work always on the latest code modification, so I'm not using Code Migrations.
I changed this:
int SomeProperty {get; set;}
To this:
int? SomeProperty {get; set;}
And then I modified the database schema (altering the column SomeProperty to allow NULLS).
Although this last part, I'm still getting the error:
The model backing the <Database> context has changed ...
What other change do I need to do so that the schema matches the model if that (int to int?) is the only change I've done?
Tell EF you want to do an automatic migration
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, MyMigrationConfiguration>());
Context.Database.Initialize(true);
public abstract class MyMigrationConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext{
protected MyMigrationConfiguration() {
AutomaticMigrationsEnabled = true; //<<<<<<<<<<<<<<<<<<<
AutomaticMigrationDataLossAllowed = true;
}
EDIT: Added comment based on feedback
Changing the Database to Match the schema means also the Migration history table will also need to be altered if it doesnt match. Setting the Model Data binary by hand to match your model, is not straight forward.
If your tables match your code, then the model recorded in __MigrationHistory as the last model, must be different. Your problem starts right there.
You can delete all entries from MigrationHistory table and try again. [disclaimer.. take a copy of entries... etc] But fighting EF isnt the ideal approach.
It also means you are in quasi DB first mode. So i would suggest you DON'T continue on this path. Unless you plan on going to DB First mode.
What you are saying is you dont trust EF to do the right thing under code first scenarios.
Backup the DB. Then try it. If EF does the "WRONG" thing try and deal with that would be my suggestion. If you have a nasty migration scenario EF cant handle, start a new question to get workarounds.
The real answer to this is a bug in EF.
In Global.asax.cs this line is needed:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//...
// Weird fix before using any DbContext instance <--- THIS
Database.SetInitializer<MyDbContext>(null);
}