[C#/.NET]Entity Framework(EF) Code First 多對多關系的實體增,刪,改,查操作全程詳細示例
本文我們來學習一下在Entity Framework中使用Context刪除多對多關系的實體是如何來實現的。我們將以一個具體的控制台小實例來了解和學習整個實現Entity Framework 多對多關系的實體刪除的操作過程。
你將學習到
-
怎樣創建一個引用Entity Framework的項目;
-
怎樣配置Entity Framework的數據庫連接;
-
怎樣去掉Entity Framework Code First 生成的表名的復數;
-
怎樣通過EntityTypeConfiguartion<T>配置實體的Fluent API ;
-
怎樣配置Entity Framework的實體多對多的關系映射;
-
Entity Framework數據初始化;
-
怎樣使用包管理工具控制台來生成和更新數據庫;
-
怎么刪除Entity Framework中的多對多關系的數據。
本示例開發環境
-
操作系統:Windows 10
-
開發工具及版本:Visual Studio 2015 Update 1
-
.NET Framework版本:.NET Framework 4.6
-
程序輸出方式:控制台應用程序
第一步、創建項目並引用程序包
1.1 創建項目
首先,我們創建一個控制台應用程序,取名為:EFRemoveManyToManyDemo,如下圖:

1.2 引用程序包
接着打開程序包管理工具,安裝必須的EntityFramework引用包,如下:

第二步、創建實體類並配置數據庫連接
2.1 創建實體類
安裝好Entity Framework包之后 ,我們先創建本示例需要的兩個實體對應的類:User和Role(都放在Model的文件夾下),如下:
User.cs
1 using System; 2 using System.Collections.Generic; 3 4 namespace EFRemoveManyToManyDemo.Model 5 { 6 public class User 7 { 8 public User() 9 { 10 Roles = new HashSet<Role>(); 11 } 12 public int Id { get; set; } 13 public string FirstName { get; set; } 14 public string LastName { get; set; } 15 public DateTime? CreatedOn { get; set; } 16 public virtual ICollection<Role> Roles { get; set; }//多對多關系中兩邊都要寫上ICollection 17 } 18 }
Role.cs
1 using System.Collections.Generic; 2 3 namespace EFRemoveManyToManyDemo.Model 4 { 5 public class Role 6 { 7 public Role() 8 { 9 this.Users = new HashSet<User>();!!依賴注入中的構造器注入 10 } 11 public int Id { get; set; } 12 public string Name { get; set; } 13 14 public virtual ICollection<User> Users { get; set; } 15 //多對多關系中兩邊都要寫上ICollection 16 } 17 }
2.2 配置Fluent API
為了配置Fluent API,新建一個Mapping文件夾,再分別創建User的配置文件UserConfigurationMapping和Role的配置文件RoleConfigurationMapping,如下:
UserConfiguration.cs
using EFRemoveManyToManyDemo.Model; using System.Data.Entity.ModelConfiguration; namespace EFRemoveManyToManyDemo.Mapping { public class UserConfigurationMapping : EntityTypeConfiguration<User> { public UserConfigurationMapping() { Property(x => x.FirstName).HasMaxLength(50).IsRequired(); Property(x => x.LastName).HasMaxLength(50).IsRequired(); } } }
RoleConfigurationMapping.cs
1 using EFRemoveManyToManyDemo.Model; 2 using System.Data.Entity.ModelConfiguration; 3 4 namespace EFRemoveManyToManyDemo.Mapping 5 { 6 public class RoleConfigurationMapping : EntityTypeConfiguration<Role> 7 { 8 public RoleConfigurationMapping() 9 { 10 HasKey(x => x.Id); 11 Property(x => x.Name).HasMaxLength(50).IsRequired(); 12 HasMany(x => x.Users) 13 .WithMany(x => x.Roles) 14 .Map(m => 15 { 16 m.MapLeftKey("RoleId"); 17 m.MapRightKey("UserId"); 18 m.ToTable("LNK_User_Role"); 19 }); 20 } 21 } 22 }
2.3 創建Context類
接下來,我們再創建一個名為:ManyToManyRemoveContext的類,該類繼承至DbContext類,用於管理數據庫的連接上下文和數據庫初始化等的一些配置和操作,如下:
using EFRemoveManyToManyDemo.Mapping; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; namespace EFRemoveManyToManyDemo { public class ManyToManyRemoveContext : DbContext { public ManyToManyRemoveContext() : base("ManyToManyRemoveContext") { } } }
2.4 配置連接字符串
再在App.config配置文件中添加本地的數據庫連接字符串,大致如下(具體的請根據你的實際數據連接參數來):
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /> </startup> <connectionStrings> <add name="ManyToManyRemoveContext" connectionString="server=你的數據庫服務器地址;database=ManyToManyRemoveDemo;uid=你的數據庫登錄名;pwd=密碼" providerName="System.Data.SqlClient"/> </connectionStrings> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>
2.5 重寫Context
為了將我們剛才寫的Fluent API應用到對應的實體上,所以我們需要重寫(override)DbContext的OnModelCreating方法,如下:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Configurations.Add(new UserConfigurationMapping()); modelBuilder.Configurations.Add(new RoleConfigurationMapping()); }
其中
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
是將Entity Framework Code First在實體類生成對應表時去掉表名的復數用的。簡單地說就是,默認情況下,Entity Framework Code First在由實體類生成對應表時的表名是復數形式的,比如本例的User和Role類,如果沒有這句配置,在生成表名的時候將會是Users和Roles這兩個表名,反之,則是User和Role這兩個表名。
好了,下面貼出完整的ManyToManyRemoveContext.cs文件的代碼:
1 using EFRemoveManyToManyDemo.Mapping; 2 using EFRemoveManyToManyDemo.Model; 3 using System.Data.Entity; 4 using System.Data.Entity.ModelConfiguration.Conventions; 5 6 namespace EFRemoveManyToManyDemo 7 { 8 public class ManyToManyRemoveContext : DbContext 9 { 10 public ManyToManyRemoveContext() : base("ManyToManyRemoveContext") 11 { 12 13 } 14 15 protected override void OnModelCreating(DbModelBuilder modelBuilder) 16 { 17 base.OnModelCreating(modelBuilder); 18 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 19 20 modelBuilder.Configurations.Add(new UserConfigurationMapping()); 21 modelBuilder.Configurations.Add(new RoleConfigurationMapping()); 22 } 23 24 public DbSet<User> Users { get; set; } 25 public DbSet<Role> Roles { get; set; } 26 } 27 }
本文寫到這里,關於Entity Framework的引用,實體類的聲明和Fluent API配置以及與數據庫連接等操作都已完成了。接下來我們要做的是利用Entity Framework所實體生成到配置好的數據庫中。
第三步、應用Migration生成數據庫
在接下來的過程中,我們會用到包管理控制台(Package Manager Console)和三個命令:
3.1 Enable-Migrations
命令使用方式如下圖:

運行以上命令后,Entity Framework會自動在我們的項目中創建一個名為Migrations的文件夾,同時生成一個Configuartion.cs的配置文件。這時的項目結構大致是這樣的:

生成好Configuration.cs的文件我們再作數據的初始化,如下:
namespace EFRemoveManyToManyDemo.Migrations { using Model; using System; using System.Collections.Generic; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<ManyToManyRemoveContext> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(ManyToManyRemoveContext context) { var roles = new List<Role> { new Role{ Id=1,Name="超級管理員" }, new Role{ Id=2,Name="管理員" }, new Role{ Id=3,Name="一般用戶" } }; var users = new List<User> { new User {Id=1,FirstName="Kobe",LastName="Bryant",CreatedOn=DateTime.Now,Roles=roles }, new User {Id=2,FirstName="Chris",LastName="Paul",CreatedOn=DateTime.Now,Roles=roles.Where(x=>x.Id==2).ToList() }, new User {Id=3,FirstName="Jerimy",LastName="Lin",CreatedOn=DateTime.Now,Roles=roles.Take(2).ToList() } }; } } }
完成第一個命令和數據初始化配置后,我們進行第二個命令。
3.2 Add-Migration Init -Verbose
執行此命令后,會在Migrations的文件夾中自動生成一個形如:時間戳_Init.cs的數據遷移文件,如本例生成的是201512040507219_Init.cs這樣一個文件名,其中Init是我們指定的本次數據遷移的版本名稱,文件中的內容如下:
1 namespace EFRemoveManyToManyDemo.Migrations 2 { 3 using System; 4 using System.Data.Entity.Migrations; 5 6 public partial class Init : DbMigration 7 { 8 public override void Up() 9 { 10 CreateTable( 11 "dbo.Role", 12 c => new 13 { 14 Id = c.Int(nullable: false, identity: true), 15 Name = c.String(nullable: false, maxLength: 50), 16 }) 17 .PrimaryKey(t => t.Id); 18 19 CreateTable( 20 "dbo.User", 21 c => new 22 { 23 Id = c.Int(nullable: false, identity: true), 24 FirstName = c.String(nullable: false, maxLength: 50), 25 LastName = c.String(nullable: false, maxLength: 50), 26 CreatedOn = c.DateTime(), 27 }) 28 .PrimaryKey(t => t.Id); 29 30 CreateTable( 31 "dbo.LNK_User_Role", 32 c => new 33 { 34 RoleId = c.Int(nullable: false), 35 UserId = c.Int(nullable: false), 36 }) 37 .PrimaryKey(t => new { t.RoleId, t.UserId }) 38 .ForeignKey("dbo.Role", t => t.RoleId, cascadeDelete: true) 39 .ForeignKey("dbo.User", t => t.UserId, cascadeDelete: true) 40 .Index(t => t.RoleId) 41 .Index(t => t.UserId); 42 43 } 44 45 public override void Down() 46 { 47 DropForeignKey("dbo.LNK_User_Role", "UserId", "dbo.User"); 48 DropForeignKey("dbo.LNK_User_Role", "RoleId", "dbo.Role"); 49 DropIndex("dbo.LNK_User_Role", new[] { "UserId" }); 50 DropIndex("dbo.LNK_User_Role", new[] { "RoleId" }); 51 DropTable("dbo.LNK_User_Role"); 52 DropTable("dbo.User"); 53 DropTable("dbo.Role"); 54 } 55 } 56 }
我們可以通過這個文件中的內容看到,有Up()和Down()這兩個方法,Up()方法要執行的其實就是本次數據遷移要對數據進行的操作,而Down()方法則是在以后我們如果要退回到此版本應該執行的操作。
經過以上兩個命令,如你迫不及待地要去數據庫管理工具中查看有一個名叫:ManyToManyRemoveDemo的數據庫是否已生成,那么很遺憾地告訴你,還沒有。這時,我們還得執行最后一個命令來生成數據庫和實體對應的表。
3.3 Update-Database -Verbose
執行以上命令,我們這時再打開數據庫管理工具。沒錯ManyToManyRemoveDemo就在那里。再查看表是否成功生成呢,再檢查一下表中是否有我們初始化的數據呢,沒錯,這些都是可以有的。怎么樣,驚喜吧,歡呼吧,我們做到了!!!

但還沒完,請先回復平靜,這還只是一個開始。Entity Framework還可以做得更多,我們需要學習的也還有很多,編程的道路從來就不是一步到位的,得有個過程。一步一步往下看吧。
第四步、增、刪、改、查操作
4.1 查詢數據示例
打開我們項目的Program.cs文件。首先,我們來查詢(Query)一下數據庫中的數據,如下:
1 static void Main(string[] args) 2 { 3 Query(); 4 ReadKey(); 5 } 6 7 static void Query() 8 { 9 using (var cxt = new ManyToManyRemoveContext()) 10 { 11 var users = cxt.Users.ToList(); 12 users.ForEach(x => 13 { 14 WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name))); 15 }); 16 } 17 }
運行結果如圖:

