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; }
Related
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!
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.
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.
I don't know if I'm missing something obvious. We're using Entity Framework 6.0.0-rc1 in a project where the model is set up with the fluent API. Configuration of one of our entities could be:
HasMany(t => t.Entity)
.WithRequired(tc => tc.ParentEntity)
.HasForeignKey(tc => new {tc.Key1, tc.Key2})
.WillCascadeOnDelete(true);
When running this configuration, database gets created correctly, with all tables and fields. Even relationships are correctly established but not the delete cascade.
If I go to Management Studio and I inspect update/delete rules of the relationship, both are deactivated.
Thanks for your help.
With these classes:
public class Parent
{
public int ID { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int ID { get; set; }
public int ParentID { get; set; }
public Parent Parent { get; set; }
}
Configured like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired(c => c.Parent)
.HasForeignKey(c => c.ParentID)
.WillCascadeOnDelete(true);
}
Puts cascade delete on the foreign key:
public override void Up()
{
CreateTable(
"dbo.Children",
c => new
{
ID = c.Int(nullable: false, identity: true),
ParentID = c.Int(nullable: false),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Parents", t => t.ParentID, cascadeDelete: true)
.Index(t => t.ParentID);
CreateTable(
"dbo.Parents",
c => new
{
ID = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ID);
}
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. :)