Model change detection ASP.NET code first migrations - asp.net

I have added
public IEnumerable<Comment> Comments { get; set; }
to a Model in an ASP.net MVC project. I ran the following to generate a migration in the package console
PM> Add-Migration AddCommentsToDevice
and the resulting migration did not pick up the change to the model
public partial class AddCommentsToDevice : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
Any tips on the syntax of the migrations or what causes detections?

You've added to little. You need to configure the relationship properly - the best with fluent api. Use this for navigation property:
public virtual ICollection<Comment> Comments { get; set; }
Important - always use ICollection, not IEnumerable for Navigation properties and make them virtual - thanks to this ef will be able to track changes.
Then in your DbContext you add following code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Model>().HasKey(x => x.Id);
modelBuilder.Entity<Model>().HasMany(x => x.Comments);
base.OnModelCreating(modelBuilder);
}
I assumed that class related to Comments is named Model.

Related

No changes in model, should EF migration create empty empty Up() Down() methods? Or no migration at all?

I'm using EntityFrameworkCore Code First to create my SQL migration scripts.
After every change in my model, I run Add-Migration changeInModel and a corresponding XXXXXXXX_changeInModel.cs file is being generated in my Migrations folder.
If I don't make any change in my models AND I run Add-Migration noChangeInModel , I was assuming (out of nowhere) no generation of XXXXXX_noChangeInModel.cs
However, EF Core is creating a XXXXXX_noChangeInModel.cs with an empty Up and Down methods.
Is that supposed to be the expected behavior? Or should EF Core just skip the generation of an empty .cs file? I can't find any reference to this in the documentation.
I have taken my main project and stripped out all the code up to the bare minimum to find out whether is the behavior or some sort of bug in my configuration.
Below the minimum core to reproduce generating empty Up/Down methods with no change in models, with the following Nuget packages:
Microsoft.EntityFrameworkCore 2.2.6
Microsoft.EntityFrameworkCore.SqlServer 2.2.6
Microsoft.EntityFrameworkCore.Tools 2.2.6
Microsoft.NETCore.App 2.2.0
namespace TestingEFCore
{
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
var connectionString = "Server=localhost;Database=TEST2;Integrated Security=SSPI;";
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connectionString);
return new BloggingContext(optionsBuilder.Options);
}
}
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
}
I would expect no XXXXXX_noChangeInModel.cs being generated, but a I get migrations with empty Up/Down methods. I can't find the documentation to describe this use case.
I think it is expected behavior. Consider the case when you have no changes in model but you need to add some data in your db like this
public partial class your_migration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"your sql");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"sql to clean up data");
}
}
Without an empty migration for no changes in model generated it would be impossible to obtain such migration.

Configure Owned property in EF Core

EDIT: The original problem statement included only one reference to the Owned entity. But it turns out that the problem I encountered happens with multiple references to the Owned entity AND some funky fluent configuration code. I've rewritten this problem statement to explain the details of the problem I ran into.
I started with one EF Core entity, which references an Owned entity:
public class InvoiceItem
{
public SubscriptionPlanDetails SubscriptionPlan { get; set; }
}
[Owned]
public class SubscriptionPlanDetails
{
public string PlanName { get; set; } // Plan name
public decimal Price { get; set; } // Price (USD)
}
When I enter the command "add-migration" in the VS Package Manager Console, scaffolder complains:
No type was specified for the decimal column 'Price' on entity type 'SubscriptionPlanDetails'.
So I added some fluent API configuration:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<SubscriptionPlanDetails>()
.Property(p => p.Price)
.HasColumnType("money");
}
Problem solved... or so I thought. Then I added another entity with a reference to the same Owned entity:
public class Account
{
public int Id { get; set; }
public SubscriptionPlanDetails SubscriptionPlan { get; set; }
// Navigation properties
public virtual ICollection<InvoiceItem> InvoiceItems { get; set; }
}
So, each account has one subscription plan (Account.SubscriptionPlan), and each account has multiple InvoiceItems, each of which contains all of the details of the subscription plan that was in effect at the time the invoice item was created (Account.InvoiceItems.SubscriptionPlan).
Now, when I try to add a migration, it complains
No type was specified for the decimal column 'Price' on entity type 'InvoiceItem.SubscriptionPlan#SubscriptionPlanDetails'.
So I added fluent API code to configure the Owned property. But I neglected to remove the previous code. So, my configuration code looked like this:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// This should have been removed!
builder.Entity<SubscriptionPlanDetails>()
.Property(p => p.Price)
.HasColumnType("money");
builder.Entity<Account>().OwnsOne(m => m.SubscriptionPlan)
.Property(p => p.Price)
.HasColumnType("money");
builder.Entity<InvoiceItem>().OwnsOne(m => m.SubscriptionPlan)
.Property(p => p.Price)
.HasColumnType("money");
}
Attempting to add a migration gets me a NullReferenceException.
If I comment out the offending call to builder.Entity<SubscriptionPlanDetails>() then it works as expected!
I think the best way to override this decimal warning is to annotate the price with the following:
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
You can also use the type "money" instead of "decimal(18,2)"

