I have models that have references to each other:
public class Dept
{
[Key]
public int DeptId { get; set; }
[ForeignKey("ManagerId")]
public Emp Manager { get; set; }
public int? ManagerId { get; set; }
public string DeptName { get; set; }
}
public class Emp
{
[Key]
public int EmpId { get; set; }
[Required]
[ForeignKey("DeptId")]
public Dept Dept { get; set; }
public int DeptId { get; set; }
public string Name { get; set; }
}
When I call Add-Migration, I get error:
The ForeignKeyAttribute on property 'Manager' on type 'App.Dept' is not valid. The foreign key name 'ManagerId' was not found on the dependent type 'App.Emp'. The Name value should be a comma separated list of foreign key property names.
What should I do to create migration with these tables?
UPD: Implicit optional Manager does not solve the problem:
modelBuilder.Entity<Emp>().HasRequired(_ => _.Dept).WithOptional(_ => _.Manager);
UPD2: Dept:Emp relation is 1:0..1
UPD3: Maybe another relation will be added to Dept model but it will be also 1:0..1:
[ForeignKey("ManagerId")]
public Emp CTO { get; set; }
public int? CTOId { get; set; }
It is not one to many relation: one department have zero or one manager, and zero or one CTO. At the moment I have only one relation, but I want to name the field ManagerId, not a EmpId.
UPD4: Schema from the beginning of my question with two primary/foreign keys relation (Dept.DeptId/Emp.DeptId, Emp.EmpId/Dept.ManagerId) works in plain SQL. I know workarounds with additional table or without foreign keys, but I need an answer how to make work schema above or why it is not working in EF.
You have mainly three way to configure 1-1 relationships (the case of your error is the 3rd explained case).
Complex types
The first way is with only one table and using complex types. Choosing this configuration has performance impact (often, overall performances are better than other configurations but it depends on the record size and on how many times you have both records).
In your case you need only to mark one of the entities with
ComplexType
attributeWith this model this is the created table
Standard foreign key
The second way is to use a standard foreign key. The model can have navigation properties on both classes, has 2 tables with independent primary keys but only 1 table has the foreign key to the other table (you wrote about this configuration on your question). You obtain this configuration overriding
OnModelCreating
. Using this way there are several configurations you can do using fluent API. The main option is where should EF insert the foreign key. In every configuration you must have Map method (I explain what happens without the Map method in the third way)The model is always this
WithRequiredPrincipal (1-1)
This is the DDL generated
WithRequiredDependent (1-1)
This is the DDL generated
WithOptional (1-0..1)
This is the DDL generated
You can use other methods to obtain similar configurations. I dont show here every example but we can mix these configurations
HasOptional
/WithRequired
HasOptional
/WithOptionalDependent
HasOptional
/WithOptionalPrincipal
EF Default 0..1-1 1-0..1 1-1 configuration
This is how EF is interpreting your configuration. In this case EF generates 2 tables with dependent primary keys. On one table there is an independent primary key (in your case identity(1,1)) and on the other table you have a primary key that is also the foreign key. This is the default configuration. This is the only way to have foreign keys on both tables (not 2 constraints, no way to have 2 circular contraints, see below)
This is the DDL generated
This should be 1-1 relationship but if we look better there is one missing constraint. The primary key of the Dept table should be a foreign key for the second table. Why EF did not insert that constraint? Because we will violate always a constraint so we could not insert records on tables (also inside a transaction a reference key constraint can be violated).
Changing the configuration to
HasRequired
/WithRequiredDependent
we obtain the table with the independent primary key will be the Emps tableThis is the DDL generated
You can use other methods to obtain similar configurations. I dont show here every example but we can mix these configurations
HasOptional
/WithRequired
HasOptional
/WithOptionalDependent
HasOptional
/WithOptionalPrincipal
From your class code you have the following
For 1-1 and 1-0..1, same primary key should appear in both tables and in your design its not the case since both tables will have their own primary key
Now, based on the code you put, the configuration should be as follows
but this will not mean a 1-1 or 1-0..1 relation.
if you want to convert your codes to become 1-0..1, then you code should be like this
The configuration should look like
for more information about the relations, you might read this article Configure One-to-Zero-or-One Relationship:
Hope this will help you