4.2 更新數據示例
再來更新一條數據庫中的數據怎么樣,如下:
1 static void Main(string[] args) 2 { 3 Update(); 4 Query(); 5 ReadKey(); 6 } 7 8 static void Query() 9 { 10 using (var cxt = new ManyToManyRemoveContext()) 11 { 12 var users = cxt.Users.ToList(); 13 users.ForEach(x => 14 { 15 WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name))); 16 }); 17 } 18 } 19 20 static void Update() 21 { 22 using (var cxt = new ManyToManyRemoveContext()) 23 { 24 var user = cxt.Users.FirstOrDefault(x=>x.Id==3); 25 user.FirstName = "ShuHao"; 26 cxt.SaveChanges(); 27 } 28 }
運行結果如我們所料,如圖:

4.3 刪除數據示例
Id為3的User的FirstName已經從數據庫更新了。同樣的,我們要完成刪除操作也比較簡,如下:
1 static void Remove() 2 { 3 using (var cxt = new ManyToManyRemoveContext()) 4 { 5 var user = cxt.Users.FirstOrDefault(x=>x.Id==2); 6 cxt.Users.Remove(user); 7 cxt.SaveChanges(); 8 } 9 }
4.4 新增數據示例
就不再貼圖了。最后是添加操作,向User表添加一個用戶並分配一個Id為1的角色,代碼如下:
1 static void Add() 2 { 3 List<Role> roles; 4 using (var cxt = new ManyToManyRemoveContext()) 5 { 6 roles = cxt.Roles.ToList(); 7 cxt.Users.Add(new User 8 { 9 Id = 4, 10 FirstName = "Console", 11 LastName = "App", 12 CreatedOn = DateTime.Now, 13 Roles = roles.Where(x => x.Id == 1).ToList() 14 }); 15 } 16 }
4.5 刪除多對多數據的示例
好了,以上是對User(用戶實體)進行簡單的增、刪、改、查的操作,那么我們要實現多對多的刪除操作呢?也就是刪除用戶的同時刪除其對應的角色,實現的代碼如下:
1 static void RemoveManyToMany() 2 { 3 using (var cxt = new ManyToManyRemoveContext()) 4 { 5 var user = cxt.Users.FirstOrDefault(x => x.Id == 1); 6 var roles = new List<Role>(); 7 roles.AddRange(user.Roles.Select(x => x)); 8 foreach (var role in roles) 9 { 10 user.Roles.Remove(role); 11 } 12 cxt.Users.Remove(user); 13 cxt.SaveChanges(); 14 } 15 }
運行結果如圖:

