使用Entity Framework Code First實現一對多和多對多的數據關係表,Entity Framework 簡介和原理 ...
一、 EntirtyFramework(EF)簡介
EntirtyFramework框架是一個輕量級的可擴展版本的流行實體框架數據訪問技術,微軟官方提供的ORM工具讓開發人員節省資料庫訪問的代碼時間,將更多的時間放到業務邏輯層代碼上。EF提供變更跟蹤、唯一性約束、惰性載入、查詢事物等。開發人員使用Linq語言,對資料庫操作如同操作Object對象一樣省事。
EF有三種使用場景,1. 從資料庫生成Class(DB First),2.由實體類生成資料庫表結構(Code First),3. 通過資料庫可視化設計器設計資料庫,同時生成實體類(Model First)。
EF架構如下:
EDM (實體數據模型):EDM包括三個模型,概念模型、 映射和存儲模型。
概念模型 ︰ 概念模型包含模型類和它們之間的關係。獨立於資料庫表的設計。
存儲模型 ︰ 存儲模型是資料庫設計模型,包括表、 視圖、 存儲的過程和他們的關係和鍵。
映射 ︰ 映射包含有關如何將概念模型映射到存儲模型的信息。
LINQ to Entities ︰ LINQ to Entities 是一種用於編寫針對對象模型的查詢的查詢語言。它返回在概念模型中定義的實體。
Entity SQL: Entity SQL 是另一種爐類似於L2E的言語,但相給L2E要複雜的多,所以開發人員不得不單獨學習它。
Object Services(對象服務):是資料庫的訪問入口,負責數據具體化,從客戶端實體數據到資料庫記錄以及從資料庫記錄和實體數據的轉換。
Entity Client Data Provider:主要職責是將L2E或Entity Sql轉換成資料庫可以識別的Sql查詢語句,它使用Ado.net通信向資料庫發送數據可獲取數據。
ADO.Net Data Provider:使用標準的Ado.net與資料庫通信。
二、EntirtyFrameworkCore(EF Core)
Entity Framework Core (EF Core) 是在 2016 年首次發佈的 EF6 的完全重寫。 它附帶於 Nuget 包中,是 Microsoft.EntityFrameworkCore 的主要組成部分。 EF Core 是一種跨平臺產品,可以在 .NET Core 或 .NET Framework 上運行。EF Core 提供了在 EF6 中不會實現的新功能(如備選鍵、批量更新以及 LINQ 查詢中的混合客戶端/資料庫評估。但由於它是一個新代碼庫,所以會缺少一些 EF6 中的功能。
EFCore與之前的EF基本類似,區別在於配置的時候有一些差異;支持DB First和Model First,廣泛使用的Code First模式;也不再支持LazyLoad。
EFCore的資料庫訪問技術使用之前請先安裝Microsoft.EntityFrameworkCore包
三、EF Core使用DB First
資料庫優先(DB First),編碼步驟如下:
1、新建資料庫
2、對模型實施反向工程
Scaffold-DbContext [-Connection] <String> [-Provider] <String> [-OutputDir <String>] [-Context <String>]
[-Schemas <String>] [-Tables <String>] [-DataAnnotations] [ -Force] [-Project <String>]
[-StartupProject <String>] [-Environment <String>] [<CommonParameters>]
3、在項目中配置資料庫連接字元串。
四、EF Core使用Code First
編碼優先(Code First),執行步驟如下:
1、新建模型類
2、新建DBContext
3、在項目中配置資料庫連接字元串
4、執行如下命令(資料庫遷移): Add-Migration [類名], Update-Database
數據表級聯,這裡主要說一下級聯刪除
1、外鍵屬性可為NULL的級聯刪除時外鍵屬性直接設置為 null即可
2、外鍵屬性不可為NULL級聯刪除時會引發異常,需要設置刪除行為 DeleteBehavior ,DeleteBehavior 的類型如下表所示
數據載入是需要重點掌握的,EF的關聯實體載入有三種方式:Lazy Loading,Eager Loading,Explicit Loading,其中Lazy Loading和Explicit Loading都是延遲載入。
(一)Lazy Loading(惰性載入)使用的是動態代理,預設情況下,如果POCO類滿足以下兩個條件,EF就使用Lazy Loading:
POCO類是Public且不為Sealed。導航屬性標記為Virtual。
關閉Lazy Loading,可以將LazyLoadingEnabled設為false,如果導航屬性沒有標記為virtual,Lazy Loading也是不起作用的。
1 /// <summary>
2 /// 用戶信息表
3 /// </summary>
4 public class User
5 {
6 /// <summary>
7 /// 用戶ID
8 /// </summary>
9 //[Key]
10 //[DatabaseGenerated(DatabaseGeneratedOption.Identity)] //設置自增
11 public int UserId { get; set; }
12 /// <summary>
13 /// 用戶編碼
14 /// </summary>
15 public string UserNo { get; set; }
16 /// <summary>
17 /// 用戶名稱
18 /// </summary>
19 public string UserName { get; set; }
20 /// <summary>
21 /// 用戶電話
22 /// </summary>
23 public string TelPhone { get; set; }
24 /// <summary>
25 /// 備註
26 /// </summary>
27 public string Remark { get; set; }
28 public virtual Orginazation Org { get; set; }
29 public virtual ICollection<UserAndRole> Roles { get; set; }
30 }
User實體類
1 /// <summary>
2 /// 角色信息表
3 /// </summary>
4 public class Role
5 {
6 /// <summary>
7 /// 角色ID
8 /// </summary>
9 //[Key]
10 //[DatabaseGenerated(DatabaseGeneratedOption.Identity)] //設置自增
11 public int RoleID { get; set; }
12 /// <summary>
13 /// 角色編碼
14 /// </summary>
15 public string RoleNO { get; set; }
16 /// <summary>
17 /// 角色名稱
18 /// </summary>
19 public string RoleName { get; set; }
20 /// <summary>
21 /// 備註
22 /// </summary>
23 public string Remark { get; set; }
24 public virtual ICollection<UserAndRole> Users { get; set; }
25 }
Role實體類
1 /// <summary>
2 /// 部門信息表
3 /// </summary>
4 public class Orginazation
5 {
6 //private readonly ILazyLoader _lazyLoader;
7
8 //public Orginazation(ILazyLoader lazyLoader)
9 //{
10 // _lazyLoader = lazyLoader;
11 //}
12
13 /// <summary>
14 /// 部門ID
15 /// </summary>
16 [Key]
17 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] //設置自增
18 public int OrgId { get; set; }
19 /// <summary>
20 /// 部門編碼
21 /// </summary>
22 public string OrgNO { get; set; }
23 /// <summary>
24 /// 部門名稱
25 /// </summary>
26 public string OrgName { get; set; }
27 /// <summary>
28 /// 備註
29 /// </summary>
30 public string Remark { get; set; }
31 public virtual ICollection<User> Users { get; set; }
32 }
Orginazation實體類
UserAndRole
在.net Core2.0及以上版本中使用惰性載入需要引用惰性載入代理包,EF Core一般情況下使用惰性載入,因此為了避免報迴圈引用的異常需要設置ReferenceLoopHandling為Ignore
Install-package Microsoft.EntityFrameworkCore.Proxies
Startup.cs文件顯示使用惰性載入
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<CodeFirstContext>(options =>
{
//註入資料庫
options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnStr")).ToString();
//打開延遲載入代理的創建。
options.UseLazyLoadingProxies();
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(options =>
{
//由於使用了惰性載入,因此需要設置此參數避免報循壞引用的異常
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
Startup.cs
1 public class HomeController : Controller
2 {
3 private readonly CodeFirstContext _dbContext;
4 public IConfiguration Configuration { get; }
5 string conn = string.Empty;
6 public HomeController(CodeFirstContext _context)
7 {
8 _dbContext = _context;
9 }
10 public IActionResult Index()
11 {
12 Role role = new Role
13 {
14 RoleNO = "0001",
15 RoleName = "管理員角色",
16 Remark = "所有許可權"
17 };
18 Orginazation org = new Orginazation
19 {
20 OrgNO = "00001",
21 OrgName = "總公司",
22 Remark = "部門根節點"
23 };
24 User user = new User
25 {
26 UserNo = "admin",
27 UserName = "管理員",
28 Remark = "系統管理員",
29 TelPhone="22342432",
30 Org = org
31 };
32 UserAndRole userAndRoleModel = new UserAndRole
33 {
34 Role = role,
35 User = user,
36 RoleID = role.RoleID,
37 UserId = user.UserId
38 };
39 user.Roles.Add(userAndRoleModel);
40 role.Users.Add(userAndRoleModel);
41 _dbContext.Roles.Add(role);
42 _dbContext.Orginazations.Add(org);
43 _dbContext.Users.Add(user);
44 _dbContext.UserAndRoles.Add(userAndRoleModel);
45
46
47 _dbContext.SaveChanges();
48
49 //延遲載入
50 var orgs = _dbContext.Orginazations;
51 foreach (var userModel in orgs.FirstOrDefault().Users)
52 {
53 //刪除部門用戶
54 _dbContext.Users.Remove(userModel);
55 }
56 _dbContext.SaveChanges();
57 return View();
58 }
59
60 public IActionResult About()
61 {
62 _dbContext.Roles.Add(new Role
63 {
64 RoleNO = "role1",
65 RoleName = "RoleName1",
66 Remark = "Remark1"
67 });
68
69 //刪除用戶,對應頭像表裡的數據也會被刪除
70 var user = _dbContext.Users.First();
71 _dbContext.Users.Remove(user);
72 _dbContext.SaveChanges();
73
74 ViewData["Message"] = "Your application description page.";
75
76 return View();
77 }
78
79 public IActionResult Contact()
80 {
81 ViewData["Message"] = "Your contact page.";
82
83 return View();
84 }
85
86 public IActionResult Privacy()
87 {
88 return View();
89 }
90
91 [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
92 public IActionResult Error()
93 {
94 return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
95 }
96 }
HomeController
1 /// <summary>
2 /// Code First編碼上下文
3 /// </summary>
4 public class CodeFirstContext : DbContext
5 {
6 public CodeFirstContext(DbContextOptions<CodeFirstContext> conn)
7 : base(conn)
8 {
9 }
10 /// <summary>
11 /// 映射用戶信息表
12 /// </summary>
13 public virtual DbSet<User> Users { get; set; }
14 /// <summary>
15 /// 映射部門信息表
16 /// </summary>
17 public virtual DbSet<Orginazation> Orginazations { get; set; }
18 /// <summary>
19 /// 映射角色信息表
20 /// </summary>
21 public virtual DbSet<Role> Roles { get; set; }
22 /// <summary>
23 /// 用戶角色信息表
24 /// </summary>
25 public virtual DbSet<UserAndRole> UserAndRoles { get; set; }
26 protected override void OnModelCreating(ModelBuilder modelBuilder)
27 {
28 //用戶
29 modelBuilder.Entity<User>(entity =>
30 {
31 entity.ToTable("User");
32 entity.Property(e => e.UserName).IsRequired().HasMaxLength(64);
33 entity.Property(e => e.UserNo).IsRequired().HasMaxLength(64);
34 entity.Property(e => e.TelPhone).HasMaxLength(64);
35 entity.Property(e => e.Remark).HasMaxLength(256);
36 entity.HasOne(u => u.Org).WithMany(r => r.Users).OnDelete(DeleteBehavior.SetNull);
37
38 });
39
40 //角色
41 modelBuilder.Entity<Role>(entity =>
42 {
43 entity.ToTable("Role");
44 entity.Property(e => e.RoleName).IsRequired().HasMaxLength(64);
45 entity.Property(e => e.RoleNO).IsRequired().HasMaxLength(64);
46 entity.Property(e => e.Remark).HasMaxLength(256);
47 });
48
49 //部門信息
50 modelBuilder.Entity<Orginazation>(entity =>
51 {
52 entity.ToTable("Orginazation");
53 entity.Property(e => e.OrgNO).IsRequired().HasMaxLength(64);
54 entity.Property(e => e.OrgName).IsRequired().HasMaxLength(64);
55 entity.Property(e => e.Remark).HasMaxLength(256);
56 //多個用戶對應一個部門
57 entity.HasMany(u => u.Users).WithOne(r => r.Org).OnDelete(DeleteBehavior.SetNull);
58 });
59
60 //用戶角色信息表
61 modelBuilder.Entity<UserAndRole>(entity =>
62 {
63 entity.ToTable("UserAndRole");
64 entity.HasKey(ua => new { ua.RoleID, ua.UserId });
65 });
66 modelBuilder.Entity<UserAndRole>(entity =>
67 {
68 entity.ToTable("UserAndRole");
69 entity.HasOne(u => u.Role).WithMany(r => r.Users);
70 });
71 modelBuilder.Entity<UserAndRole>(entity =>
72 {
73 entity.ToTable("UserAndRole");
74 entity.HasOne(u => u.User).WithMany(r => r.Roles);
75 });
76 }
77 }
CodeFirstContext資料庫上下文類
執行資料庫遷移命令Add-Migration init22222結果如下:
更新資料庫Update-Database,生成資料庫表結果如下:
至此、Code First主要步驟完成了,通過Code First正確創建了表之間一對多和多對多的關係,本實例中User和Role是多對多的關係,User和Orginazation是多對一的關係,
(二)Eager Loading(預載入)使用Include方法關聯預先載入的實體。在這裡就不舉例說明瞭。
(三)Explicit Loading(直接載入)使用Entry方法,對於集合使用Collection,單個實體則使用Reference。在這裡就不舉例說明瞭。
以上是我對Entity Framework Core的總結和使用,歡迎糾錯!!!