提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在进行项目开发时,很少有一张数据库表是独立存在的,大部分数据表之间都是有关系的,这也是关系型数据库的体现
一、一对一
1.代码构成
实体之间的关系有很多,最常见就是一对一的关系了,现在我们通过一个实例来体会一下一对一是怎么进行配置的
上面是最终的项目结构
- 首先是两个实体类
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; } ///指向订单的外键
}
class Order
{
public long Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Delivery? Delivery { get; set; }
}
- 然后是各自对应的Config配置类
class DeliveryConfig : IEntityTypeConfiguration<Delivery>
{
public void Configure(EntityTypeBuilder<Delivery> builder)
{
builder.ToTable("T_Deliveries");
}
}
class OrderConfig : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.ToTable("T_Orders");
builder.Property(o => o.Address).IsUnicode();
builder.Property(o => o.Name).IsUnicode();
builder.HasOne<Delivery>(o => o.Delivery).WithOne(o => o.Order).HasForeignKey<Delivery>(d => d.OrderId);
}
}
- 这里需要注意了,我们一对一的配置就在上面这些代码中,一开始创建的实体类时,
public long OrderId { get; set; } //指向订单的外键
,这里配置了一个外键,大家都知道外键是干啥的,因为是一对一,这个外键可以说在哪个类上配置都可以的,这里只是演示其中一种,而在Order
实体类中有这样一个属性public Delivery? Delivery { get; set; }
,这个属性的意义就是一个订单对应一个快递信息,是快递Delivery
的引用类型 - 而在配置类中,在
OrderConfig
中进行的一对一的配置 builder.HasOne<Delivery>(o => o.Delivery).WithOne(o => o.Order).HasForeignKey<Delivery>(d => d.OrderId);
的意义也很明确,代表:Order实体类中有一个Delivery属性,这个属性对应一个订单Order,并且在Delivery
类中有一个外键OrderId
2.测试例子
完成上面的代码书写后,别忘了DbContext
class MyDbContext:DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<Delivery> Deliveries { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer
("Server=.;Database=demo4;Trusted_Connection=True;MultipleActiveResultSets=true;Encrypt=True;TrustServerCertificate=True;");
optionsBuilder.LogTo(Console.WriteLine);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//从当前程序集中加载所有的IEntityTypeConfiguration
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
执行Add-Migration
命令,然后Update-database
这里提供一些测试例子
class Program
{
static async Task Main(string[] args)
{
using(MyDbContext context =new MyDbContext())
{
Order order = new Order();
order.Address = "某某市某某区";
order.Name = "USB充电器";
Delivery delivery = new Delivery();
delivery.CompanyName = "蜗牛快递";
delivery.Number = "SN1654921344";
delivery.Order = order;
context.Deliveries.Add(delivery);
await context.SaveChangesAsync();
}
}
}
二、一对多
使用一个文章Article
对应多个评论 Comment
来进行实例演示
1.代码构成
- 实体类的代码
class Article
{
public long Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public List<Comment> comments { get; set; } = new List<Comment>(); //建议给个空的List
}
class Comment
{
public long Id { get; set; }
public string Message { get; set; }
public Article TheArticle { get; set; }
public long TheArticleId { get; set; }
}
- 实体类的配置
class ArticleConfig : IEntityTypeConfiguration<Article>
{
public void Configure(EntityTypeBuilder<Article> builder)
{
builder.ToTable("T_Articles");
builder.Property(a => a.Title).HasMaxLength(100).IsUnicode().IsRequired();
builder.Property(a => a.Message).IsUnicode().IsRequired();
}
}
class CommentConfig : IEntityTypeConfiguration<Comment>
{
public void Configure(EntityTypeBuilder<Comment> builder)
{
builder.ToTable("T_Comments");
builder.Property(a => a.Message).IsUnicode().IsRequired();
builder.HasOne<Article>(c => c.TheArticle).WithMany(a => a.comments).HasForeignKey(c=>c.TheArticleId).IsRequired();
}
}
- 在上面是一对多的配置类,我们可以看到在
Article
中,是正常配置的表名和属性限制的,我们是在多的一方,也就是评论Comment
的配置类中进行一对多进行配置的 builder.HasOne<Article>(c => c.TheArticle).WithMany(a => a.comments).HasForeignKey(c=>c.TheArticleId).IsRequired();
意义很明显一个文章对应多个评论,并且在Comment
中有一个外键TheArticleId
至于DbContext的配置,这里省略了,自行配置…
2.测试例子
internal class Program
{
static void Main(string[] args)
{
using(MyDbContext context =new MyDbContext())
{
/*Article a1 = new Article();
a1.Title= "杨中科被评为亚洲最有实力程序员";
a1.Message = "据报道...";
Comment c1 = new Comment { Message="太厉害了!"};
Comment c2 = new Comment { Message="吹吧,SB!"};
a1.comments.Add(c1);
a1.comments.Add(c2);
context.Articles.Add(a1);
context.SaveChanges();*/
//通过文章获取评论数据
/*Article article = context.Articles.Include(a => a.comments).Single(a => a.Id == 1);
Console.WriteLine(article.Id);
Console.WriteLine(article.Title);
foreach(Comment com in article.comments)
{
Console.WriteLine(com.Id+","+com.Message);
}*/
//通过评论获取文章数据
/* Comment comment = context.Comments.Include(c => c.TheArticle).Single(c => c.Id == 2);
Console.WriteLine(comment.Message);
Console.WriteLine(comment.TheArticle.Id+","+comment.TheArticle.Title);*/
/*var value = context.Articles.Select(a => new { a.Id, a.Title }).First();*/
/*var comment = context.Comments.Select(c => new { c.Id, c.TheArticleId }).Single(c => c.Id == 2);
Console.WriteLine(comment.Id + "," + comment.TheArticleId);*/
}
}
}
三,多对多
多对多的关系配置演示,一般使用老师学生的例子…
1.代码构成
- 实体类
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>();
}
- 在多对多的关系配置中,由于每个实体中都会有不确定的对应关系,因此,这时候外键是无法进行描述的,只能采用第三方关联表进行描述关系,不过在
EF Core
中,我们不需要自己去维护和生成这个表,只需要我们配置好多对多的关系,EF Core
会帮我们完成这一工作! - 这两个类中都有彼此的List集合类
这样的实体类设置,是多对多关系的设置的一种处理…
- 实体配置类
class TeacherConfig : IEntityTypeConfiguration<Teacher>
{
public void Configure(EntityTypeBuilder<Teacher> builder)
{
builder.ToTable("T_Teachers");
builder.Property(s => s.Name).IsUnicode().HasMaxLength(20);
}
}
class StudentConfig : IEntityTypeConfiguration<Student>
{
public void Configure(EntityTypeBuilder<Student> builder)
{
builder.ToTable("T_Students");
builder.Property(s => s.Name).IsUnicode().HasMaxLength(20);
builder.HasMany<Teacher>(s => s.Teachers).WithMany(t => t.Students).UsingEntity(j => j.ToTable("T_Students_Teachers"));
}
}
- 这两个配置类,一个是正常配置属性,另一个则负责了多对多关系的配置,当然也可以反过来,都可以的
- 现在来看配置类中代码,它的意义是:学生有很多老师,老师也有很多学生,并且设置关联表的名字
之后的DbContext,Add-Migration Update-database
等省略
2.测试例子
static void Main(string[] args)
{
using(MyDbContext myDb =new MyDbContext())
{
/*Student s1 = new Student { Name="张三"};
Student s2 = new Student { Name="李四"};
Student s3 = new Student { Name="王五"};
Teacher t1 = new Teacher { Name = "Tom" };
Teacher t2 = new Teacher { Name = "Jerry" };
Teacher t3 = new Teacher { Name = "Zack" };
s1.Teachers.Add(t1);
s1.Teachers.Add(t2);
s2.Teachers.Add(t3);
s2.Teachers.Add(t2);
s3.Teachers.Add(t1);
s3.Teachers.Add(t2);
s3.Teachers.Add(t3);
myDb.Teachers.Add(t1);
myDb.Teachers.Add(t2);
myDb.Teachers.Add(t3);
myDb.Students.Add(s1);
myDb.Students.Add(s2);
myDb.Students.Add(s3);
myDb.SaveChanges();*/
//查询
var teachers= myDb.Teachers.Include(t => t.Students);
foreach(var s in teachers)
{
Console.WriteLine(s.Name);
foreach(Student student in s.Students)
{
Console.WriteLine("\t"+student.Name);
}
}
}
}
}