There are these two entities:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public CompanyVehicle CompanyVehicle { get; set; }
}
and
public class CompanyVehicle
{
public int Id { get; set; }
public string Name { get; set; }
public Employee Employee { get; set; }
}
Using Entity Framework Core 5.0.8
on SQL Server 2019
, the configuration for CompanyVehicle is:
entityBuilder.HasOne(t => t.Employee)
.WithOne(t => t.CompanyVehicle)
.HasForeignKey<Employee>(t => t.Id)
.IsRequired();
And we'll try to insert something:
public void Create(Employee employee)
{
employee.CompanyVehicle = new CompanyVehicle();
dbContext.Add<Employee>(employee);
dbContext.SaveChanges();
}
The above code used to work fine in EF6
. Two new records in both Employee and CompanyVehicle tables were created with the same Id
. After migrating to EF Core 5.0.8
, dbContext.SaveChanges() throws an exception:
System.InvalidOperationException: 'The value of 'Employee.Id' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known.'
Note that these entities are just examples and the database design should not be altered in my case.
Update
After some more investigation, I've found out my problem is:
Having X
(principal) and Y
(dependent) as two tables where X.Id
is PK for X
and Y.Id
is PK for Y
and also FK to X
, in EF Core a record of X
cannot be inserted.
So I finally found the problem, configuring a Property to be both PK
and FK
is possible and very easy. We had our old codes after migrating to EFCore
from EF6
in an assembly. The project is a framework so in OnModelCreating
we use modelBuilder.ApplyConfigurationsFromAssembly
in our base DbContext
to register configurations in the guest projects. The project will automatically find all the configurations in all of assemblies referenced by the project or DLLs in the application path.
The key point is: In EF Core
explicit fluent FK
configuration is in the reverse order compared to EF6
. So in EF6
for Employee
we used to write:
this.HasRequired(t => t.CompanyVehicle)
.WithRequiredDependent(t => t.Employee)
.HasForeignKey(d => d.Id);
and in EF Core
we should write:
b.HasOne(t => t.CompanyVehicle)
.WithOne(t => t.Employee)
.HasForeignKey<Employee>(t => t.Id).IsRequired();
The parameter d
used in the first part is of type CompanyVehicle
. So our migrator converted the old code to:
b.HasOne(t => t.CompanyVehicle)
.WithOne(t => t.Employee)
.HasForeignKey<CompanyVehicle>(t => t.Id).IsRequired();
Which is incorrect. The generic parameter should be the dependent table type. We later fixed the issue in a new namespace but the ApplyConfigurationsFromAssembly
method kept applying the obsolete code after our configuration too.
I used the following block of code at the end of OnModelCreating
to investigate the issue:
foreach (var entity in modelBuilder.Model.GetEntityTypes())
foreach(var key in entity.GetForeignKeys())
{
//Check what is in the key...
}
and noticed that there are duplicated keys configured for my entities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With