1.无载荷(with NO Payload)的多对多关系建模
在数据库中,存在通过一张链接表来关联两张表的情况。链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到实体框架模型中。
1.1 假设我们有如下数据关系:
我们有三张表用来存储Album(专辑)、Artist(艺人)、LinkTable(专辑与艺人的关系表)。一张“专辑”可以有多个“艺人”,同时一个“艺人”也可以有多张专辑。
1.2 右键你的项目,使用entityframework的database first创建实体模型。注意把这三张表都选上,当点击“完成”之后,实体框架生成的视图如图4-1
图4-1
1.3 观察图4-1可以发现,Album与Artist之间的关系被映射成了一条多对多的直线,而当初选择的LinkTable表没有出现在视图中。这是因为LinkTable中没有标量属性,也就是说没有载荷。实体框架认为,LinkTable存在的意义就是表示Album与Artist之间的关系的,而上图足以表示他两的关系,所以LinkTable就没有存在的必要了。但是如果LinkTable中有标量属性,实体框架则会为他创建相应的模型,如下一节2.1。
1.4 对上述关系的插入与查询测试。输出如图4-2
1 using (var context = new ef6recipesEntities()) 2 { 3 4 // 添加一个拥有两张专辑的艺术家 5 var artist = new Artist { FirstName = "Alan", LastName = "Jackson" }; 6 var album1 = new Album { AlbumName = "Drive" }; 7 var album2 = new Album { AlbumName = "Live at Texas Stadium" }; 8 artist.Albums.Add(album1); 9 artist.Albums.Add(album2); 10 context.Artists.Add(artist); 11 12 //添加两个艺术家的专辑 13 var artist1 = new Artist { FirstName = "Tobby", LastName = "Keith" }; 14 var artist2 = new Artist { FirstName = "Merle", LastName = "Haggard" }; 15 var album = new Album { AlbumName = "Honkytonk University" }; 16 artist1.Albums.Add(album); 17 artist2.Albums.Add(album); 18 context.Artists.Add(artist1); 19 context.Artists.Add(artist2); 20 21 context.SaveChanges(); 22 } 23 24 using (var context = new ef6recipesEntities()) 25 { 26 Console.WriteLine("Artists and their albums..."); 27 var artists = context.Artists; 28 foreach (var artist in artists) 29 { 30 Console.WriteLine("{0} {1}", artist.FirstName, artist.LastName); 31 foreach (var album in artist.Albums) 32 { 33 Console.WriteLine(" {0}", album.AlbumName); 34 } 35 } 36 Console.WriteLine(" Albums and their artists..."); 37 var albums = context.Albums; 38 foreach (var album in albums) 39 { 40 Console.WriteLine("{0}", album.AlbumName); 41 foreach (var artist in album.Artists) 42 { 43 Console.WriteLine(" {0} {1}", artist.FirstName, artist.LastName); 44 } 45 } 46 }
图4-2
2 有载荷的多对多关系建模
在数据库中,存在通过一张链接表来关联两张表的情况。链接表除了包含连接两张表形成多对多关系的外键外,还包含一些额外的属性,你需要把这两张多对多关系的表导入到实体框架模型中。
2.1 假设我们有如下关系:
一个订单(Order)可以拥有多个订单项(Item),一个订单项(Item)可以属于多个订单(Order),在连接Order、Item实例的关系上有一个Count属性,这个属性被称为一个有效载荷。
2.2 和上节的一样,右键你的项目,使用entityframework的database first创建实体模型。注意把这三张表都选上,当点击“完成”之后,实体框架生成的视图如图4-2-1
图4-2-1
2.3 观察上图可以发现与上节生成的视图有些不同。这次生成的视图好像更符合数据库中标的结构一样。Order与Item之间的多对多关系,被映射成两个一对多的关系,即Order与OrderItem的一对多、Item与OrderItem的一对多。
2.4 对上述关系的插入与查询测试。输出如图4-2-2
1 using (var context = new ef6recipesEntities1()) 2 { 3 var order = new Order 4 { 5 OrderId = 1, 6 OrderDate = new DateTime(2010, 1, 18) 7 }; 8 var item = new Item 9 { 10 SKU = 1729, 11 Description = "Backpack", 12 Price = 29.97 13 }; 14 var oi1 = new OrderItem { Order = order, Item = item, Count = 1 }; 15 item = new Item 16 { 17 SKU = 2929, 18 Description = "Water Filter", 19 Price = 13.97 20 }; 21 var oi2 = new OrderItem { Order = order, Item = item, Count = 3 }; 22 item = new Item 23 { 24 SKU = 1847, 25 Description = "Camp Stove", 26 Price = 43.99 27 }; 28 var oi3 = new OrderItem { Order = order, Item = item, Count = 1 }; 29 context.OrderItems.Add(oi1); 30 context.OrderItems.Add(oi2); 31 context.OrderItems.Add(oi3); 32 context.SaveChanges(); 33 } 34 35 using (var context = new ef6recipesEntities1()) 36 { 37 foreach (var order in context.Orders) 38 { 39 Console.WriteLine("Order # {0}, ordered on {1}", 40 order.OrderId.ToString(), 41 order.OrderDate.ToShortDateString()); 42 Console.WriteLine("SKU Description Qty Price"); 43 Console.WriteLine("--- ----------- --- -----"); 44 foreach (var oi in order.OrderItems) 45 { 46 Console.WriteLine("{0} {1} {2} {3}", oi.Item.SKU, 47 oi.Item.Description, oi.Count.ToString(), 48 oi.Item.Price.ToString("C")); 49 } 50 } 51 }
图4-2-2
最后,虽然ef支持无载荷模式,但是在项目中最好不要使用,因为一旦以后要把无载荷改成有载荷模式,实体关系也要随之改变、程序代码也要随之改变,这样造成很多不必要的麻烦。有一个好的解决办法是,在项目的一开始就使用有载荷模式,即使我们的表在一开始是无载荷,我们也可以给他加一个随机的属性,使之变成有载荷模式。