Why a new migration on making a property virtual? - entity-framework

I had entities such as this:
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; }
public Bar2 Bar2 { get; set; }
}
public class Bar
{
public int Id { get; set; }
public string Description { get; set; }
}
public class Bar2
{
public int Id { get; set; }
public string Description { get; set; }
}
which migrations are:
CreateTable(
"dbo.Bar",
c => new
{
Id = c.Int(nullable: false, identity: true),
Description = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Bar2",
c => new
{
Id = c.Int(nullable: false, identity: true),
Description = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Foo",
c => new
{
Id = c.Int(nullable: false, identity: true),
Bar_Id = c.Int(),
Bar2_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Bar", t => t.Bar_Id)
.ForeignKey("dbo.Bar2", t => t.Bar_Id)
.Index(t => t.AlertCause_Id)
then I set a Bar property at Foo as virtual and it breaks with "AutomaticMigrationsDisabledException: Unable to update database to match the current model because there are pending changes and automatic migration is disabled." And after the migration re-scaffolding the code changes only in that Bar_Id becomes BarId but for Bar2 it remains Bar2_Id. So I wonder why it gets me the migration re-scaffolded if it seems it does not change anything? Yes it needs the proxy classes and lazy load, etc. but why a new migration? Thanks!
UPDATE
I've missed that the migration was actually triggered by the adding of a foreign key property, BarId. So my mistake here.

Found the reason. This is an incorrect question as the migration was triggered because of the adding a reference property (BarID). My mistake. So closing it.

Related

Entity Framework can't create foreign key constraint, "There are no primary or candidate keys in the referenced table..."

I'm trying to create a one-to-many relationship between two models, File and AMCN. Here's my File:
public class File
{
public int Id { get; set; }
public int AmcnId { get; set; }
[StringLength(255)]
public string FileName { get; set; }
[StringLength(100)]
public string ContentType { get; set; }
public byte[] Content { get; set; }
public virtual AMCN Amcn { get; set; }
}
Here's part of my AMCN:
public class AMCN
{
public int Id { get; set; }
public DateTime Created { get; set; }
//lots of other data here...
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<FoSL> FoSLs { get; set; }
}
so I went into the package manager console, did add-migration files, which worked and generated this:
public partial class files : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Files",
c => new
{
Id = c.Int(nullable: false, identity: true),
AmcnId = c.Int(nullable: false),
FileName = c.String(maxLength: 255),
ContentType = c.String(maxLength: 100),
Content = c.Binary(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AMCNs", t => t.AmcnId, cascadeDelete: true)
.Index(t => t.AmcnId);
}
public override void Down()
{
DropForeignKey("dbo.Files", "AmcnId", "dbo.AMCNs");
DropIndex("dbo.Files", new[] { "AmcnId" });
DropTable("dbo.Files");
}
}
but when I do update-database I get an error:
Error Number:1776,State:0,Class:16
There are no primary or candidate keys in the referenced table 'dbo.AMCNs' that match the referencing column list in the foreign key 'FK_dbo.Files_dbo.AMCNs_AmcnId'.
Could not create constraint. See previous errors.
As far as I know, entity framework should be able to figure out that I want AmcnId to be a foreign key pointing to AMCN.Id. In fact, when I made another class, FoSL, it worked fine.
public class FoSL
{
public int Id { get; set; }
public int AmcnId { get; set; }
public string FO { get; set; }
public DateTime StartDate { get; set; }
public string Location { get; set; }
}
generated migration class
public partial class FoSL : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.FoSLs",
c => new
{
Id = c.Int(nullable: false, identity: true),
AmcnId = c.Int(nullable: false),
FO = c.String(),
StartDate = c.DateTime(nullable: false),
Location = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AMCNs", t => t.AmcnId, cascadeDelete: true)
.Index(t => t.AmcnId);
}
public override void Down()
{
DropForeignKey("dbo.FoSLs", "AmcnId", "dbo.AMCNs");
DropIndex("dbo.FoSLs", new[] { "AmcnId" });
DropTable("dbo.FoSLs");
}
}
I can't see any real difference between the FoSL and File classes, or their migrations. As far as I know, this should work. So what am I doing wrong?
I've tried adding [Key] on AMCN.Id, didn't help.
edit: the initial dbmigration included this for Up():
CreateTable(
"dbo.AMCNs",
c => new
{
Id = c.Int(nullable: false, identity: true),
Created = c.DateTime(nullable: false),
//lots of other data here....
})
.PrimaryKey(t => t.Id);
Turns out the culprit was something I'd forgotten about; I made a backup database for testing so I wouldn't affect the production database until I'm ready. However, when I backed up the data into a new database, it didn't take the keys with it. So my AMCN table in the database didn't have the primary key that entity framework expected it to have. I added a primary key, ran update-database in the package manager console again, and it worked fine.
(not sure if I should delete this question...)
The exact same thing that happened to Adam happened to me. I had created a backup database for testing changes without affecting my production environment. Using the SQL Import/Export Wizard on MS SSMS to make a copy, meant that the PK information for the tables hadn't been copied across, hence the Migration working, but trying to update the DB did not, as no explicit PK could be found.
Manually configured the PK in SSMS Table Designer and the Migration worked perfectly. Posting in case someone makes the same mistake I did!

Entity framework 6, same column for many foreign keys

I need help with this issue, because after many hours of investigation I am stuck.
I created a datamodel from an existing old database, using Entity Framework 6 (I am using Code First approach). This database was multi-company oriented, so most of its tables has a column "Company" that its used as a part of almost all primary keys and foreign keys.
The datamodel creation created all the foreign keys using Fluent API. But this don't helps and when I try to select data from any table I received errors "invalid columna name 'TABLE_COLUMN'. Because in this database usually the columns has different name in every table and the Entity framework can't determine the relation, so its required to map the column names.
So, I can solve the issue using DataAnnotations, and I can do, for example:
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("CODHOR")]
public int NUMHOR { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("DISTAINIC")]
public int DISTAINIC { get; set; }
[Key]
[Column(Order = 3)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("COMPANY")]
public int COMPANY{ get; set; }
What happends now?
The table has another foreign key that also needs the column COMPANY. Because data annotations don't allow me to use the column twice, I can't make the table to work.
I repeat, in the data model, it created a fluent api definition for the second foreign key, but it don't works.
modelBuilder.Entity<CABAJUSTES>()
.HasMany(e => e.AJUSBLOQ)
.WithRequired(e => e.CABAJUSTES)
.HasForeignKey(e => new { e.NUMAJUST, e.COMPANY})
The fact its that everytime I try to get data I received errors like "Invalid column name CABAJUSTES_CODAJUSTE" and "Invalid column name CABAJUSTES_COMPANY". And I am unable to map this second foreign key.
What can I do?
Thanks in advance.
Its a bit hard to follow your table structure, so I've tried to set up a comprehensive example using some common entities anyone should be able to follow. Please comment if this does not fully describe your problem.
Note that I've deliberately used pretty shitty foreign keys to make sure the helping automapping in Entity Framework doesn't help me, and to show that this works with any legacy database design you may have.
First the expected structure in the example
One Company holds many Articles and many Invoices.
One Invoice holds many InvoiceRows.
Each InvoiceRow may optionally refer to an Article.
The actual Entities
class Company
{
public int TheCompanyKey { get; set; }
public virtual ICollection<Invoice> Its_Invoices { get; set; }
public virtual ICollection<Article> Its_Articles { get; set; }
}
class Invoice
{
public int Its_CompanyKey { get; set; }
public int TheInvoiceKey { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public virtual Company Its_Company { get; set; }
public virtual ICollection<InvoiceRow> Its_Rows { get; set; }
}
class InvoiceRow
{
public int Rows_Company_Key { get; set; }
public int Its_InvoiceID { get; set; }
public int RowNumber { get; set; }
public int? Its_Articles_ID { get; set; }
public string Text { get; set; }
public double Price { get; set; }
public virtual Invoice Its_Invoice { get; set; }
public virtual Article Its_Article { get; set; }
}
class Article
{
public int TheArticleCompany_Key { get; set; }
public int TheArticleKey { get; set; }
public string ArticleNumber { get; set; }
public double Cost { get; set; }
public double TargetPrice { get; set; }
public virtual Company Its_Company { get; set; }
}
The DbContext with OnModelCreating()
There are multiple ways to generate the required structure, depending on if you think top-down or bottom-up. My take on modelling is to start with the base tables and the describe how children relate to them.
class MyContext : DbContext
{
public MyContext() : base("name=MyContext")
{
}
public virtual IDbSet<Company> Companies { get; set; }
public virtual IDbSet<Invoice> Invoices { get; set; }
public virtual IDbSet<InvoiceRow> InvoiceRows { get; set;}
public virtual IDbSet<Article> Articles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Company>()
.HasKey(e => e.TheCompanyKey);
modelBuilder.Entity<Article>()
.HasKey(e => new { e.TheArticleCompany_Key, e.TheArticleKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Articles).HasForeignKey(e => e.TheArticleCompany_Key);
modelBuilder.Entity<Invoice>()
.HasKey(e => new { e.Its_CompanyKey, e.TheInvoiceKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Invoices).HasForeignKey(e => e.Its_CompanyKey);
modelBuilder.Entity<InvoiceRow>()
.HasKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID, e.RowNumber });
modelBuilder.Entity<InvoiceRow>()
.HasRequired(e => e.Its_Invoice).WithMany(e => e.Its_Rows)
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID }).WillCascadeOnDelete();
modelBuilder.Entity<InvoiceRow>()
.HasOptional(e => e.Its_Article)
.WithMany()
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_Articles_ID });
}
}
Finally the generated migration
Running add-migration multikeys in the Package Manager Console window results in the following migration:
public partial class multikeys : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Articles",
c => new
{
TheArticleCompany_Key = c.Int(nullable: false),
TheArticleKey = c.Int(nullable: false),
ArticleNumber = c.String(),
Cost = c.Double(nullable: false),
TargetPrice = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.TheArticleCompany_Key, t.TheArticleKey })
.ForeignKey("dbo.Companies", t => t.TheArticleCompany_Key, cascadeDelete: true)
.Index(t => t.TheArticleCompany_Key);
CreateTable(
"dbo.Companies",
c => new
{
TheCompanyKey = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.TheCompanyKey);
CreateTable(
"dbo.Invoices",
c => new
{
Its_CompanyKey = c.Int(nullable: false),
TheInvoiceKey = c.Int(nullable: false),
InvoiceNumber = c.String(),
InvoiceDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => new { t.Its_CompanyKey, t.TheInvoiceKey })
.ForeignKey("dbo.Companies", t => t.Its_CompanyKey, cascadeDelete: true)
.Index(t => t.Its_CompanyKey);
CreateTable(
"dbo.InvoiceRows",
c => new
{
Rows_Company_Key = c.Int(nullable: false),
Its_InvoiceID = c.Int(nullable: false),
RowNumber = c.Int(nullable: false),
Its_Articles_ID = c.Int(),
Text = c.String(),
Price = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.Rows_Company_Key, t.Its_InvoiceID, t.RowNumber })
.ForeignKey("dbo.Articles", t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.ForeignKey("dbo.Invoices", t => new { t.Rows_Company_Key, t.Its_InvoiceID }, cascadeDelete: true)
.Index(t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.Index(t => new { t.Rows_Company_Key, t.Its_InvoiceID });
}
public override void Down()
{
DropForeignKey("dbo.Articles", "TheArticleCompany_Key", "dbo.Companies");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" }, "dbo.Invoices");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" }, "dbo.Articles");
DropForeignKey("dbo.Invoices", "Its_CompanyKey", "dbo.Companies");
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" });
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" });
DropIndex("dbo.Invoices", new[] { "Its_CompanyKey" });
DropIndex("dbo.Articles", new[] { "TheArticleCompany_Key" });
DropTable("dbo.InvoiceRows");
DropTable("dbo.Invoices");
DropTable("dbo.Companies");
DropTable("dbo.Articles");
}
}
Summary
I believe this describes the OP problem and with a little study gives a good understanding of how Fluent can be used to map entities.
Good luck!