完整示例代碼及下載地址
好了,最后把Program.cs這個測試文件貼上來,供參考:
1 using EFRemoveManyToManyDemo.Model; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using static System.Console; 6 7 namespace EFRemoveManyToManyDemo 8 { 9 public class Program 10 { 11 static void Main(string[] args) 12 { 13 //Update(); 14 WriteLine("Before many to many removed"); 15 Query(); 16 RemoveManyToMany(); 17 WriteLine("After many to many removed"); 18 Query(); 19 ReadKey(); 20 } 21 22 static void Query() 23 { 24 using (var cxt = new ManyToManyRemoveContext()) 25 { 26 var users = cxt.Users.ToList(); 27 users.ForEach(x => 28 { 29 WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name))); 30 }); 31 } 32 } 33 34 static void Add() 35 { 36 List<Role> roles; 37 using (var cxt = new ManyToManyRemoveContext()) 38 { 39 roles = cxt.Roles.ToList(); 40 cxt.Users.Add(new User 41 { 42 Id = 4, 43 FirstName = "Console", 44 LastName = "App", 45 CreatedOn = DateTime.Now, 46 Roles = roles.Where(x => x.Id == 1).ToList() 47 }); 48 } 49 } 50 51 static void Update() 52 { 53 using (var cxt = new ManyToManyRemoveContext()) 54 { 55 var user = cxt.Users.FirstOrDefault(x => x.Id == 3); 56 user.FirstName = "ShuHao"; 57 cxt.SaveChanges(); 58 } 59 } 60 61 static void Remove() 62 { 63 using (var cxt = new ManyToManyRemoveContext()) 64 { 65 var user = cxt.Users.FirstOrDefault(x => x.Id == 2); 66 cxt.Users.Remove(user); 67 cxt.SaveChanges(); 68 } 69 } 70 71 static void RemoveManyToMany() 72 { 73 using (var cxt = new ManyToManyRemoveContext()) 74 { 75 var user = cxt.Users.FirstOrDefault(x => x.Id == 1); 76 var roles = new List<Role>(); 77 roles.AddRange(user.Roles.Select(x => x)); 78 foreach (var role in roles) 79 { 80 user.Roles.Remove(role); 81 } 82 cxt.Users.Remove(user); 83 cxt.SaveChanges(); 84 } 85 } 86 } 87 }
