Search code examples
c#.netentity-framework-corehas-many

EF 7 Unidirectional many-to-many error: Every skip navigation must have a configured foreign key


following official doc i setup my objects in that way but got

OneTimeSetUp: System.InvalidOperationException : The skip navigation 'Contatore.Workflow1' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.

for Contatori property of Workflow class

while i add foregin key correctly

public class Workflow
{
    public required int Id { get;  set; }
    public virtual ICollection<Contatore> Contatori { get; set; } = new HashSet<Contatore>();
}

public class Contatore
{
    public override int Id { get; set; }

    public virtual ICollection<Contatore> ContatoriFigli { get; set; } = new HashSet<Contatore>();
    public virtual ICollection<Contatore> ContatoriPadri { get; set; } = new HashSet<Contatore>();
}



internal class ContatoreConfiguration : IEntityTypeConfiguration<Contatore>
{
public void Configure(EntityTypeBuilder<Contatore> builder)
    {
        builder
            .ToTable($"NAT_{Costanti.CodiceApplicazione}_CONTATORE_003")
            .HasKey(c => c.Id)
            .HasName("NAK_DASH_003");

        builder.Property(b => b.Id)
            .HasColumnName("ID_003")
            .HasComment("Chiave primaria")
            .HasDefaultValueSql($"SYUNA{Costanti.CodiceApplicazione}.SEQ_{Costanti.CodiceApplicazione}_003.NEXTVAL");
        
        builder.HasMany(x => x.ContatoriFigli)
            .WithMany(x => x.ContatoriPadri)
            .UsingEntity("NAT_DASH_CONTATORE_LINKS_004",
                m => m.HasOne(typeof(Contatore)).WithMany().HasForeignKey("IdContatorePrincipale").HasPrincipalKey(nameof(Contatore.Id)),
                s => s.HasOne(typeof(Contatore)).WithMany().HasForeignKey("IdContatoreCollegato").HasPrincipalKey(nameof(Contatore.Id)),
                j => {
                    j.Property("IdContatorePrincipale").HasColumnName("ID_CONT_PRINC_004");
                    j.Property("IdContatoreCollegato").HasColumnName("ID_CONT_COLL_004");
                    j.HasKey("IdContatorePrincipale", "IdContatoreCollegato");
                }
            );
    }


internal class WorkflowConfiguration : IEntityTypeConfiguration<Workflow>
{
    public void Configure(EntityTypeBuilder<Workflow> builder)
    {
        builder
            .ToTable($"NAT_DASH_WORKFLOW_005")
            .HasKey(c => c.Id)
            .HasName("NAK_DASH_005");

        builder.Property(b => b.Id)
            .HasColumnName("ID_005")
            .HasComment("Chiave primaria")
            .HasDefaultValueSql($"SYUNA{Costanti.CodiceApplicazione}.SEQ_{Costanti.CodiceApplicazione}_005.NEXTVAL");
               
        builder.HasMany(x => x.Contatori)
            .WithMany()
            .UsingEntity("NAT_DASH_WORKFLOW_CONTATORE_006",
                m => m.HasOne(typeof(Workflow)).WithMany().HasForeignKey("ID_WORKFLOW_006").HasPrincipalKey(nameof(Workflow.Id)),
                s => s.HasOne(typeof(Contatore)).WithMany().HasForeignKey("ID_CONTATORE_006").HasPrincipalKey(nameof(Contatore.Id))

            );
    }

Oracle table:

CREATE TABLE "NAT_DASH_WORKFLOW_CONTATORE_006"
   (    
    "ID_WORKFLOW_006" NUMBER (9,0),
    "ID_CONTATORE_006" NUMBER (9,0), 
    CONSTRAINT NAF_DASH_006_004 FOREIGN KEY (ID_WORKFLOW_006) REFERENCES NAT_DASH_WORKFLOW_005 (ID_005),
    CONSTRAINT NAF_DASH_006_005 FOREIGN KEY (ID_CONTATORE_006) REFERENCES NAT_DASH_CONTATORE_003 (ID_003)
    );

Solution

  • The problem is caused by the UsingEntity configuration

    
    builder.HasMany(x => x.Contatori)
        .WithMany()
        .UsingEntity("NAT_DASH_WORKFLOW_CONTATORE_006",
            m => m.HasOne(typeof(Workflow)).WithMany().HasForeignKey("ID_WORKFLOW_006").HasPrincipalKey(nameof(Workflow.Id)),
            s => s.HasOne(typeof(Contatore)).WithMany().HasForeignKey("ID_CONTATORE_006").HasPrincipalKey(nameof(Contatore.Id))
    
        );
    

    Note that in all UsingEntity overloads expect first right relationship configuration, then left, and finally optionally the join entity itself.

    In this case, the left is Workflow (the one on which you call HasMany) and right is Contatore (the one on which you call WithMany), but your fluent configuration is the exact opposite.

    So simply exchange them and the issue will be solved

    builder.HasMany(x => x.Contatori)
        .WithMany()
        .UsingEntity("NAT_DASH_WORKFLOW_CONTATORE_006",
            s => s.HasOne(typeof(Contatore)).WithMany().HasForeignKey("ID_CONTATORE_006").HasPrincipalKey(nameof(Contatore.Id)),
            m => m.HasOne(typeof(Workflow)).WithMany().HasForeignKey("ID_WORKFLOW_006").HasPrincipalKey(nameof(Workflow.Id))
        );
    

    Also consider doing the same for the other many-to-many relationship shown. Because while it does not produce error, the meaning of the collection navigation properties (and their content) would be exactly the opposite of what is intended.