7. EF Core 导航属性配置
一、多导航属性配型
在 Post 类中,可能需要跟踪是文章的创建者和最后编辑者,下面是 Post 类的两个新的导航属性。
1、设置导航属性方式
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public User Author { get; set; } public User Contributor { get; set; } } public class User { public string UserId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [InverseProperty(nameof(Post.Author))] //设置反转导航属性 public List<Post> AuthoredPosts { get; set; } [InverseProperty(nameof(Post.Contributor))] //设置反转导航属性 public List<Post> ContributedToPosts { get; set; } }
在Post类设置反转导航属性也可以
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; }
[InverseProperty(nameof(User.AuthoredPosts))] public User Author { get; set; }
[InverseProperty(nameof(User.ContributedToPosts))] public User Contributor { get; set; } } public class User { public string UserId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<Post> AuthoredPosts { get; set; } public List<Post> ContributedToPosts { get; set; } }
Post表会默认生成:“导航属性名Id” 的外键 AuthorId,ContributorId
2.ForeignKeyAttribute方式设置
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public User Author { get; set; } public User Contributor { get; set; } } public class User { public string UserId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public User Author { get; set; } [ForeignKey("AuthorID")] public string ContributorID { get; set; } [ForeignKey("ContributorID")] public User Contributor { get; set; } }
3.Fluent API方式
modelBuilder.Entity<Post>().HasOne(p => p.Author).WithMany(u=>u.AuthoredPosts).HasForeignKey("AuthorId"); modelBuilder.Entity<Post>().HasOne(p => p.Contributor).WithMany(u => u.ContributedToPosts);
二、Fluent API显示设置外键
1、显示设置外键
referenceCollectionBuilder.HasForeignKey(p => p.BlogForeignKey); modelBuilder.Entity<Car>().HasKey(c => new { c.State, c.LicensePlate }); //复合主键设置 referenceCollectionBuilder.HasFoHasForeignKey(s => new { s.CarState, s.CarLicensePlate }) //设置有复合主键表的外键,依赖主体要定义CarState,CarLicensePlate 这两个复合主键的属性字段 referenceCollectionBuilder.HasForeignKey("BlogId");
2、显式设置外键关联主体主键之外的键,通过 HasPrincipalKey 配置
modelBuilder.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts).HasForeignKey(p => p.BlogUrl).HasPrincipalKey(b => b.Url); //Post表BlogUrl字段是外键,关联Blog表Url字段 modelBuilder.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts).HasForeignKey(p => new { p.BlogUrl, p.BlogState }).HasPrincipalKey(b => new { b.Url, b.State });
三、一对一关系:一对一关系两端都是引用导航属性,无法判断那个作为主体实体,推荐显式指定外键属性
1、数据注解方式
public class Blog { public int BlogId { get; set; } public string Url { get; set; } public BlogImage BlogImage { get; set; } } public class BlogImage { public int BlogImageId { get; set; } public byte[] Image { get; set; } public string Caption { get; set; }
public int BlogId { get; set; } //显示指定外键属性,设置在Blog端也可以 public Blog Blog { get; set; } }
2、Fluent API方式:使用 HasOne 和 WithOne 方法
modelBuilder.Entity<Blog>().HasOne(p => p.BlogImage).WithOne(i => i.Blog).HasForeignKey<BlogImage>(b => b.BlogForeignKey); //HasForeignKey<BlogImage>指定设置外键那一端
四、多对多:关系型数据库中不支持多对多的映射,通过建立中间表连接,使用一对多的方式模拟多对多关系
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public List<PostTag> PostTags { get; set; } }
public class Tag { public string TagId { get; set; } public List<PostTag> PostTags { get; set; } }
//中间表 public class PostTag { public int PostId { get; set; } public Post Post { get; set; } public string TagId { get; set; } public Tag Tag { get; set; } } class MyContext : DbContext { public DbSet<Post> Posts { get; set; } public DbSet<Tag> Tags { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<PostTag>() .HasKey(t => new { t.PostId, t.TagId }); //设置中间表主键
modelBuilder.Entity<PostTag>() .HasOne(pt => pt.Post) .WithMany(p => p.PostTags) .HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>() .HasOne(pt => pt.Tag) .WithMany(t => t.PostTags) .HasForeignKey(pt => pt.TagId); } }
五、自引用关系: 依赖关系和主体实体类型相同的关系
1.模型定义
public class PictureCategory
{ [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int CategoryId { get; private set; } public string Name { get; set; } public int? ParentCategoryId { get; private set; }public virtual PictureCategory ParentCategory { get; set; } //没有virtual关键字,这会导致导航属性不能加载 public virtual List<PictureCategory> Subcategories { get; set; } }
2.FluentAPI配置:在EFCore Context中重写方法OnModelCreating配置双向关联(ParentCategory 和 SubCategories)
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<PictureCategory>() .WithMany(cat => cat.SubCategories) .HasOne(cat => cat.ParentCategory) .HasForeignKey(cat=>cat.ParentCategoryId); }