zoukankan      html  css  js  c++  java
  • Entity Framework多对多关系实践(manytomany)

      Entity Framework中有三种关系,一对一(one-to-one),一对多(one-to-many),多对多(many-to-many),前两种就不说了,园子里这方面的文章很多(dudu的:Entity Framework 实践系列,杨延成的:EF框架step by step郝冠军的:Entity Framework系列文章),看过之后简单的使用基本没什么问题,这里要说的是第三种:多对多(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<Tag> 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<Question> Questions { get; set; }
    19 }
    20
    21 public class BlogDbContext : DbContext
    22 {
    23 protected override void OnModelCreating(DbModelBuilder modelBuilder)
    24 {
    25 modelBuilder.Entity<Question>()
    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<Question> Questions { get; set; }
    40 public IDbSet<Tag> 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<QuestionTag> 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<QuestionTag> 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<Question> Questions { get; set; }
    4 public IDbSet<Tag> Tags { get; set; }
    5 public IDbSet<QuestionTag> 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的数据也同时删除了,正是我们需要的结果。

    最后做个小结吧:

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

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

    版权

    作者:Artwl

    出处:http://artwl.cnblogs.com

    本文首发博客园,版权归作者跟博客园共有。转载必须保留本段声明,并在页面显著位置给出本文链接,否则保留追究法律责任的权利。

  • 相关阅读:
    八大排序算法——插入排序(动图演示 思路分析 实例代码java 复杂度分析)
    八大排序算法——冒泡排序(动图演示 思路分析 实例代码java 复杂度分析)
    八大排序算法——选择排序(动图演示 思路分析 实例代码Java 复杂度分析)
    蓝桥杯 算法训练 素因子去重 (java)
    蓝桥杯 每周一练 第一周(3n+1问题)
    第八届蓝桥杯程序设计大赛 国赛 填空题第一题 平方十位数
    拼多多 2018 校招编程题 六一儿童节
    泛型的实质
    JAVA 反射之Method
    JAVA反射之 Field (属性)
  • 原文地址:https://www.cnblogs.com/artwl/p/2234359.html
Copyright © 2011-2022 走看看