I have the following classes and I've created the database tables using EF Code-first. However, I found the deletion cascade is on and tried to remove it.
public class Category
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(100)]
public string Title { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Event
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[ForeignKey("Category")]
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
And I have added the following code in the DbContext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Use singular table names
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Database.SetInitializer<MyContext>(null);
modelBuilder.Entity<Category>()
.HasMany(c => c.Events)
.WithRequired()
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
}
However, the generated migration code generate two AddForeignKey states with one with cascadeDeletion and another one without it.
AddForeignKey("dbo.Event", "CategoryId", "dbo.Category", "Id");
AddForeignKey("dbo.Event", "CategoryId", "dbo.Category", "Id", cascadeDelete: true);
CreateIndex("dbo.Event", "CategoryId");
CreateIndex("dbo.Event", "CategoryId");
The .WithRequired() call you use configures the relationship to be optional:required without a navigation property on the other side of the relationship so that results in one AddForeignKey call.
Then you have a Category navigation property that hasn't been configured in the FluentAPI so EF generates another AddForeignKey call that uses the default configuration.
Try using the override of WithRequired that configures the relationship to be optional:required with a navigation property on the other side of the relationship
modelBuilder.Entity<Category>()
.HasMany(c => c.Events)
.WithRequired(e => e.Category)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
Related
modelBuilder.Entity<Food>(food =>
{
food.ToTable("foods");
food.Property(e => e.FoodCategoryId).HasColumnName("food_category_id").IsRequired();
food.HasOne(e => e.FoodCategory).WithMany().HasForeignKey(e =>
e.FoodCategoryId).HasConstraintName("fk_foods_food_categories_id");
});
I wrote one to many relation.But i need many to one
One to many and many to one are the same definition but this article Configuring One To Many Relationships in Entity Framework Core can help you to do this.
The following model represents companies and employees with an inverse navigation property defined in the dependent entity (Employee) but no matching foreign key property in the dependent
note: you can access the Foreign key from navigation property
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Company Company { get; set; }
}
A company has many employees, each with one company. That relationship is represented as follows:
// This method belongs to your context class which is inherited from DbContext
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Employees)
.WithOne(e => e.Company);
}
It can also be configured by starting with the other end of the relationship:
// This method belongs to your context class which is inherited from DbContext
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOne(e => e.Company)
.WithMany(c => c.Employees);
}
Morning, I used Code First to build my database, and inheritance hierarchy of Table per Type (TPT) approach was implemented. As below is a sample model of my project:
public enum Type
{
A = 0,
B = 1
}
public abstract class Device
{
public int DeviceId { get; set; }
public Type Type { get; set; }
}
[Table("DeviceA")]
public class DeviceA : Device
{
public int Value { get; set; }
}
[Table("DeviceB")]
public class DeviceB : Device
{
public int Value { get; set; }
}
And I have a table, which is related to both A & B. The model is as shown below:
public class Sir
{
public int SirId { get; set; }
[Required]
public virtual DeviceA DeviceA { get; set; }
public virtual DeviceB DeviceB { get; set; }
}
And the OnModelCreating function as below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Sir>()
.HasRequired(s => s.DeviceA)
.WithMany()
.WillCascadeOnDelete(true);
modelBuilder.Entity<Sir>()
.HasOptional(s => s.DeviceB)
.WithMany()
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Here comes the problem, when I attempt to delete a device in the superclass. A SQL error prompted:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.DeviceA_dbo.Devices_DeviceId". The conflict occurred in database "CodeFirst", table "dbo.DeviceA", column 'DeviceId'.
As far as I understand, according to MSDN documentation in http://msdn.microsoft.com/en-us/data/jj591620.aspx (Under the header section of Enabling Cascade Delete), it states that . If a foreign key on the dependent entity is not nullable, then Code First sets cascade delete on the relationship. If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.
But still, I fail to cascade delete a record A in the superclass after trying for hours.
Please advise. Thanks !
Try mapping the foreign keys:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Sir>()
.HasRequired(s => s.DeviceA)
.WithMany()
.Map(m => m.MapKey("DeviceA_Id"))
.WillCascadeOnDelete(true);
modelBuilder.Entity<Sir>()
.HasOptional(s => s.DeviceB)
.WithMany()
.Map(m => m.MapKey("DeviceB_Id"))
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
I'm using Entity Framework 5, Code-First.
I've two domain objects (or tables). 1st is User, and 2nd is UserProfile. One user can have only one profile, and one profile belongs to only one user. That is 1-1 relationship.
Here are the classes.... (I simplified the code to make it understandably, It is actually more complex)
User
public class User {
public virtual Int64 UserId { get; set; }
public virtual UserProfile UserProfile { get; set; }
public virtual String Username{ get; set; }
public virtual String Email { get; set; }
public virtual String Password { get; set; }
}
UserProfile
public class UserProfile {
public virtual Int64 UserId { get; set; }
public virtual User User { get; set; }
public virtual Int64 Reputation { get; set; }
public virtual String WebsiteUrl { get; set; }
}
Here are the Maps....
UserMap
public UserMap() {
this.Property(t => t.Email)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.Password)
.IsRequired()
.HasMaxLength(15);
this.Property(t => t.Username)
.IsRequired()
.HasMaxLength(15);
}
UserProfileMap
public UserProfileMap()
{
this.HasKey(t => t.UserId);
}
Here is the Context....
public class TcContext : DbContext {
static TcContext () {
Database.SetInitializer(new TcContextInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Configurations.Add(new UserMap());
modelBuilder.Configurations.Add(new UserProfileMap());
}
}
And here is my error message....
Unable to determine the principal end of an association between the types 'Tc.Domain.UserProfile' and 'Tc.Domain.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I think in this way EF should determine the relationship automatically. But it gives me the above error message. I've researched this problem for a while but can't find a good illustration of the problem in my case.
Where is my mistake? Or, should I define some sort of additional relations in maps?
I had to modify the UserMap class as follows
public UserMap() {
this.Property(t => t.Email)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.Password)
.IsRequired()
.HasMaxLength(15);
this.Property(t => t.Username)
.IsRequired()
.HasMaxLength(15);
this.HasOptional(t => t.UserProfile)
.WithRequired(t => t.User);
}
It essentially says:
"User has an optional entity UserProfile which in turn has a required User entity"
The definition of the key in UserProfile is a must here.
Just looked at it again, do you need to add HasKey to UserMap so it knows which one is the key otherwise you could do the following :-
Are you able to change UserProfile to inherit from User and then add this to your OnModelCreating method
modelBuilder.Entity().ToTable("UserProfile");
this will create your one to one relationship on the database. I had to do this with my current model to get one to one otherwise if you do not specify the ToTable part it will lump them into one table with a Discriminator column
cheers Mark
I will create two references between Employee and Team entities with foreign keys.
So I defined two entities as follow
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
[ForeignKey("FirstTeam")]
public int FirstTeamId { get; set; }
[InverseProperty("FirstEmployees")]
public virtual Team FirstTeam { get; set; }
[ForeignKey("SecondTeam")]
public int SecondTeamId { get; set; }
[InverseProperty("SecondEmployees")]
public virtual Team SecondTeam { get; set; }
}
public class Team
{
public int Id { get; set; }
public string TeamName { get; set; }
[InverseProperty("FirstTeam")]
public virtual ICollection<Employee> FirstEmployees { get; set; }
[InverseProperty("SecondTeam")]
public virtual ICollection<Employee> SecondEmployees { get; set; }
}
I thought it is correct theoretically, but it shows the Exception as follow :
{"Introducing FOREIGN KEY constraint 'Employee_SecondTeam' on table 'Employees' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint. See previous errors."}
Can anybody help me?
Thanks in advance
Kwon
It is theoretically correct but SQL server (not Entity framework) doesn't like it because your model allows single employee to be a member of both First and Second team. If the Team is deleted this will cause multiple delete paths to the same Employee entity.
This cannot be used together with cascade deletes which are used by default in EF code first if you define foreign key as mandatory (not nullable).
If you want to avoid the exception you must use fluent mapping:
public Context : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Team> Teams { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>()
.HasRequired(e => e.SecondTeam)
.WithMany(t => t.SecondEmployees)
.HasForeignKey(e => e.FirstTeamId)
.WillCascadeOnDelete(false);
...
}
}
This will result in scenario where you must delete members of SecondTeam manually before you delete the team.
All is correct in previous answer, but one thing is wrong
modelBuilder.Entity<Employee>()
.HasRequired(e => e.SecondTeam)
.WithMany(t => t.SecondEmployees)
.HasForeignKey(e => e.SecondTeamId) // mistake
.WillCascadeOnDelete(false);
FirstTeamId instead of SecondTeamId will cause that in SecondTeam navigation property will be always FirstTeam
I have two entities referenced one to many. When entity framework created the table it creates two foreign keys, one for the key I have specified with the fluent interface and the other for the ICollection. How do I get rid of the duplicate foreign key?
public class Person
{
public long RecordId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public long DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
public class Department
{
public long RecordId { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasRequired(p => p.Department)
.WithMany()
.HasForeignKey(p => p.DepartmentId)
.WillCascadeOnDelete(false);
}
Thanks!
You must specify the many-end of the association explicitely:
modelBuilder.Entity<Person>()
.HasRequired(p => p.Department)
.WithMany(d => d.People)
.HasForeignKey(p => p.DepartmentId)
.WillCascadeOnDelete(false);
Otherwise EF will assume that there are two associations: One which is not exposed in Department with the foreign key DepartmentId and navigation property Department in the Person class as you have defined in the Fluent code - and another association which belongs to the exposed navigation property People but with another not exposed end in Person and a foreign key automatically created by EF. That's the other key you see in the database.
The default Code First conventions detect your DepartmentId foreign key, since it is, well, conventional. I think you should remove the Fluent definition:
modelBuilder.Entity<Person>()
.HasRequired(p => p.Department)
.WithMany()
.WillCascadeOnDelete(false);
best thing is to remove departmentid property from Person class and add the following statement. MapKey will create foreign key column with the name you specify
modelBuilder.Entity<Person>().HasRequired(p => p.Department)
.WithMany().Map(x=>x.MapKey("DepartmentId"))
.WillCascadeOnDelete(false);