博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Entity Framework多对多关系实践(many-to-many)
阅读量:6625 次
发布时间:2019-06-25

本文共 8514 字,大约阅读时间需要 28 分钟。

 Entity Framework中有三种关系,一对一(one-to-one),一对多(one-to-many),多对多(many-to-many),前两种就不说了,园子里这方面的文章很多(dudu的:,杨延成的:,郝冠军的:),看过之后简单的使用基本没什么问题,这里要说的是第三种:多对多(many-to-many)。

  这里单独把多对多关系拿出来说,不是因为上述系列文章中没有,只不过需求不同,我的需求用上述系列文章中的方法实现不了。这里先用一个例子说一下我的需求吧:我要用EF处理 question(QID,Title)与tag(TID,TagName)之间的关系,这是一个多对多关系(一个问题有多个标签,一个标签有多个问题),因此在数据库中除了question与tag表外应该还有他们的关系表question_tag表,问题就出在question_tag表上。

  如果我的question_tag表仅仅只有两个字段QID与TID,那么用上面系列文章中提到的方法就可以实现,关键代码如下:

1     [Table("Question")]  2     public class Question  3     {
4 [Key] 5 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 6 public int QID { get; set; } 7 public string Title { get; set; } 8 public virtual ICollection
Tags { get; set; } 9 } 10 11 [Table("Tag")] 12 public class Tag 13 {
14 [Key] 15 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 16 public int TID { get; set; } 17 public string TagName { get; set; } 18 public virtual ICollection
Questions { get; set; } 19 } 20 21 public class BlogDbContext : DbContext 22 {
23 protected override void OnModelCreating(DbModelBuilder modelBuilder) 24 {
25 modelBuilder.Entity
() 26 .HasMany(q => q.Tags) 27 .WithMany(t => t.Questions) 28 .Map 29 ( 30 m => 31 {
32 m.MapLeftKey("QID"); 33 m.MapRightKey("TID"); 34 m.ToTable("Question_Tag"); 35 } 36 ); 37 base.OnModelCreating(modelBuilder); 38 } 39 public IDbSet
Questions { get; set; } 40 public IDbSet
Tags { get; set; } 41 }

  现在的问题是我的question_tag表为了业务需求不仅仅只有这两个字段(这个需求应该很常见,本例中增加一个时间字段DateAdded作为示例),因此用上面的方案就不行了。那么要怎么处理呢,找了好多资料都不行,没办法只好自己动手,丰衣足食。

  首先想到的是,既然question_tag表中还有其它字段,那么这个实体肯定要表现出来。然后想到的是,按原来的方法question跟tag是直接产生联系的,EF根据question和tag的定义可以判断出是多对多关系,但现在加了一个关系实体question_tag,question跟question_tag的关系是一对多,tag跟question_tag的关系也是一对多,因此可以通过question_tag来联接question跟tag(数据库中这个表的存在本来就是这个意思),也就是说question跟tag不直接产生联系。有了上面的想法,经过多次尝试后,我把实体间的关系修改为如下形式:

1     [Table("Question")]  2     public class Question  3     {
4 [Key] 5 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 6 public int QID { get; set; } 7 public string Title { get; set; } 8 public virtual ICollection
QuestionTags { get; set; } 9 } 10 11 [Table("Tag")] 12 public class Tag 13 {
14 [Key] 15 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 16 public int TID { get;set;} 17 public string TagName { get; set; } 18 public virtual ICollection
QuestionTags { get; set; } 19 } 20 21 [Table("QuestionTag")] 22 public class QuestionTag 23 {
24 [Key] 25 [Column(Order=0)] 26 [ForeignKey("Question")] 27 public int QID { get; set; } 28 29 [Key] 30 [Column(Order = 1)] 31 [ForeignKey("Tag")] 32 public int TID { get; set; } 33 34 public DateTime DateAdded { get; set; } 35 36 public virtual Question Question { get; set; } 37 38 public virtual Tag Tag { get; set; } 39 }

  有了上面的定义后,DbContext的定义就很简单了,不需要重写OnModelCreating:

1     public class BlogDbContext : DbContext 2     {
3 public IDbSet
Questions { get; set; } 4 public IDbSet
Tags { get; set; } 5 public IDbSet
QuestionTags { get; set; } 6 }

好了,下面开始写测试代码,就是增删改查操作:

 