Name of Foreign Key EF6

My entity has a List<SecondEntityDTO>. When EF generates the table, in the table SecondEntities there's a column name FirstEntityDTO_id. I would like this column to be named "ParentEntity_id". How can I do that?
I tried annotating the List of SecondEntityDTO and a bunch of other things...
Edit1: I belive you guys missunderstood.
This is my MainEntity:
[Table("MainEntities")]
public class MainEntityDTO
{
public string Title { get; set; }
[Key]
public int id { get; set; }
public List<SubEntityDTO> SubEntities { get; set; }
}
This is SubEntityDTO:
[Table("SubEntities")]
public class SubEntityDTO
{
[Key]
public int id { get; set; }
public string Title { get; set; }
}
And this is the Migration:
public override void Up()
{
CreateTable(
"dbo.MainEntities",
c => new
{
id = c.Int(nullable: false, identity: true),
Title = c.String(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.id);
CreateTable(
"dbo.SubEntities",
c => new
{
id = c.Int(nullable: false, identity: true),
Title = c.String(),
MainEntityDTO_id = c.Int(),
})
.PrimaryKey(t => t.id)
.ForeignKey("dbo.MainEntities", t => t.MainEntityDTO_id)
.Index(t => t.MainEntityDTO_id);
}
Note the name of the third column on the SubEntities table!
Also, you can do the same using Fluent Api, for example, overwritting the OnModelCreating method of your Context and doing this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SecondEntity>().Property(s => s.FirstEntityDTO_id).HasColumnName("ParentEntity_id");
}
Update
Why you don't edit Func<> that specifies the columns of that table. As you can see you are creating an anonymous type, so you can change the name of the column there, eg:
CreateTable(
"dbo.SubEntities",
c => new
{
id = c.Int(nullable: false, identity: true),
Title = c.String(),
ParentEntity_id = c.Int(),
})
.PrimaryKey(t => t.id)
.ForeignKey("dbo.MainEntities", t => t.ParentEntity_id)
.Index(t => t.ParentEntity_id);
If you do this, remember change the name of that property in the Down method, but if you already executed that script, don't change the FK name yet in the Down method. Execute again the Update Database command specifying the name of that script. That will drop those tables and they will be created once again using the Up method, but now with the FK name that you want it.In that moment is when you can change the FK name in the Down method:
public override void Down()
{
DropForeignKey("dbo.SubEntities", "ParentEntity_id", "dbo.MainEntities");
DropIndex("dbo.SubEntities", new[] { "ParentEntity_id" });
DropTable("dbo.SubEntities");
DropTable("dbo.MainEntities");
}
Put the following code in your SubEntity class:
[ForeignKey("ParentEntity")]
public int ParentEntity_id { get; set; }
public virtual MainEntity ParentEntity { get; set; }

How to set up a hierarchy via Fluent API in Entity Framework

I have a hierarchy of employees, which have boss and subordinates.
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Job { get; set; }
public virtual Employee Boss { get; set; }
public virtual ICollection<Employee> Subordinates { get; set; }
}
I tried to configure it as follows:
internal class EmployeeConfiguracao : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguracao()
{
HasOptional(p => p.Boss).WithMany(p => p.Subordinates).WillCascadeOnDelete(false);
}
}
But the result of Add-Migration Init
Note that Boss is not-null but the is optional (president has no boss)
CreateTable(
"dbo.Employees",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 90),
Job = c.String(),
Boss_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Employees", t => t.Boss_Id)
.Index(t => t.Boss_Id);
How to set up this hierarchy so that it can be employed without boss (as President)
If Boss should be nullable it should be defined like this:
Boss_Id = c.Int(nullable: false)
If it is setup like this:
Boss_Id = c.Int()
then default value for nullable will be used which is true, so it is as you wanted it to be.

