【EF Core】配置关系

EF Core中实体之间关系的配置的套路:

HasXXX(…).WithXXX(…);

XXX可选值One、Many。

  • 一对多:HasOne(…).WithMany(…);
    也可以配置多对一:HasMany(…).WithOne(…);
  • 一对一:HasOne(…).WithOne (…);
  • 多对多:HasMany (…).WithMany(…);

一对多

双向导航属性

    public class Article
    {
        public long Id { get; set; }//主键
        public string Title { get; set; }//标题
        public string Content { get; set; }//内容
        public List<Comment> Comments { get; set; } = new List<Comment>(); //此文章的若干条评论
    }
    class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles");
        }
    }
    public class Comment
    {
        public long Id { get; set; }
        public Article Article { get; set; }
        public long TheArticleId { get; set; }
        public string Message { get; set; }
    }
    public class CommentConfig : IEntityTypeConfiguration<Comment>
    {
        public void Configure(EntityTypeBuilder<Comment> builder)
        {
            builder.ToTable("T_Comments");
            builder.HasOne<Article>(c => c.Article).WithMany(a => a.Comments)
                .IsRequired().HasForeignKey(c => c.TheArticleId);
        }
    }

Comment表中的TheArticleId属性并不是必须的,生成Comment表时会自动生成外键。
但是不创建外键属性的话,如果需要获取外键列的值,就需要关联查询,效率低,所以建议添加
如果添加了外键属性,需要通过HasForeignKey(c => c.TheArticleId)声明一下它是外键属性

单向导航属性

对于主从结构的“一对多”表关系,一般是声明双向导航属性。
而对于其他的“一对多”表关系:如果表属于被很多表引用的基础表,则用单项导航属性。
如用户id是很多表的外键,如果用户实体中添加所有的导航属性,会非常多。这种情况应该用单向导航属性。

    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    class UserConfig : IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder)
        {
            builder.ToTable("T_Users");
        }
    }
    public class Order
    {
        public int Id { get; set; }
        public User User { get; set; }
    }
    public class OrderConfig : IEntityTypeConfiguration<Order>
    {
        public void Configure(EntityTypeBuilder<Order> builder)
        {
            builder.ToTable("T_Orders");
            builder.HasOne<User>(o => o.User).WithMany()//单向导航属性WithMany参数为空即可
                .IsRequired();
        }
    }

单向导航属性WithMany参数为空即可

多对一

上面一对多双向导航案例中,关系是配置再CommentConfig表中,也可以配置在Article表

builder.HasMany<Comment>(a => a.Comments).WithOne(c => c.Article).IsRequired();

自组织

class OrgUnit
{
	public long Id { get; set; }
	public string Name { get; set; }
	public OrgUnit Parent { get; set; }
	public List<OrgUnit> Children { get; set; } = new List<OrgUnit>();
}
class OrgUnitConfig : IEntityTypeConfiguration<OrgUnit>
{
    public void Configure(EntityTypeBuilder<OrgUnit> builder)
    {
        builder.HasOne<OrgUnit>(u => u.Parent).WithMany(p => p.Children);
    }
}

一对一

如订单、快递单属于一对一关系
必须显式的在其中一个实体类中声明一个外键属性。因为一对一关系,外键建在哪个表都可以,需要我们自己指定一个,否则EF不知道在哪个表创建外键

public class Order
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public Delivery Delivery { get; set; }
}
public class Delivery
{
    public long Id { get; set; }
    public string CompanyName { get; set; }
    public String Number { get; set; }
    public Order Order { get; set; }
    public long OrderId { get; set; }
}
public class OrderConfig : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        builder.ToTable("T_Orders");
        builder.HasOne<Delivery>(o => o.Delivery).WithOne(d => d.Order).HasForeignKey<Delivery>(d => d.OrderId);
    }
}

多对多

多对多需要中间表,EF会为我们创建中间表

class Student
{
	public long Id { get; set; }
	public string Name { get; set; }
	public List<Teacher> Teachers { get; set; } = new List<Teacher>();
}
class Teacher
{
	public long Id { get; set; }
	public string Name { get; set; }
	public List<Student> Students { get; set; } = new List<Student>();
}
builder.HasMany<Teacher>(s => s.Teachers).WithMany(t=>t.Students).UsingEntity(j=>j.ToTable("T_Students_Teachers"));

UsingEntity()方法并不是必须的,我们可以通过它指定中间表名称

sql

插入数据

using (var ctx = new TestDbContext())
{
    Article article = new Article { Title = "文章1", Content = "文章1" };
    article.Comments.Add(new Comment { Message = "评论11" });
    article.Comments.Add(new Comment { Message = "评论12" });
    ctx.Articles.Add(article);
    ctx.SaveChanges();
}

查询关联属性

如果需要查询关联属性,需要使用Include(),最终会生成Join查询sql

using (var ctx = new TestDbContext())
{
    var article = ctx.Articles.Include(a => a.Comments).FirstOrDefault(a => a.Id == 1);
    Console.WriteLine($"articleTitle:{article.Title}");
    foreach (var comment in article.Comments)
    {
        Console.WriteLine($"commentMessage:{comment.Message}");
    }
}

查询评论中含有“微软”的所有的文章:

ctx.Articles.Where(a=>a.Comments.Any(c=>c.Message.Contains("微软")));

另一种方法:

ctx.Comments.Where(c => c.Message.Contains("微软")).Select(c => c.Article).Distinct();

posted @ 2022-04-21 00:10  .Neterr  阅读(600)  评论(0编辑  收藏  举报