在數據庫中,表與表之間可能存在多種聯系,比如,一對多,多對多的關系。當我們使用邏輯外鍵在數據庫建立兩張表之間的關系的時候,我們使用EF實體框架
必然也會將這種關系映射到我們的實體關系中來。所以,在我們做項目的時候,很多情況下我們都使用邏輯外鍵建立兩張表之間的關系,從而避免刪除等操作帶來的種
種問題。
那么,我們的EF實體框架是怎么解決這種關聯關系呢?假如給我們兩張表,一張是用戶表,另外一張是訂單表。用戶表與訂單表是一對多的關系,我們來仿照EF
代碼簡單的寫一個自己的框架(CodeOnly)
第一步:建兩個類UserInfo和Order類
下面這時UserInfo類:
1 namespace codeOnly 2 { 3 public class UserInfo 4 { 5 [Key] 6 public int Id { get; set; } 7 public string UName { get; set; } 8 //這里顯示了一對多的關系,一個用戶可以有多個Order表 9 public ICollection<Order> Order { get; set; } 10 } 11 }
下面這是Order類
namespace codeOnly { public class Order { [Key] public int Id { get; set; } public string Content { get; set; } //一個Order對應一個User public UserInfo User { get; set; } } }
這樣便解決了一對多的關系,多對多的實現也是如此,那么我們再來看看它的上下文的實現過程吧
1 namespace codeOnly 2 { 3 public class DbContentDemo:DbContext 4 { 5 //實現了.Net與數據庫的連接 6 public DbContentDemo() 7 : base("name=Demo") 8 { 9 10 } 11 public DbSet<UserInfo> UserInfo { get; set; } 12 public DbSet<Order> Order { get; set; } 13 } 14 }
看到這里,我們是不是就想起了HttpContext這個類呢,簡直是具有異曲同工之處呢。前邊也已經介紹了上下文的作用,這里就不再多說了,主要介紹一下其
中“name=Demo”的作用。在App.config下:Demo就是與數據庫進行連接的字符串。所以它在內部實現了與數據庫進行連接的操作。下面這段代碼就可以實現在數
據庫中創建與我們的實體一一對應的表格了。
1 namespace codeOnly 2 { 3 class Program 4 { 5 public static void Main(string[] args) 6 { 7 DbContentDemo dbcontext = new DbContentDemo(); 8 //初始化數據庫 9 dbcontext.Database.CreateIfNotExists();//如果數據庫不存在則自動創建 10 UserInfo user = new UserInfo(); 11 user.Id = 5; 12 user.UName = "大家好"; 13 dbcontext.SaveChanges(); 14 Console.ReadKey(); 15 } 16 }
上面這些代碼頗為簡單,相信大家都能看明白EF實體框架內部的處理過程是怎樣的了(這是ModelFirst的情況),那么在DbFirst的情況下處理一對多多對多的關系
我們也簡單的用一段小代碼來詮釋吧。
1 namespace ModelFirstaaaa 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //一對多的關系 8 Model1Container dbContext=new Model1Container(); 9 UserInfo user=new UserInfo(); 10 user.ID = 5; 11 user.IsDel = "false"; 12 user.Phone = "13363605745"; 13 user.SubTime=DateTime.Now; 14 user.UName = "大家好"; 15 dbContext.UserInfo集.AddObject(user); 16 17 Order order=new Order(); 18 order.ID = 5; 19 order.Content = "你哈"; 20 order.UserInfo = user; 21 22 dbContext.SaveChanges(); 23 Console.WriteLine("執行成功"); 24 Console.ReadKey(); 25 26 //多對多的關系 27 28 #region 多對多的關系 29 Model1Container dbContext = new Model1Container(); 30 UserInfo user = new UserInfo(); 31 user.ID = 6; 32 user.IsDel = "false"; 33 user.Phone = "13363605745"; 34 user.SubTime = DateTime.Now; 35 user.UName = "me"; 36 dbContext.UserInfo集.AddObject(user); 37 38 Order order = new Order(); 39 order.Content = "你好"; 40 order.ID = 7; 41 dbContext.Order.AddObject(order); 42 43 order.UserInfo.Add(user); 44 dbContext.SaveChanges(); 45 Console.WriteLine("ok"); 46 #endregion 47 } 48 } 49 }
另外,EF還有一個很優美的特定,那就是延遲加載的特性。EF 中第一個延遲加載機制:當我們用到的時候才會去加載數據。每次使用的時候都會全新為我們加
載數據。使用多少次便查詢多少次 。當我們使用Linq表達式寫一個查詢語句的時候,返回的結果基本上都是一個 IQuerable 類型集合,數據庫的sql執行的順序一般為from,
where,select......而我們EF實體框架將程序編譯成sql腳本的時候 先把執行的順序根據算法生成樹的形式(編譯原理),根節點是 from, 先中間,后左邊,然后最右邊。
有可能繼續 而我們 C# 提供的 Expression 類型就為我們提供了樹的結構。我們寫的 Linq 表達式最終只不過是將 Linq 生成了表達式放到了 Expression 表達式類型中去了。放到了
IQueryable 內部中去了。我們用到的 IQueryable 接口的集合的時候, IQuerable 集合內部的 Provider 屬性會解析 Expression, 然后生成相應的sql語句去做相應查詢並加載數據。
1 var data = dbContext.ClassInfo.Where(c => c.ClassName.Contains("計算機")); 2 foreach (var classinfor in data) 3 { 4 Console.WriteLine(classinfor.ClassId+classinfor.ClassName); 5 }
2.第二種延遲加載機制:
導航屬性的延遲加載機制:如果實體是查詢出來的,那么通過導航屬性去訪問其它有關聯的實體的時候 EF 會自動幫我們去查詢關聯表的數據。不管有沒有 ToList,
導航屬性 有個需求:緩存。 IQueryable 是一個非常特殊的接口,數據並不存放在本地,只有用到的時候通過 Provinder 去解析加載數據 ( 它本身是不存儲數據的 ) 。而我們
List , Array(實現了IEnumrable接口,可以對數組進行遍歷) 都是一個本地集合 。Var users=(from u in dbHmEntities select u).ToList();當查詢成功的 時候就相
當於將數據庫中的內容立刻加載到內存中,這時候使用 foreach 遍歷集合的時候就不會去執行 sql 腳本了。 FirstOrDefault 也是立即去查詢。
1 //IQueryable:是非常特殊的接口,數據並不存放在本地,只有用到的時候去解析Exp加載數據。 2 var data2 = dbContext.ClassInfo.Where(c => c.ClassName.Contains("計算機")).ToList(); 3 foreach (var classinfo in data2) 4 { 5 Console.WriteLine(classinfo.ClassName+classinfo.ClassId); 6 }