简体   繁体   中英

EF-Model for many-to-many Junction Table

I'm trying to straighten out the EF-Model for a junctiontable OwnerCows.dbo. There's a class Cow with an Id, a class Owner with an Id and i want to reference them both in a OwnerCows-table that has only an OwnerCowId, a CowId(FK) and a OwnerId(FK).

The error I'm getting is: Cannot create a relationship between 'Owner.OwnerCows' and 'OwnerCow.Owner', because there already is a relationship between 'Owner.CowOwners' and 'OwnerCow.Owner'. Navigation properties can only participate in a single relationship.

Does it mean I have a circular reference? How can I solve this?

the Owner.cs:

public class Owner : EntityBase<Guid>
  {

   public string Name { get; set; }

   [NotMapped]
   public ICollection<Cow> Cows { get; set; }
     = new List<Cow>();

   public virtual List<OwnerCow> CowOwners { get; set; }
   public Cow Cow { get; set; }

   }

the Cow.cs:

public class Cow : EntityBase<Guid>
{ 
    [MaxLength(50)]
    public string Name { get; set; }         

    public string Breed { get; set; }
    public string Color {  get; set; }

    public ICollection<Entities.Weight> Weights { get; set; }
      = new List<Weight>();
    public ICollection<Vaccination> Vaccinations { get; set; }
      = new List<Vaccination>();

    [NotMapped]
    public ICollection<Owner> CowOwners { get; set; }
     = new List<Owner>();
    public List<OwnerCow> OwnerCows { get; set; }

}

the OwnerCows.cs:

public class OwnerCow 
{
    public Guid OwnerCowId { get; set; }
    public Cow Cow { get; set; }
    public Guid CowId { get; set; }
    public Owner Owner { get; set; }
    public Guid OwnerId { get; set; }
}

the Context-class:

    public class DogFaceContext : DbContext
    {
      public DogFaceContext()
      {

      }
      public DogFaceContext(DbContextOptions<DogFaceContext> options)
        : base(options)
      {
        Database.Migrate();          
      }

      //Entity Tables
      public virtual DbSet<Owner> Owners { get; set; }
      public virtual DbSet<Cow> Cows { get; set; }
      public virtual DbSet<Vaccination> Vaccination { get; set; }
      public virtual DbSet<Weight> Weight { get; set; }

      //Junction Tables
      public virtual DbSet<OwnerCow> OwnerCows { get; set; }

      protected override void OnModelCreating(ModelBuilder builder)
      {
        base.OnModelCreating(builder);

        builder.Entity<Cow>().HasMany(x => x.CowOwners).WithOne(x => x.Cow);
        builder.Entity<Owner>().HasMany(u => u.CowOwners).WithOne(X => X.Owner);

        builder.Entity("DogFace.API.Entities.OwnerCow", b =>
        {
          b.HasOne("DogFace.API.Entities.Cow", "Cow")
          .WithMany("OwnerCows")
          .HasForeignKey("CowId")
          .OnDelete(DeleteBehavior.Restrict);

         b.HasOne("DogFace.API.Entities.Owner", "Owner")
         .WithMany("OwnerCows")
         .HasForeignKey("OwnerId")
         .OnDelete(DeleteBehavior.Restrict);
        });
      }
   }

EF模型 Can I get it to work with this design? Is it possible with EFCore? Any other suggestions? Thanks!

You model is very complex and has some unnecessary relationships like Owner.Cows since you decide to configure many-to-many relationship.You could just get Owner's cows using

var owner = new Owner();
List<Cow> cows = owner.OwnerCows.Where(oc => oc.OwnerId == owner.Id)
                       .Select(oc => oc.Cow)
                       .ToList();

1.To have OwnerCowId, a CowId(FK) and a OwnerId(FK) in OwnerCows, refer to my below configuration:

public class Owner : EntityBase<Guid>
{
    public string Name { get; set; }   

    public virtual List<OwnerCow> OwnerCows { get; set; }
}

public class Cow : EntityBase<Guid>
{
    [MaxLength(50)]
    public string Name { get; set; }
    public string Breed { get; set; }
    public string Color { get; set; }
    public ICollection<Entities.Weight> Weights { get; set; } = new List<Weight>();
    public ICollection<Vaccination> Vaccinations { get; set; }= new List<Vaccination>();

    public List<OwnerCow> OwnerCows { get; set; }

}

public class OwnerCow
{
    [Key]
    public Guid OwnerCowId { get; set; }
    public Cow Cow { get; set; }
    public Guid CowId { get; set; }
    public Owner Owner { get; set; }
    public Guid OwnerId { get; set; }
}

DbContext:

protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<OwnerCow>()
                .HasOne(oc => oc.Cow)
                .WithMany(c => c.OwnerCows)
                .HasForeignKey(oc => oc.CowId);
        builder.Entity<OwnerCow>()
            .HasOne(oc => oc.Owner)
            .WithMany(o => o.OwnerCows)
            .HasForeignKey(oc => oc.OwnerId);

    }
}

In this case, your OwnerCowId id the primary key for your OwnerCows table which is not reasonable and it may have the same record of CowId,OwnerId for OwnerCows.

2.Usually,the primary key for the join table is a composite key comprising both of the foreign key values,I suggest that you could use composite key for your OwnerCow :

public class OwnerCow
{
    public Cow Cow { get; set; }
    public Guid CowId { get; set; }
    public Owner Owner { get; set; }
    public Guid OwnerId { get; set; }
}

DbContext:

protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);


        builder.Entity<OwnerCow>()
               .HasKey(oc => new { oc.OwnerId, oc.CowId });
        builder.Entity<OwnerCow>()
                .HasOne(oc => oc.Cow)
                .WithMany(c => c.OwnerCows)
                .HasForeignKey(oc => oc.CowId);
        builder.Entity<OwnerCow>()
            .HasOne(oc => oc.Owner)
            .WithMany(o => o.OwnerCows)
            .HasForeignKey(oc => oc.OwnerId);

    }
}

Refer to https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

Fix context builder:

  protected override void OnModelCreating(ModelBuilder builder)
  {
    base.OnModelCreating(builder);

    builder.Entity<Cow>().HasMany(x => x.CowOwners).WithOne(x => x.Cow);
    builder.Entity<Owner>().HasMany(u => u.Cows).WithOne(X => X.Owner); // Cows instead of CowOwners

    builder.Entity("DogFace.API.Entities.OwnerCow", b =>
    {
      b.HasOne("DogFace.API.Entities.Cow", "Cow")
      .WithMany("OwnerCows")
      .HasForeignKey("CowId")
      .OnDelete(DeleteBehavior.Restrict);

     b.HasOne("DogFace.API.Entities.Owner", "Owner")
     .WithMany("CowOwners") // CowOwners instead of OwnerCows
     .HasForeignKey("OwnerId")
     .OnDelete(DeleteBehavior.Restrict);
    });
  }

... or fix property names in classes:

public class Owner : EntityBase<Guid>
{
    public string Name { get; set; }

    [NotMapped]
    public ICollection<Cow> CowOwners { get; set; } // CowOwners instead of Cows ?
        = new List<Cow>();

    public virtual List<OwnerCow> OwnerCow { get; set; } // OwnerCow instead  of CowOwners ?
    public Cow Cow { get; set; }
}

but don't forget to change dbcontext builder with fixed property names.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM