Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception The value of 'X' is unknown when attempting to save changes

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.

like image 437
Bamdad Avatar asked Jul 24 '21 07:07

Bamdad


1 Answers

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.

like image 179
Bamdad Avatar answered Oct 21 '22 14:10

Bamdad