I migrated my .NET Core 1.1 app over to .NET Core 2 and have issues with the ef db migration.
I noticed that the new migration I added when my app is running .NET Core 2 seems to not have knowledge of my .NET Core 1.1 migrations. Is this because the new file has a product version of
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452") Where as my old ones were:
.HasAnnotation("ProductVersion", "1.1.2")?
@p2atran This is not due to the product version in the file. Can you post a runnable project/solution that demonstrates the issue you are seeing such that we can investigate it?
When you say running project/solution do you mean one that you are able to compile and run on your end?
In the generated migration file, it tries to drop UserNameIndex, which has already been dropped in a previous migration file. It also tries to rename ApplicationUserToken to UserTokens which also has been done in a previous migration. Come to think of it, I think everything in that new migration file has been done before except the two last AddForeignKeys which I just added to the context file's OnModeCreating
ApplicationUser:
public class ApplicationUser : IdentityUser
{
[InverseProperty("User")]
public virtual Player Player { get; set; }
public List<ApplicationUserClubs> Clubs { get; set; }
public int? CustomerProfileId { get; set; }
// TODO: change these to int after the migration works
public virtual ICollection<IdentityUserRole<string>> Roles { get; } = new List<IdentityUserRole<string>>();
// public virtual ICollection<IdentityUserClaim<string>> Claims { get; } = new List<IdentityUserClaim<string>>();
// public virtual ICollection<IdentityUserLogin<string>> Logins { get; } = new List<IdentityUserLogin<string>>();
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<IdentityUserRole<string>> Users { get; } = new List<IdentityUserRole<string>>();
// public virtual ICollection<IdentityUserRole<string>> Users { get; set; }
public ApplicationRole(string roleName) : base(roleName) {}
public ApplicationRole() : base() {}
}
md5-f79c202c8852498f011be9cd68410921
public class MyContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{ }
public virtual DbSet<ApplicationUser> AspNetUsers { get; set; }
public virtual DbSet<ApplicationUserClubs> ApplicationUserClubs { get; set; }
public virtual DbSet<ApplicationUserToken> ApplicationUserToken { get; set; }
public virtual DbSet<InboxNotification> InboxNotifications { get; set; }
public virtual DbSet<PushToken> PushTokens { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IdentityUser>().ToTable("AspNetUsers")
.HasKey(x => x.Id);
builder.Entity<IdentityRole>().ToTable("AspNetRoles")
.HasKey(x => x.Id);
// builder.Entity<ApplicationUser>()
// .HasMany(e => e.Claims)
// .WithOne()
// .HasForeignKey(e => e.UserId)
// .IsRequired()
// .OnDelete(DeleteBehavior.Cascade);
// builder.Entity<ApplicationUser>()
// .HasMany(e => e.Logins)
// .WithOne()
// .HasForeignKey(e => e.UserId)
// .IsRequired()
// .OnDelete(DeleteBehavior.Cascade);
builder.Entity<ApplicationUser>()
.HasMany(u => u.Roles)
.WithOne()
.HasForeignKey(r => r.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<ApplicationRole>()
.HasMany(r => r.Users)
.WithOne()
.HasForeignKey(r => r.RoleId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
// builder.Entity<ApplicationUser>()
// .HasMany(u => u.Claims)
// .WithOne()
// .HasForeignKey(c => c.UserId)
// .IsRequired()
// .OnDelete(DeleteBehavior.Cascade);
// builder.Entity<ApplicationRole>()
// .HasMany(r => r.Claims)
// .WithOne()
// .HasForeignKey(c => c.RoleId)
// .IsRequired()
// .OnDelete(DeleteBehavior.Cascade);
builder.Entity<IdentityUserRole<string>>().ToTable("AspNetUserRoles")
.HasKey(x => new { x.UserId, x.RoleId });
builder.Entity<IdentityUserClaim<string>>().ToTable("AspNetUserClaims")
.HasKey(x => x.Id);
builder.Entity<IdentityUserLogin<string>>().ToTable("AspNetUserLogins")
.HasKey(x => new { x.UserId, x.ProviderKey });
builder.Entity<IdentityUserToken<string>>().ToTable("AspNetUserTokens")
.HasKey(x => new { x.UserId, x.LoginProvider, x.Name });
builder.Entity<IdentityRoleClaim<string>>().ToTable("AspNetRoleClaims")
.HasKey(x => x.Id);
builder.Entity<ApplicationUserToken>().ToTable("UserTokens")
.HasKey(x => x.Id);
}
}
md5-3ec8bc35d404d20928bad003c1e28c04
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ApplicationUserToken_AspNetUsers_UserId",
table: "ApplicationUserToken");
migrationBuilder.DropIndex(
name: "Team_Name",
table: "Teams");
migrationBuilder.DropIndex(
name: "IX_TeamMatches_Name_TeamId",
table: "TeamMatches");
migrationBuilder.DropIndex(
name: "IX_TeamMatches_Slug_TeamId",
table: "TeamMatches");
migrationBuilder.DropIndex(
name: "IX_Practices_Name_TeamId",
table: "Practices");
migrationBuilder.DropIndex(
name: "IX_Practices_Slug_TeamId",
table: "Practices");
migrationBuilder.DropIndex(
name: "IX_Players_UserId",
table: "Players");
migrationBuilder.DropIndex(
name: "IX_Clubs_Name",
table: "Clubs");
migrationBuilder.DropIndex(
name: "UserNameIndex",
table: "AspNetUsers");
migrationBuilder.DropPrimaryKey(
name: "PK_ApplicationUserToken",
table: "ApplicationUserToken");
migrationBuilder.DropIndex(
name: "RoleNameIndex",
table: "AspNetRoles");
migrationBuilder.RenameTable(
name: "ApplicationUserToken",
newName: "UserTokens");
migrationBuilder.RenameIndex(
name: "IX_ApplicationUserToken_UserId",
table: "UserTokens",
newName: "IX_UserTokens_UserId");
migrationBuilder.AddColumn<string>(
name: "Discriminator",
table: "AspNetRoles",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
migrationBuilder.AddPrimaryKey(
name: "PK_UserTokens",
table: "UserTokens",
column: "Id");
migrationBuilder.CreateIndex(
name: "Team_Name",
table: "Teams",
columns: new[] { "Name", "LeagueId" },
unique: true,
filter: "[Name] IS NOT NULL AND [LeagueId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_TeamMatches_Name_TeamId",
table: "TeamMatches",
columns: new[] { "Name", "TeamId" },
unique: true,
filter: "[Name] IS NOT NULL AND [TeamId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_TeamMatches_Slug_TeamId",
table: "TeamMatches",
columns: new[] { "Slug", "TeamId" },
unique: true,
filter: "[Slug] IS NOT NULL AND [TeamId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Practices_Name_TeamId",
table: "Practices",
columns: new[] { "Name", "TeamId" },
unique: true,
filter: "[Name] IS NOT NULL AND [TeamId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Practices_Slug_TeamId",
table: "Practices",
columns: new[] { "Slug", "TeamId" },
unique: true,
filter: "[Slug] IS NOT NULL AND [TeamId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Players_UserId",
table: "Players",
column: "UserId",
unique: true,
filter: "[UserId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Clubs_Name",
table: "Clubs",
column: "Name",
unique: true,
filter: "[Name] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.AddForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
table: "AspNetUserTokens",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_UserTokens_AspNetUsers_UserId",
table: "UserTokens",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
@p2atran "When you say running project/solution do you mean one that you are able to compile and run on your end?" Yes. If there is a bug here, then we want to be able to reproduce it so that we can investigate it and find the root cause. That's very hard to do with just the results of running the migration process (as posted above), since it doesn't help figure out why the results are the way they are.
@ajcvickers Are there any other approaches we can post to simulate? I don't think I can just zip and attach the project here as we have some privacy agreements with our client.
Also are there specific steps to take in doing a new migration in NET Core 2.0, when we have existing migration files from NET Core 1.1?
@p2atran There are some migration instructions for ASP.NET here: https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/?view=aspnetcore-2.1
Usually creating the repro involves setting up a new project/solution which reproduces the structure of the project and its references but trims out all the code not needed to demonstrate the issue and uses dummy names/code as needed so as not to disclose private information.
@ajcvickers Thanks for the link. I have followed/completed that. I meant in terms of doing a ef db migration. After I did the .NET Core migration, I did the following:
1) dotnet ef migrations Add NewMigration
-- results in existing migration stuff too
2 dotnet ef database update
Are these the correct commands to use after a framework migration from Core 1.1 to Core 2?
I'll start working on a reproducible case, but It may be tough since we had at 40 db migrations in Core 1.1 that I will need to emulate.
@p2atran Yes, those steps look correct. I don't expect you will need to emulate all the migrations. Probably only the state at the end of the final 1.1 migration is important.
@ajcvickers I found this issue which is the exact same thing I am facing.
https://github.com/aspnet/EntityFrameworkCore/issues/10367
I'm going to try your tip and test on 2.0.3 to see if it still occurs.
@ajcvickers I updated to the latest packages and it still occurs.
Also one thing to note is that I am getting this warning regardless of version:
The annotation 'TableName' was specified twice with potentially different values. Specifying the same annotation multiple times for different providers is no longer supported. Review the generated Migration to ensure it is correct and, if necessary, edit the Migration to fix any issues.
@ajcvickers Also is there a reason why Entity tries to drop an index before creating it? Is this a new pattern?
@p2atran With regard to the table name, are you building a model for multiple database providers? Also, what database provider and version are you using?
With regard to dropping the Index, EF is dropping it because a previous migration generated it (or at least, the inference from the model snapshot it that it was previously generated) and now needs to change some configuration of the index which requires that it be dropped and re-created.
@ajcvickers Good morning Arthur, I'm building for MS SQL Server, I believe I have Microsoft.EntityFrameworkCore.SqlServer version 2.0.2
If the index was created in MigrationFile1 and then later dropped in MigrationFile2, My current migration shouldn't need to drop/create it again right?
Are we supposed to delete our old .NET Core 1.1 migration files/snapshot before running in core 2?
@p2atran We could continue to investigate this, but it might be more productive to just generate a new baseline migration for 2.0. That is, generate a migration for 2.0, remove all the code in the Up/Down methods, but keep the migration and the snapshot file update. As long as the EF Core model maps to the current database without issue, then this should provide a sound starting point to continue work with 2.0.
Note that this should not be needed, but:
@ajcvickers If I do the baseline route, Do I have to remove anything from the Designer file?
@p2atran You should not need to change the designer file.
@bricelam and I just talked again about this. It could be that we added FK constraints that weren't there in 1.1, and this is why you are seeing issues. That is, the FKs were defined in the EF model even in 1.1, but sometimes these were not then created as constraints in the database. It's possible that then going forward EF will still think that there are FK constraints in the database when there are not, and might try to drop them EF thinks this is needed. In such cases you may just want to remove the 'drops' from the generated migrations and let EF create the FK constraints. (Note that EF doesn't need the FK constraints; they just provide extra fidelity in the database schema.)
@ajcvickers Ahh that makes sense. I was also thinking maybe since Identity had breaking changes that there were new things EF could be seeing too, but I'm not sure. I tried doing that clean migration as a base line. It does catches new changes after so that's good. I was worried migrations would be broken for my app moving forward.
However, I did notice I have problems with the authorization/claims. Is there a sure way I can check if my EF models are mapped correctly and that this is a different issue?
@p2atran Probably the best way would be to scaffold a model from the database and compare the model created from that with the one that you have. (Probably easiest way to diff would be to add an empty migration after doing the scaffolding the model and then use the model snapshot file for the diff.)
@ajcvickers The EF and database stuff seems to be working. I only have one issue left in the migration and its the static files loading extremely slow. I can't imagine that being related to ef.
Thanks for all the help! I really appreciate it. I made a post in the aspnet/Mvc group. Hopefully they can help with the performance issue.
@p2atran No problem. Glad I could help.
I have a same problem when migrating my project from core 1.1 to core 2.1 and I found a solution that might help other developers who encountered the same problem.
The reason for the new EF to eliminate tables that do not exist and to create tables that already exist is in the old ForSqlServerToTable method. This method (deprecated in this version, overridden by the ToTable method) has generated this code in ModelSnapshot.cs:
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
b.HasAnnotation("SqlServer:TableName", "Roles");
});
The new ToTable method doesn`t create "b.HasAnnotation("SqlServer:TableName", "Roles");" line and not recognizes this. In other words, the new implementation consider "AspNetRoles" as current table name and then attempts to change the AspNetRoles to Roles, for example.
For workaround this behavior, I change my ModelSnapshot class manualy, removing "b.HasAnnotation("SqlServer:TableName", "Roles");" line, and change "b.ToTable("AspNetRoles");" to "b.ToTable("Roles");".
Now my code looks like this:
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("Roles");
});
That way, I was able to perform new migrations and the generated code did not want to drop and create tables.