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);
Related
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);
I am trying to establish a foreign key relationship between following domain classes using Fluent API (Entity Framework v5):
public partial class User
{
public long UserID { get; set; }
public string UserName { get; set; }
}
public partial class AccountGroup : BaseEntity
{
public long AccountGroupID { get; set; }
public string Name { get; set; }
public long ModifiedBy { get; set; }
public virtual User User { get; set; }
}
Fluent API
builder.Entity<User>().HasKey(p => p.UserID); //Set User Id as primary key
builder.Entity<AccountGroup>().HasKey(x => x.AccountGroupID); //SetAccountGroupId as PK
I am not sure how to set a relationship between User.UserId and AccountGroup.ModifiedBy column using fluent API. I can do this by Data Annotation but I am looking for a solution using fluent api
Remove the ModifiedBy property from your entity:
public partial class AccountGroup
{
public long AccountGroupID { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
And then map the foreign key like so:
builder.Entity<AccountGroup>()
.HasRequired(x => x.User)
.WithMany()
.Map(m => m.MapKey("ModifiedBy"));
Use HasOptional instead if the foreign key should be nullable:
builder.Entity<AccountGroup>()
.HasOptional(x => x.User)
.WithMany()
.Map(m => m.MapKey("ModifiedBy"));
You also don't need to specify the primary keys like you are. Entity Framework will discover those by convention.
This is my first attempting at creating my own EF model, and I'm finding myself stuck attempting to create a lookup table association using Code First so I can access:
myProduct.Category.AltCategoryID
I have setup models and mappings as I understand to be correct, but continue to get
error 0019: Each property name in a type must be unique. Property name 'CategoryID' was already defined
The following models are represented in my code:
[Table("Product", Schema="mySchema")]
public class Product {
[Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None)]
public int ProductID { get; set; }
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
}
[Table("Category", Schema="mySchema")]
public class Category {
[Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None)]
public int CategoryID { get; set; }
public string Name { get; set; }
public int AltCategoryID { get; set; }
}
I have specified the associations with:
modelBuilder.Entity<Product>()
.HasOptional(p => p.Category)
.WithRequired()
.Map(m => m.MapKey("CategoryID"));
I've tried a few other things, including adding the [ForeignKey] annotation, but that results in an error containing a reference to the ProductID field.
You are looking for:
modelBuilder.Entity<Product>()
// Product must have category (CategoryId is not nullable)
.HasRequired(p => p.Category)
// Category can have many products
.WithMany()
// Product exposes FK to category
.HasForeignKey(p => p.CategoryID);
I'm trying to learn code first within the Entity Framework and am having trouble modelling a relationship. It's a basic HR database which for the sake of this has two entities, Employees and Departments.
The Employee belongs to a department and the department has a Team Administrator and a Manager, both of whom are in effect employees. I've tried to model this using the following:
EMPLOYEE
public int? DepartmentID { get; set; }
public virtual Department Department { get; set; }
Context:
modelBuilder.Entity<Employee>().HasOptional(x => x.Department);
DEPARTMENT
public class Department
{
[Required]
public int DepartmentID { get; set; }
[Required(ErrorMessage = "The description is required.")]
public string Description { get; set; }
public int? ManagerID { get; set; }
public virtual Employee Manager { get; set; }
public int? TeamAdministratorID { get; set; }
public virtual Employee TeamAdministrator { get; set; }
}
Context:
modelBuilder.Entity<Department>().HasOptional(x => x.Manager);
modelBuilder.Entity<Department>().HasOptional(x => x.TeamAdministrator);
Obviously I would want the Department table to have only four columns - DepartmentID, Description, ManagerID and TeamAdministratorID but it is generating an extra two for the relationship, namely Manager_EmployeeID and Team_Administrator_EmployeeID. Also, in the Employee table the column Department_DepartmentID is generated to store the DepartmentID instead of it using the DepartmentID column I specified in the entity.
What am I doing wrong? How do I need to define the fields and relationships to avoid having code first ignore what I specify and generate it's own navigation fields in the database?
That because your model configuration is incomplete - you started your own mapping with Fluent API so you must tell EF that these properties are indeed FKs for relations. For employee use:
modelBuilder.Entity<Employee>()
.HasOptional(x => x.Department)
.WithMany()
.HasForeignKey(x => x.DepartmentID);
And for department use:
modelBuilder.Entity<Department>()
.HasOptional(x => x.Manager)
.WithMany()
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Department>()
.HasOptional(x => x.TeamAdministrator);
.WithMany()
.HasForeignKey(x => x.TeamAdministratorID);
Btw. without collection navigation properties on opposite side of relations it will be hard to use model (all WithMany are empty). At least Department should have:
public virtual ICollection<Employee> Employees { get; set;}
And mapping should be modified to:
modelBuilder.Entity<Employee>()
.HasOptional(x => x.Department)
.WithMany(y => y.Employees)
.HasForeignKey(x => x.DepartmentID);
See your employee class
EMPLOYEE
public int? DepartmentID { get; set; }
public virtual Department Department { get; set; }
In order to show the relationship between an employee and a department you used ID and Department. In reality you only need to do this once - via Department. EF by default searches for the ID property and links two classes for you. So your classes should only include one ID - ID of the class itself. Try removing IDs to the other classes.
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