1     public class EFTest   2     {
3 public void Insert() 4 {
5 using (var db = new BlogDbContext()) 6 {
7 //添加一个question,两个tag 8 var question = new Question() { Title = "abc" }; 9 var tagA = new Tag() { TagName = "a" }; 10 var tagB = new Tag() { TagName = "b" }; 11 var qes = db.Questions.Add(question); 12 var tA = db.Tags.Add(tagA); 13 var tB = db.Tags.Add(tagB); 14 db.SaveChanges(); 15 16 //添加question_tag 17 var questiontaga = new QuestionTag() { QID = qes.QID, TID = tA.TID, DateAdded = DateTime.Now }; 18 var questiontagb = new QuestionTag() { QID = qes.QID, TID = tB.TID, DateAdded = DateTime.Now }; 19 var qtA = db.QuestionTags.Add(questiontaga); 20 var qtB = db.QuestionTags.Add(questiontagb); 21 db.SaveChanges(); 22 Console.WriteLine("Insert Success"); 23 24 //显示数据 25 Show(); 26 } 27 } 28 29 public void Delete() 30 {
31 using (var db = new BlogDbContext()) 32 {
33 var qes = db.Questions.SingleOrDefault(q => q.QID == 1); 34 if (qes != null) 35 {
36 db.Questions.Remove(qes); 37 db.SaveChanges(); 38 Console.WriteLine("Delete Success"); 39 40 Show(); 41 } 42 } 43 } 44 45 public void Update() 46 {
47 using (var db = new BlogDbContext()) 48 {
49 var qes = db.Questions.SingleOrDefault(q => q.QID == 2); 50 if (qes != null) 51 {
52 qes.Title = "update abc"; 53 db.SaveChanges(); 54 Console.WriteLine("Update Success"); 55 56 Show(); 57 } 58 } 59 } 60 61 public void Select() 62 {
63 using (var db = new BlogDbContext()) 64 {
65 var qes = db.Questions.SingleOrDefault(q => q.QID == 2); 66 if (qes != null) 67 {
68 Console.WriteLine("Select Question:"); 69 Console.Write("QID:"+qes.QID + "\tTitle:" + qes.Title+"\tTag:"); 70 qes.QuestionTags.ForEach(t => Console.Write(t.Tag.TagName+"\t")); 71 } 72 Console.WriteLine("\nSelect Success"); 73 } 74 } 75 76 public void Show() 77 {
78 using (var db = new BlogDbContext()) 79 {
80 //显示question 81 var qes = db.Questions; 82 if (qes != null) 83 {
84 Console.WriteLine("Question:"); 85 qes.ForEach(q => 86 {
87 Console.Write("QID:"+q.QID+"\tTitle:"+q.Title+"\tTag:"); 88 q.QuestionTags.ForEach(t => 89 {
90 Console.Write(t.Tag.TagName+"\t"); 91 }); 92 Console.WriteLine(); 93 }); 94 } 95 96 //显示tag 97 var tag = db.Tags; 98 if (tag != null) 99 {
100 Console.WriteLine("Tag:"); 101 tag.ForEach(t => 102 {
103 Console.Write("TID:" + t.TID + "\tTagName:" + t.TagName + "\tQuestion:"); 104 t.QuestionTags.ForEach(q => 105 {
106 Console.Write(q.Question.Title + "\t"); 107 }); 108 Console.WriteLine(); 109 }); 110 } 111 112 Console.WriteLine(); 113 } 114 } 115 }

  很幸运地通过了,运行后数据库中生成的表如下:

 

  要注意的是这里QID和TID不仅仅是PK,也是FK。

  程序的输出如下:

  可以看到程序能很好地满足我的需求。在上面的删除代码中删除了QID为1的question后,数据库中question_tag表中的数据如下:

  我们可以看到,question_tag表中QID为1的数据也同时删除了,正是我们需要的结果。

最后做个小结吧:

  一直以来都是看的多,写的少,从老鸟那里吸收的多,贡献的少,总怕自己写的不好,以后争取慢慢改变这种状况,把自己学到的知识总结出来,争取能给新手一些帮助吧。

  希望这篇文章对大家有所帮助,当然了,限于水平欢迎大家拍砖,提出更好的解决方案。

本文转自博客园博客,原文链接:,如需转载请自行联系原作者

你可能感兴趣的文章
JEESNS数据库表设计结构
查看>>
JavaScript学习笔记:判断变量是否为undefined,判断变量和函数是否声明
查看>>
局域网访问Apache服务器
查看>>
JavaScript 闭包
查看>>
Spark算子:RDD行动Action操作(3)–aggregate、fold、lookup
查看>>
UILabel总结
查看>>
java获取当前时间前一周、前一月、前一年的时间
查看>>
话说WEB开发之页面重绘和回流
查看>>
using标识使用
查看>>
解决linux下不能上网
查看>>
nginx rewrite伪静态配置参数说明
查看>>
python学习笔记(15-18)
查看>>
Oracle 查询不区分大小写 (正则函数)
查看>>
T264接口说明
查看>>
SELinux介绍
查看>>
visual C++ 用 TextOut 输出单个字符
查看>>
Rsyslog实现Nginx日志统一收集
查看>>
开源数字媒体资产管理系统:Razuna
查看>>
linux文本处理三剑客之grep家族及其相应的正则表达式使用详解
查看>>
Java中的IO操作(一)
查看>>