EF Code First migrations: Table Per Hierarchy Bug

Often we might need to use Entity Framework Code First with an existing database.
The existing database may have a structure the allows "Table Per Hierarchy" inheritance.
Or we might start with an object model that looks like:
public partial class Person {
public int Id { get; set; }
public string Discriminator { get; set; }
public string Name { get; set; }
public Nullable<int> StudentTypeId { get; set; }
public virtual StudentType StudentType { get; set; }
}
public partial class StudentType {
public StudentType() {
this.People = new List<Person>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}
We create the initial migration:
enable-migrations
add-migration Initial
The migration looks like:
public override void Up()
{
CreateTable(
"dbo.Person",
c => new
{
Id = c.Int(nullable: false, identity: true),
Discriminator = c.String(maxLength: 4000),
Name = c.String(maxLength: 4000),
StudentTypeId = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.StudentType", t => t.StudentTypeId)
.Index(t => t.StudentTypeId);
CreateTable(
"dbo.StudentType",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(maxLength: 4000),
})
.PrimaryKey(t => t.Id);
}
To generate this database we:
update-database
This results in a database that we could have generated like this.
create table Person(
Id int Identity(1,1) Primary key,
Discriminator nvarchar(4000) null,
StudentTypeId int null,
)
create table StudentType(
Id int Identity(1,1) Primary key,
Name nvarchar(4000) not null
)
alter table Person
add constraint StudentType_Person
foreign key (StudentTypeId)
references StudentType(Id)
We use this database in production for a while...
Now we want to add the concept of students that are different from just regular people.
Entity Framework provides three approaches for representing inheritance. In this case we choose the "Table Per Hierarchy" approach.
To implement this approach we modify our POCOs as follows:
public class Person {
public int Id { Get; set; }
public string Name { get; set }
}
public class Student : Person {
public virtual StudentType StudentType { get; set; }
public int? StudentTypeId { get; set; }
}
public class StudentType {
public StudentType() {
Students = new List<Student>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Note:
Only Students have access to the StudentType property.
We don't specify the Discriminator property in our Person class. EF Code First sees that Student inherits from Person and will add a Discriminator column to the Person table for us.
Now we run:
add-migration Person_TPH
And we get this unexpected output.
public override void Up()
{
AddColumn("dbo.Person", "StudentType_Id", c => c.Int());
AlterColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: 128));
AddForeignKey("dbo.Person", "StudentType_Id", "dbo.StudentType", "Id");
CreateIndex("dbo.Person", "StudentType_Id");
}
It should not be adding the StudentType_Id column or index.
We can be explicit by adding the 'StudentMap' class:
public class StudentMap : EntityTypeConfiguration<Student> {
public StudentMap() {
this.HasOptional(x => x.StudentType)
.WithMany()
.HasForeignKey(x => x.StudentTypeId);
}
}
But no joy..
Indeed, if we delete the database and all the migrations.
Then run add-migration Initial against our new model we get:
public override void Up()
{
CreateTable(
"dbo.Person",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(maxLength: 4000),
StudentTypeId = c.Int(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.StudentType", t => t.StudentTypeId)
.Index(t => t.StudentTypeId);
CreateTable(
"dbo.StudentType",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 100),
})
.PrimaryKey(t => t.Id);
}
In this "correct" version we see that EF Code First migrations uses the StudentTypeId column as expected.
Question
Given that the database already exists, is there a way to tell EF Code First migrations to use the existing StudentTypeId column.
The GitHub repo that demonstrates the problem is here:
https://github.com/paulyk/ef_code_first_proof_of_tph_bug.git
Git tags
1_add_migration_Initial
2_add_migration_person_TPH
3_add_studentMap
There are 3 conventions that I found that relate to the discovery of explicit foreign keys in the class:
System.Data.Entity.ModelConfiguration.Conventions.NavigationPropertyNameForeignKeyDiscoveryConvention
System.Data.Entity.ModelConfiguration.Conventions.PrimaryKeyNameForeignKeyDiscoveryConvention
System.Data.Entity.ModelConfiguration.Conventions.TypeNameForeignKeyDiscoveryConvention
The PrimaryKeyNameForeignKeyDiscoveryConvention would not help here since the primary key on StudentType is just Id. The other two would both match on StudentTypeId though, so as long as you aren't removing both of those, the conventions should pick it up.
According to this question (Foreign key navigation property naming convention alternatives) though, you can also add [ForeignKey("StudentTypeId")] to the StudentType property on Student and [InverseProperty("StudentType")] to the Students property on StudentType.
Hope that helps. :)