How to configure one-to-many when the FK/PK is in a common Entity base class

0

Given the following classes that models a TPT inheritance using EF Core 5:

public abstract class Entity
{
    public int Id { get; set; }
    public int PartitionId { get; set; }
    public Company Company { get; set; }
}
public class Employee : Entity { }
public class Company : Entity { }

How can the Entity.PartitionId be configured to be the FK for the Entity.Company navigation property?

I tried the following

public class MyContext : DbContext
{
    public MyContext(DbContextOptions options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Company>().ToTable("Company");
        modelBuilder.Entity<Employee>().ToTable("Employee");
        modelBuilder.Entity<Entity>().ToTable("Entity");
        modelBuilder.Entity<Entity>(b =>
        {
            b.HasOne(e => e.Company)
            .WithMany()
            .HasForeignKey(e => e.PartitionId)
            .HasPrincipalKey(c => c.PartitionId);
        });
    }
}

which works as expected when reading data. e.g:

MyContext ctx = ...;
ctx.Set<Employee>().Include(_ => _.Company).ToList();

is correctly translated to

SELECT [e].[Id], [e].[PartitionId], [t].[Id], [t].[PartitionId]
FROM [Entity] AS [e]
INNER JOIN [Employee] AS [e0] ON [e].[Id] = [e0].[Id]
INNER JOIN (
    SELECT [e1].[Id], [e1].[PartitionId]
    FROM [Entity] AS [e1]
    INNER JOIN [Company] AS [c] ON [e1].[Id] = [c].[Id]
) AS [t] ON [e].[PartitionId] = [t].[PartitionId]

But I'm getting an exception when writing data. e.g.:

MyContext ctx = ...;

// company 1
ctx.Set<Company>().Add(new Company { PartitionId = 1 });
ctx.Set<Employee>().AddRange(new Employee { PartitionId = 1 }, new Employee { PartitionId = 1 }); // exception here

// company 2
ctx.Set<Company>().Add(new Company { PartitionId = 2 });
ctx.Set<Employee>().AddRange(new Employee { PartitionId = 2 }, new Employee { PartitionId = 2 });

ctx.SaveChanges();

System.InvalidOperationException HResult=0x80131509 Message=The instance of entity type 'Employee' cannot be tracked because another instance with the same key value for {'PartitionId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.SetEntityStates(IEnumerable`1 entities, EntityState entityState)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.AddRange(TEntity[] entities)

Thanks in advance!

c#
entity-framework
entity-framework-core
asked on Stack Overflow Mar 16, 2021 by Jan Paolo Go • edited Mar 16, 2021 by Jan Paolo Go

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0