如何定义两个实体之间的多个关系?

时间:2017-12-09 11:56:01

标签: c# .net entity-framework asp.net-core entity-framework-core

我的情况是用户可以上传多张照片(一对多)。 用户也可以包含默认照片(一对一)。但是,我实体框架核心2.0 告诉我在使用以下代码时无法识别这种关系:

public class User
{
    public Guid Id { get; set; }

    public ICollection<Photo> Photos{ get; set; }

    public Photo DefaultPhoto { get; set; }
    [ForeignKey("DefaultPhoto")]
    public Guid DefaultPhotoId { get; set; }
}

public class Photo
{
    public Guid Id { get; set; }

    public User Owner { get; set; }
}

我如何实现这些多重关系?

EF-Core显示错误:

  

System.InvalidOperationException:&#39;无法确定导航属性&#39; Photo.Owner&#39;所代表的关系。类型&#39;用户&#39;。手动配置关系,或使用&#39; [NotMapped]&#39;忽略此属性。属性或使用&#39; EntityTypeBuilder.Ignore&#39;在&#39; OnModelCreating&#39;。&#39;

更新

[InverseProperty("Photos")]添加到文件模型中的导航属性所有者似乎正在运行。我不确定这是不是正确的方法?

enter image description here 在此图像中File = Photo; Uploader = Owner(与上述型号相当)。

更新2:

我确认@Ivan在评论中说的是,使用DataAnnotation方法,我在两个方向上获得一对多,而不是一对多和一对一。此图显示了使用InverseProperty生成的数据库(to实体之间的连接显示了双向一对多关系):

enter image description here

在此图片中File = Photo; Uploader = Owner(与上述型号相当)。

1 个答案:

答案 0 :(得分:2)

使用Fluent API建立一对一关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // Establish a one to one relationship with the
    // User as the dependent End
    modelBuilder.Entity<User>()
        .HasOne(u => u.DefaultPhoto)
        .WithOne(p => p.Owner)
        .HasForeignKey<User>(u => u.DefaultPhotoId);
}

要使关系成为一对一(1:0..1)关系,必须在两个主键或两个候选键之间建立关系。 (有关详细信息,请查看this blog post。)

EF目前无法通过Annotations设置候选键(也称为唯一键或备用键),因此这是您唯一的选择。在Microsoft Docs上查看EF文档:One to One relationships

更新:早期代码会自动生成UserId列并正确设置关系。我已将OwnerId字段添加到Photo实体,以手动设置关系,如您所愿:

public class User
{
    public Guid Id { get; set; }

    [InverseProperty("Owner")]
    public ICollection<Photo> Photos{ get; set; }

    public Photo DefaultPhoto { get; set; }
    public Guid DefaultPhotoId { get; set; }
}

public class Photo
{
    public Guid Id { get; set; }

    public Guid OwnerId { get; set; }
    public User Owner { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Photo> Photos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // You may use this instead of the inverse property
        // // The One to Many relationship between
        // // User.Id (Principal End) and Photo.OwnerId
        // modelBuilder.Entity<Photo>()
        //     .HasOne(p => p.Owner)
        //     .WithMany(u => u.Photos)
        //     .HasForeignKey(p => p.OwnerId);

        // Establishes 1:0..1 relationship between
        // Photo.Id (Principal End) and User.DefaultPhoto (Dependent end)
        modelBuilder.Entity<User>()
            .HasOne(u => u.DefaultPhoto)
            .WithOne() // we leave this empty because it doesn't correspond to a navigation property in the Photos table
            .HasForeignKey<User>(u => u.DefaultPhotoId);
    }
}

诀窍在于计算关系(特别是主要结束和从属结束)以及确定哪些需要导航属性。

Inverse属性是必需的,因为EF不知道要映射到Photo.Owner的属性。这只是在这种情况下使关系明确的一种方式。