多对多连接表的EF模型

时间:2019-09-18 07:22:56

标签: c# asp.net-core-mvc ef-core-2.2 ef-model-builder

我正在尝试为联结表OwnerCows.dbo简化EF模型。 有一个带有ID的Cow类,一个带有ID的Owner类,我想在一个只有OwnerCowId,CowId(FK)和OwnerId(FK)的OwnerCows表中引用它们。

我得到的错误是: 无法在“ Owner.OwnerCows”和“ OwnerCow.Owner”之间创建关系,因为“ Owner.CowOwners”和“ OwnerCow.Owner”之间已经存在关系。导航属性只能参与单个关系。

这是否意味着我有一个循环引用?我该怎么解决?

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; }

   }

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; }

}

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; }
}

上下文类:

    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-Model 我可以使它与这种设计一起使用吗? EFCore是否可以?还有其他建议吗?谢谢!

2 个答案:

答案 0 :(得分:1)

修复上下文生成器:

#pragma mark - private
- (void)createWithName:(NSString *)name andLogo:(UIImage * _Nullable)logo {
    self.opaque = NO;
    self.alpha = 0.9;
    self.backgroundColor = COLOR(0xfcfcfc);

    //    if (@available(iOS 13.0, *)) {
    //        self.backgroundColor = UIColor.secondarySystemBackgroundColor;
    //    }
    CGSize labelSize = CGSizeZero;


    if (logoView == nil) {
        if (logo != nil) {
            logoView = [[UIImageView alloc] initWithImage:logo];

            [self addSubview:logoView];
        } else {
            NSString *imagePath = [XXUtility pathForResourceNamed:@"Logo" withExtension:@"png"];
            UIImage *imageToAdd = [[UIImage alloc] initWithContentsOfFile:imagePath];
            logoView = [[UIImageView alloc] initWithImage:imageToAdd];
            [self addSubview:logoView];
        }
    }

    if (textLabel == nil) {
        textLabel = [[UILabel alloc] init];
        NSString * labelString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"welcom banner", @"userInterface", [XXUtility bundleForStrings], @"Dear %@, welcome into game", @"user welcome banner text"), name];
        labelSize = [labelString sizeWithAttributes:@{NSFontAttributeName : textLabel.font}];
        textLabel.text = labelString;
        [self addSubview:textLabel];
    }
    NSLog(@"XXUserWelcomeBanner Label size is %f x %f", labelSize.height, labelSize.width);
    [self bannerSizeWithLabelSize:labelSize];
    CGFloat bannerHeight = self.frame.size.height;
    CGFloat bannerWidth = self.frame.size.width;

    CGFloat logoHeight = bannerHeight * 0.45;
    CGFloat logoWidth = logoHeight;
    CGFloat logoXPos = (bannerWidth - logoWidth - labelSize.width) / 2;
    CGFloat logoYPos = (bannerHeight - logoHeight) / 2;
    CGFloat labelHeight = labelSize.height;
    CGFloat labelWidth = labelSize.width;
    CGFloat labelXPos = logoXPos + logoWidth + 5;
    CGFloat labelYPos = (bannerHeight - labelHeight) / 2;

    logoView.frame = CGRectMake(logoXPos, logoYPos, logoWidth, logoHeight);
    textLabel.frame = CGRectMake(labelXPos, labelYPos, labelWidth, labelHeight);

    [self bannerShow];
}

- (void)bannerSizeWithLabelSize:(CGSize)lSize {
    _width = 40 + lSize.width + 5;
    CGFloat height = 40;
    CGFloat xPosition = ScreenWidth * 1/2 - _width * 1/2;
    CGFloat yPosition = - height;

    self.frame = CGRectMake(xPosition, yPosition, _width, height);
}

- (void)bannerShow {
    UIViewController *vc;
    if ([TOP_VIEWCONTROLLER respondsToSelector:@selector(topViewController)]) {
        vc = [TOP_VIEWCONTROLLER topViewController];
    } else {
        vc = TOP_VIEWCONTROLLER;
    }

    [vc.view addSubview:self];

    [UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{

        CGFloat height = 40;
        CGFloat xPos = ScreenWidth * 1/2 - self->_width * 1/2;
        CGFloat yPos = height * 1/3 + KiPhoneXSafeAreaDValue;

        self.frame = CGRectMake(xPos, yPos, self->_width, height);
    } completion:^(BOOL finished) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseIn animations:^{

                CGFloat height = 40;
                CGFloat xPosition = ScreenWidth * 1/2 - self->_width * 1/2;
                CGFloat yPosition = - height;

                self.frame = CGRectMake(xPosition, yPosition, self->_width, height);
            } completion:^(BOOL finished) {
                [self bannerDestroy];
                self.alpha = 0;
            }];
        });
    }];
}


- (void)bannerDestroy {
    [logoView removeFromSuperview];
    [textLabel removeFromSuperview];
    logoView = nil;
    textLabel = nil;
    [self removeFromSuperview];
}

- (void)dealloc
{
    //
    NSLog(@"uiview dealloc");
}

/*
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 - (void)drawRect:(CGRect)rect {
 // Drawing code
 }
 */

@end

...或修复类中的属性名称:

  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);
    });
  }

但是请不要忘记更改具有固定属性名称的dbcontext构建器。

答案 1 :(得分:1)

您的模型非常复杂并且具有一些不必要的关系,例如Owner.Cows,因为您决定配置多对多关系。您可以使用

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

1。要在OwnerCows中拥有OwnerCowId,CowId(FK)和OwnerId(FK),请参阅我的以下配置:

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);

    }
}

在这种情况下,您的OwnerCowId ID是OwnerCows表的主键,这是不合理的,它可能与OwnerCows的CowId,OwnerId记录相同。

2。通常,联接表的主键是一个包含两个外键值的复合键,我建议您可以对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);

    }
}

请参阅https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration