Why does my migrations create every table for the data model? - c#

I've recently added a migration to my data model where I added a bool property to the Member model:
public class Member
{
public bool IsLegacy { get; set; }
}
Having added the migration via dotnet ef migrations add <name>, the migration generated code to create every table.
As a result, when I push the migration to the database dotnet ef database update, I get an error saying
table '...' already exists
The existing table could change from time to time.
I would expect the migration to simply add the column to the table so that the only could would probably be something like:
public partial class AddedLegacyFieldToMember : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsLegacy",
table: "Member",
type: "bit" // assuming sql type is what it needs.
);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn("IsLegacy", "Members");
}
}
So what I can't figure out is why the migration is trying to build every table all new as though no other migration exists.
The migration history table in the database does have the previous migration listed there though.
Have I done something wrong?
Note
I've tried using .EnsureCreated() to sidestep the issue, but clearly that's misguided as it didn't make any difference.
Also, building the migration logic into the Startup doesn't address the problem either.

Related

Intercepting SqlServerMigrationsSqlGenerator to prevent migration of certain tables?

I am trying to use EF Core 3.0 migrations with a hybrid of an existing tables and new tables built with code first. To prevent the scaffolding of the existing tables I would like to decorate the model class with an attribute (fluently or annotations) so that the migration code generation for those tables is skipped but the model is still built into the DbContext class.
The approach I'm taking is to add the following lines to OnConfiguring
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
And then creating a SkipMigrator with the following code
public class SkipMigrator:SqlServerMigrationsSqlGenerator
{
public SkipMigrator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations){}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation.FindAnnotation("SkipMigrations")!=null)
{
Console.WriteLine("Skipping table:");
}
else
{
base.Generate(operation,model,builder);
}
}
}
I assumed that the Generate method was what triggered the creation of the migration code file but it never gets called. Id there a different place I should be intercepting the code generation?
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Your means of trying to create your own IMigrationsSqlGenerator was correct, and I've used that approach before to alter the SQL that is generated as part of a migration.
services.AddDbContext<MyDbContext>(opt =>
{
opt.UseSqlServer();
opt.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
});
However, as of EF Core 5.0 it's now much easier to exclude specific tables from migrations using the ExcludeFromMigrations() method on a TableBuilder:
public class ReportingContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable(nameof(Users), t => t.ExcludeFromMigrations());
}
}
More info: https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-efcore-5-0-rc1/#exclude-tables-from-migrations
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Yes, but it requires a different approach.
Instead of using your current DbContext class for creating migrations, create a SecondDbContext class only for the very purpose of creating migrations. This new SecondDbContext class will hold the DbSets<T> that you want EF to do its migrations on.
Then simply specify the second context when calling add-migration UpdateTable -c SecondDbContext and then update-database -c SecondDbContext.

Removing EF Core Migration removes columns the Migration had nothing to do with

I have a class that includes 2 properties: Value and Description. These 2 properties were added to the class a couple of Migrations back and there was never a problem.
Today, while working on a branch I noticed that when I added a new Migration it was scaffolded to once again add the columns, so I started doing some testing.
Starting from the current baseline, with no entity changes, if I add a Migration called "Test", I get this:
using Microsoft.EntityFrameworkCore.Migrations;
namespace AgWare.Data.Migrations
{
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
Which is correct, I haven't made any changes to my Models. Git only shows the new Migration and its Designer.cs files as added, with no changes to the Snapshot. Now, if I run Remove Migration, the Test Migration is removed but suddenly Git says that my Snapshot has changed, with the following lines having been deleted from the modelBulder entry for the above-mentioned class:
b.Property<decimal?>("Value")
.HasColumnType("Money");
b.Property<string>("Description")
.HasMaxLength(75);
So removing a Migration which had nothing to do with these properties somehow results in updating the Snapshot to reflect that they have been removed for some reason. This means when I add another Migration it will generate code to create these columns again, which won't work since they already exist. What could be the cause of this? I want to figure this out now so this doesn't become a reoccurrence for all future Migrations.

Custom Entity Framework Core Migration Script?

I need to make a "full-text indexed" but I am using ef core 2.1 code first migration, but Full Indexes do not have first class support in core.
How can I write my own migration that would be applied while the other generated migrations are being applied?
You have to create empty migration and then in Up method you can write your SQL query.
Create Empty Migration by running Add-Migration command in Package
Manager Console.
Add the SQL function in Up method like the following way.
public partial class your_migration_name : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"<YourQuery>");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"Drop <YourQuery>");
}
}
Run Update-Database in Package Manager Console.

