EF self referencing entity - entity-framework

Why is this EF 6 code generating an unexpected Code First Migration result?
I would expect this code to generate only one ParentId that is a self referencing FK.
This is based on the article attached at the end which was based on EF 4.1. Did anything change in EF 6 that could affect the behaviour in this case?
Entity:
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
public class CategoryMap : EntityTypeConfiguration<Category> {
public CategoryMap() {
HasKey(x => x.CategoryId);
Property(x => x.CategoryId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.Name)
.IsRequired()
.HasMaxLength(255);
HasOptional(x => x.Parent)
.WithMany(x => x.Children)
.HasForeignKey(x => x.ParentId)
.WillCascadeOnDelete(false);
}
}
Code First Migration Result:
CreateTable(
"dbo.Categories",
c => new
{
CategoryId = c.Int(nullable: false, identity: true),
Name = c.String(),
ParentId = c.Int(),
Parent_CategoryId = c.Int(),
})
.PrimaryKey(t => t.CategoryId)
.ForeignKey("dbo.Categories", t => t.Parent_CategoryId)
.Index(t => t.Parent_CategoryId);
Code First Migration Expectation:
CreateTable(
"dbo.Categories",
c => new
{
CategoryId = c.Int(nullable: false, identity: true),
Name = c.String(),
ParentId = c.Int()
})
.PrimaryKey(t => t.CategoryId)
.ForeignKey("dbo.Categories", t => t.ParentId )
.Index(t => t.ParentId );
You can use this as a reference for further details: http://dotnet.dzone.com/news/using-self-referencing-tables

Related

Why a new migration on making a property virtual?

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.

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; }

EF: 1:1 relationship independent association

I'm trying to create a simple 1:1 relationship with EF code first.
Example: A person always owns a single car and the car always belongs to a single person.
Person:
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public Car Car { get; set; }
}
// PersonEntityTypeConfiguration
HasKey(k => k.Id);
Property(p => p.Name).IsOptional();
HasRequired(n => n.Car).WithRequiredPrincipal().WillCascadeOnDelete(true);
Car:
public class Car
{
public Guid Id { get; set; }
public string SerialNumber { get; set; }
}
// CarEntityTypeConfiguration
HasKey(k => k.Id);
Property(p => p.SerialNumber).IsOptional();
This created the following migration script:
// Up
CreateTable(
"dbo.People",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Cars",
c => new
{
Id = c.Guid(nullable: false),
SerialNumber = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.People", t => t.Id, cascadeDelete: true)
.Index(t => t.Id);
I expected EF to generate a foreign key like:
CreateTable(
"dbo.Cars",
c => new
{
Id = c.Guid(nullable: false),
SerialNumber = c.String(),
Person_Id = c.Guid()
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.People", t => t.Person_Id, cascadeDelete: true)
.Index(t => t.Id);
Why EF didn't create a foreign key like that?
How do I tell EF to generate the script that I expect?
If you want Car to have a foreign key to Person this is not a 1:1 association but 1:n (because n cars can refer to the same person).
What you see here is EF's way to enforce a 1:1 association: the primary key of the principal entity (Person) is copied to the primary key of the dependent entity (Car). The latter is also a foreign key to the principal.

WillCascadeOnDelete doesn't establish cascade delete on database

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);
}

Entity Framework Table by Type Design and Insert

I have a table by Type design
class Table1
{
public int id {get;set;}
public string data1{get;set;}
public string data2{get;set;}
}
class Table2
{
public int table1_id {get;set;}
public string data3{get;set;}
public string data4{get;set;}
}
class Table3
{
public int table1_id {get;set;}
public string data5{get;set;}
public string data6{get;set;}
}
In my seed method i insert a row into Table1. new {id=0,...}
Then i do
context.Table2Set.AddOrUpdate(new Table2{table1_id = 0,...});
When i run my Update-Database i get:
System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Am i doing something wronge?
UPDATE The real data
public class Identity
{
public Identity()
{
this.Roles = new List<Role>();
}
public int Id { get; set; }
public System.DateTime DateAdded { get; set; }
public string identityprovider { get; set; }
public string nameidentifier { get; set; }
public System.DateTime LastLoggedIn { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Member : Identity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public Address Address { get; set; }
public int Id { get; set; }
public virtual ICollection<News> News { get; set; }
}
public IdentityMap()
{
// Primary Key
this.HasKey(t => t.Id);
this.Property(t=>t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Properties
this.Property(t => t.identityprovider)
.IsRequired()
.HasMaxLength(255);
this.Property(t => t.nameidentifier)
.IsRequired();
// Table & Column Mappings
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.DateAdded).HasColumnName("DateAdded").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
this.Property(t => t.identityprovider).HasColumnName("identityprovider");
this.Property(t => t.nameidentifier).HasColumnName("nameidentifier");
this.Property(t => t.LastLoggedIn).HasColumnName("LastLoggedIn")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
// Relationships
this.HasMany(t => t.Roles)
.WithMany(t => t.Identities)
.Map(m =>
{
m.ToTable("IdentityRoles");
m.MapLeftKey("Identities_Id");
m.MapRightKey("Roles_Id");
});
this.ToTable("Identities");
}
public MemberMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.FirstName)
.IsRequired()
.HasMaxLength(150);
this.Property(t => t.LastName)
.IsRequired()
.HasMaxLength(50);
this.Property(t => t.Email)
.IsRequired()
.HasMaxLength(150);
this.Property(t => t.Address.Id)
.IsRequired()
.HasMaxLength(100)
.HasColumnName("Address_FirstLine");
this.Property(t => t.Address.ZipCode)
.IsRequired()
.HasMaxLength(20).HasColumnName("Address_Zip");
this.Property(t => t.Address.Contry)
.IsRequired()
.HasMaxLength(100).HasColumnName("Address_Contry");
this.Property(t => t.Address.City)
.IsRequired()
.HasMaxLength(100).HasColumnName("Address_Town");
this.Property(t => t.Id);
// Table & Column Mappings
this.ToTable("Identities_Member");
this.Property(t => t.FirstName).HasColumnName("FirstName");
this.Property(t => t.LastName).HasColumnName("LastName");
this.Property(t => t.Email).HasColumnName("Email");
this.Property(t => t.Id).HasColumnName("Id");
// Relationships
}
CreateTable(
"dbo.Identities_Member",
c => new
{
Id = c.Int(nullable: false),
FirstName = c.String(nullable: false, maxLength: 150),
LastName = c.String(nullable: false, maxLength: 50),
Email = c.String(nullable: false, maxLength: 150),
Address_FirstLine = c.String(nullable: false, maxLength: 100),
Address_Street = c.String(),
Address_Town = c.String(nullable: false, maxLength: 100),
Address_Zip = c.String(nullable: false, maxLength: 20),
Address_Contry = c.String(nullable: false, maxLength: 100),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Identities", t => t.Id)
.Index(t => t.Id);
}
CreateTable(
"dbo.Identities",
c => new
{
Id = c.Int(nullable: false, identity: true),
DateAdded = c.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()"),
identityprovider = c.String(nullable: false, maxLength: 255),
nameidentifier = c.String(nullable: false,maxLength:100),
LastLoggedIn = c.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()"),
})
.PrimaryKey(t => t.Id)
.Index(t=>t.nameidentifier);