ef core migration insert data

I'd like to insert data into table in migration. Is it possible? Migration needs parameterless constructor available and I'd like to use db context defined in Startup.cs file (best I'd like to get it throught dependency injection). How do that?
In the EF Core 2.1, migrations can automatically compute what insert, update or delete
operations must be applied when upgrading the database to a new version of the model.
As an example, we have a User and UserComment entities like these:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public List<UserComment> UserComments { get; set; }
}
public class UserComment
{
[Key]
public int CommentId { get; set; }
public string CommentTitle { get; set; }
[ForeignKey("User")]
public int FKUserId { get; set; }
public User User { get; set; }
}
In the DBContext, override the OnModelCreating function and seed data to each entity:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasData(new User[] {
new User{UserId=1,Name="iman"},
new User{UserId=2,Name="Alex"},
});
}
To seed datas that have a relationship, the foreign key value must be specified:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserComment>().HasData(new UserComment[] {
new UserComment{FKUserId=1,CommentId=1,CommentTitle="Comment1"},
});
}
Be careful: you must use migrations to apply changes
Migration is a process of "upgrading" your DB to a new "version". During this, your existing DB tables ("old version") does not required to match your classes (entities) ("new version"), so you can't safely use them.
During migration you should operate only with tables and records using raw SQL commands. You may use migrationBuilder.Sql("UPDATE ..."); for such updates, put them manually into migration Up() code.
If you need perform data modifications using entity classes - you should use "Seed Data" solution (from #itikhomi comment), but remember that it will be run every time your app starts, so you should do some version-check inside it.

Renamed default tables but AspNetusers Table Still Getting Created

I am working with ASP.NET identity. I renamed the default tables using the following code.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("Users");
modelBuilder.Entity<IdentityRole>().ToTable("Roles");
modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogins");
}
}
}
But when I run my app, AspnetUsers is still created but with a single column named 'Id'. This table is being populated along with the new "Users" table. Why? And how do I stop this behavior.
I see the same question asked here, but without a good response: Identity 2.0 Code First Table Renaming
I hope I am not violating StackOverflow's guidelines in reasking this question/// if so apologies in advance!
Stumbled across this post when looking for a quick solution - now I've dug a bit through the code and found the answer; maybe it helps someone.
I suppose you have overwritten the default IdentityUser class, right?
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
...
In that case, you'll have to exclude the default IdentityUser from the ModelBuilder:
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Entity<IdentityUser>().ToTable("Users", "dbo"); // we have overwritten IdentityUser, so no more need for the empty AspNetUsers table
modelBuilder.Entity<ApplicationUser>().ToTable("Users", "dbo"); // Use ApplicationUser instead
modelBuilder.Entity<IdentityRole>().ToTable("Roles", "dbo");

How can I avoid ASP.NET Identity from adding this extra field?

I have a new MVC5 project with ASP.NET Identity 2.0 and EF 6.1.1.
I added my own ApplicationUser (based on built-in IdentityUser). This is how my DbContext is created.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
When the database is created I have tables like AspNetUsers, AspNetUserRoles, AspNetUserClaims, and AspNetUserLogins. Then I added OnModelCreating() with just the most basic statements.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}
As soon as I add OnModelCreating(), the identity tables are automatically renamed to ApplicationUsers, IdentityUserRoles, IdentityUserClaims, and IdentityUserLogins. That's fine with me (and I know how to rename them).
But what I don't like: All of a sudden, IdentityUserRoles, IdentityUserClaims, and IdentityUserLogins have an extra field called "ApplicationUser_Id". The original "AspNetXXX" tables didn't have such a field.
Why is that? And is there anything I can do in order to avoid this?
You need to call base.OnModelCreating. There are a number of additional things OnModelCreating does in IdentityDbContext that you may be missing without calling it - the default names of the tables being one of them.
Its best to call it first, then apply your own changes afterwards.
As mentioned by Matt Lassam-Jones
worked for me also and Thank You.
public class NebulaContext : IdentityDbContext<ApplicationUser>
{
public NebulaContext()
: base("Name=MyEntity", throwIfV1Schema: false)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); //Optional
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();//Optional
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); //Optional
base.OnModelCreating(modelBuilder);
}
}

Resources