EF6 Migration fails

I am trying to create a migration for a MYSQL database with EF 6 and running into problem where MYSQL exception is thrown informing that the table is not found. From what i see the problem comes from the fact that migrations have a dbo. prefix on the statements. Manually changing the migration file and removing the dbo. part works.
The strange thing here is that the error only occurs when there is a second migration exists and after examining the first migration file i noticed that its also have a dbo. prefix but still database creation happens correctly and the exception is only thrown once database is being migrated to second version.
When database created there is no dbo. prefixes on the tables or any other database part even when dbo. prefixes exists in initial create migration so it makes me wonder that there must be some part of the code that takes care of it?
Migration code:
public partial class Update1 : DbMigration
{
public override void Up()
{
AddColumn("dbo.ProductOrderLine", "SomeData_Id", c => c.Int());
CreateIndex("dbo.ProductOrderLine", "SomeData_Id");
AddForeignKey("dbo.ProductOrderLine", "SomeData_Id", "dbo.User", "UserId");
}
public override void Down()
{
DropForeignKey("dbo.ProductOrderLine", "SomeData_Id", "dbo.User");
DropIndex("dbo.ProductOrderLine", new[] { "SomeData_Id" });
DropColumn("dbo.ProductOrderLine", "SomeData_Id");
}
}
Exception when downgrading, Funny enough upgrading worked.
Reverting migrations: [201508280829293_Update1].
Reverting explicit migration: 201508280829293_Update1.
alter table `dbo.ProductOrderLine` drop foreign key `FK_dbo.ProductOrderLine_dbo.User_SomeData_Id`
MySql.Data.MySqlClient.MySqlException (0x80004005): Table '_gizmo_db.dbo.productorderline' doesn't exist
Here i am able to run database update but after that downgrade fails.
Ok it seems that the problem was that i didn't had MySqlMigrationCodeGenerator set as migration code generator and that was causing the generation of dbo prefixes which lead to drop/create problems.

Changing the default SQL Server schema used from [dbo] to something else

I am using EF5 with the DefaultMembershipProvider and want to control the schema used in SQL Server for the tables created by the DefaultMembershipProvider.
EF5 says this is doable as:
modelBuidler.Entity<MyEntity>().ToTable("MyTable", "MySchema");
but as these are not "my" entities I cannot do it this way.
Questions:
1) So how do I do this in EF5?
2) Is this dealt with in EF6 when using DbModelBuilder.HasDefaultSchema?
Yes indeed I would upgrade to EF6 and then make your context look like this, e.g.:
public partial class BlogContext : DbContext
{
public BlogContext()
: base("BlogDb")
{
Database.SetInitializer<BlogContext>(null);
}
public DbSet<BlogPost> BlogPosts { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>();
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.HasDefaultSchema("");
}
}
You see setting the default schema at the end to "", which also works, in my case, Oracle 12c takes the login user name as schema and everything works fine. Of course you could also load the schema name from configuration and insert it there.
Note: Automatic migrations will NOT work anymore, as this seems to confuse the system quite much. See here for a possible solution so that at least explicit migrations will work somehow: Entity Framework using IdentityDbContext with Code First Automatic Migrations table location and schema?

Categories