翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html
問題sql
你想在查詢的過濾條件中使用位操做。數據庫
解決方案框架
假設你有一個實體類型,它有一個你想用來作位標識的整型屬性。你將使用這個屬性中的bit位來表示實體中特殊屬性存在與否(譯註:做者想表達的是,bit中位爲0或1時,實體的類型就會不同)。例如,假設你有一個表示當地畫廊的贊助者(patrons)實體,一些贊助者直接捐款(contribute money),一些在畫廊裏當志願者(volunteer),一些服務於董事會(board of directors)。一些贊助者不止提供一種方式來贊助畫廊。一個包含此實體的模型,如圖3-17所示。ide
圖3-17 實體類型Patron,有一個SponsorType屬性,它被用做一個用來指示Patron贊助類型的位標識集合函數
咱們想經過贊助者(patron)提供的贊助類型來過濾查詢。按代碼清單3-34來實現咱們的要求。學習
代碼清單3-34. 在查詢中使用位操做測試
1 static void Main(string[] args) 2 { 3 RunExample(); 4 } 5 6 [Flags] 7 public enum SponsorTypes 8 { 9 None = 0, 10 ContributesMoney = 1, 11 Volunteers = 2, 12 IsABoardMember = 4 13 }; 14 15 static void RunExample() 16 { 17 using (var context = new EFRecipesEntities()) 18 { 19 // 刪除以前的測試數據 20 context.Database.ExecuteSqlCommand("delete from chapter3.patron"); 21 // 添加新的測試數據 22 context.Patrons.Add(new Patron 23 { 24 Name = "Jill Roberts", 25 SponsorType = (int)SponsorTypes.ContributesMoney 26 }); 27 context.Patrons.Add(new Patron 28 { 29 Name = "Ryan Keyes", 30 //注意位操做符中的OR操做符'|'的用法 31 SponsorType = (int)(SponsorTypes.ContributesMoney | 32 SponsorTypes.IsABoardMember) 33 }); 34 context.Patrons.Add(new Patron 35 { 36 Name = "Karen Rosen", 37 SponsorType = (int)SponsorTypes.Volunteers 38 }); 39 context.Patrons.Add(new Patron 40 { 41 Name = "Steven King", 42 SponsorType = (int)(SponsorTypes.ContributesMoney | 43 SponsorTypes.Volunteers) 44 }); 45 context.SaveChanges(); 46 } 47 48 using (var context = new EFRecipesEntities()) 49 { 50 Console.WriteLine("Using LINQ..."); 51 var sponsors = from p in context.Patrons 52 //注意位操做符中的AND操做符'&'的用法 53 where (p.SponsorType & 54 (int)SponsorTypes.ContributesMoney) != 0 55 select p; 56 Console.WriteLine("Patrons who contribute money"); 57 foreach (var sponsor in sponsors) 58 { 59 Console.WriteLine("\t{0}", sponsor.Name); 60 } 61 } 62 63 using (var context = new EFRecipesEntities()) 64 { 65 Console.WriteLine("\nUsing Entity SQL..."); 66 var esql = @"select value p from Patrons as p 67 where BitWiseAnd(p.SponsorType, @type) <> 0"; 68 var sponsors = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Patron>(esql, 69 new ObjectParameter("type", (int)SponsorTypes.ContributesMoney)); 70 Console.WriteLine("Patrons who contribute money"); 71 foreach (var sponsor in sponsors) 72 { 73 Console.WriteLine("\t{0}", sponsor.Name); 74 } 75 } 76 Console.WriteLine("\nPress <enter> to continue..."); 77 Console.ReadLine(); 78 }
代碼清單3-34的輸出以下:ui
Using LINQ...
Patrons who contribute money
Jill Roberts
Ryan Keyes
Steven King
Using Entity SQL...
Patrons who contribute money
Jill Roberts
Ryan Keyes
Steven King
原理spa
在咱們的模型中,實體類型Patron,將多個位標識打包在一個單獨的整形屬性中。一個贊助者(patron)能夠用多種方式贊助(sponsor)畫廊。每種贊助類型用SponsorType屬性中的不一樣的位來表示,咱們能夠建立一個enum類型來表示每種贊助方式。咱們爲每種類型分配2的整數冪做爲它的值。這意味中每一個類型在SponsorType屬性中都有肯定的一個位。(譯註:整型在C#中佔用32位bit,2的二進制表示爲 00000000000000000000000000000010,它在示例中表示 志願者(Volunteers),4的二進制表示爲00000000000000000000000000000100,它在示例中表示 董事會成員(IsABoardMember))。
當插入patrons時,咱們分配贊助類型給SponsorType屬性,對於不止一種方式(類型)贊助畫廊的贊助者,咱們簡單地使用OR操做符(|)將不一樣的方式合併起來。
對於LINQ查詢,咱們使用了AND位操做符(&),從SponsorType屬性值中提取ContributesMoney(捐錢)這種贊助方式的位。若是結果爲非零,那麼這個贊助者就有ContributesMoney標識。若是咱們想查詢不止一種贊助方式(類型)的贊助者,得在咱們使用位操做符AND(&)來提取標識位以前,使用OR來鏈接全部咱們感興趣的SponsorType.
第二種方法演示了,使用Entity SQL的方式。咱們使用函數BitWiseAnd()來提取標識位。Entity SQL支持完整的位操做函數。
問題
你想經過多個屬性來鏈接(join)兩個實體。
解決方案
假設你有一個如圖3-18所示的模型。Account(帳戶)實體類型與Order(訂單)實體類型有一對多關聯。每一個帳戶可能有多個訂單,然而,一個訂單隻能關聯到一個確切的帳戶上。你想查找全部的快遞到與帳號的city,state相同的訂單。
圖3-18 一個包含Account實體類型和與之關聯的Order實體的模型
該示例使用Code-First方法,在代碼清單3-35中,咱們建立了實體類型。
代碼清單3-35. 實體類型Account和Order
public class Account { public Account() { Orders = new HashSet<Order>(); } public int AccountId { get; set; } public string City { get; set; } public string State { get; set; } public virtual ICollection<Order> Orders { get; set; } } public class Order { public int OrderId { get; set; } public Decimal Amount { get; set; } public int AccountId { get; set; } public string ShipCity { get; set; } public string ShipState { get; set; } public virtual Account Account { get; set; } }
接下來,代碼清單3-36中建立了上下文對象,它是用Code-First方法訪問實體框架功能的入口。
代碼清單3-36. 上下文對象
public class EFRecipesEntities : DbContext { public EFRecipesEntities() : base("ConnectionString") {} public DbSet<Order> Orders { get; set; } public DbSet<Account> Accounts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Account>().ToTable("Chapter3.Account"); modelBuilder.Entity<Order>().ToTable("Chapter3.Order"); base.OnModelCreating(modelBuilder); } }
使用代碼清單3-37查找訂單。
代碼清單3-37. 使用多屬性鏈接(Join)來查找全部快遞到與帳號的City和State相同的訂單。
1 using (var context = new EFRecipesEntities()) 2 { 3 //刪除以前的測試數據 4 context.Database.ExecuteSqlCommand("delete from chapter3.[order]"); 5 context.Database.ExecuteSqlCommand("delete from chapter3.account"); 6 //添加新的測試數據 7 var account1 = new Account { City = "Raytown", State = "MO" }; 8 account1.Orders.Add(new Order 9 { 10 Amount = 223.09M, 11 ShipCity = "Raytown", 12 ShipState = "MO" 13 }); 14 account1.Orders.Add(new Order 15 { 16 Amount = 189.32M, 17 ShipCity = "Olathe", 18 ShipState = "KS" 19 }); 20 21 var account2 = new Account { City = "Kansas City", State = "MO" }; 22 account2.Orders.Add(new Order 23 { 24 Amount = 99.29M, 25 ShipCity = "Kansas City", 26 ShipState = "MO" 27 }); 28 29 var account3 = new Account { City = "North Kansas City", State = "MO" }; 30 account3.Orders.Add(new Order 31 { 32 Amount = 102.29M, 33 ShipCity = "Overland Park", 34 ShipState = "KS" 35 }); 36 context.Accounts.Add(account1); 37 context.Accounts.Add(account2); 38 context.Accounts.Add(account3); 39 context.SaveChanges(); 40 } 41 42 using (var context = new EFRecipesEntities()) 43 { 44 var orders = from o in context.Orders 45 join a in context.Accounts on 46 // 使用匿名類型來構造一個複合的查詢表達式 47 new { Id = o.AccountId, City = o.ShipCity, State = o.ShipState } 48 equals 49 new { Id = a.AccountId, City = a.City, State = a.State } 50 select o; 51 52 Console.WriteLine("Orders shipped to the account's city, state..."); 53 foreach (var order in orders) 54 { 55 Console.WriteLine("\tOrder {0} for {1}", order.AccountId.ToString(), 56 order.Amount.ToString()); 57 } 58 } 59 60 Console.WriteLine("\nPress <enter> to continue..."); 61 Console.ReadLine();
代碼清單3-37的輸出以下:
Orders shipped to the account's city, state... Order 31 for $223.09 Order 32 for $99.29
原理
爲了解決這個問題,你能夠先查找出全部的accounts,而後經過比較Orders集合中的每一個訂單,並找出與account的state和city同樣的訂單。對於只有少許account的狀況,這多是一個合理的解決方案。可是,通常狀況下,最好的解決方案是,把這類處理放在存儲層去,由於在存儲層會更有效率。
一開始,Account和Order經過AccountId屬性鏈接在一塊兒,然而,在這個解決方案中,咱們經過在equals從句的兩邊分別建立一個匿名類型明確地造成一個鏈接(Join)。鏈接(Join)實體的屬性數量多於一個時,須要用到匿名構造。 咱們要確保兩邊的匿名類型是相同的,必需要有相同的屬性,相同屬性定義順序。這裏,咱們明確地在數據庫中的兩張表間建立了一個內鏈接(inner-join),意味着,由於鏈接條件,寄往別cities和state的orders將不會包含在結果中。
至此,第三章就到此結束。下一篇咱們將進行第四章的學習。感謝你的閱讀和學習。
實體框架交流QQ羣: 458326058,歡迎有興趣的朋友加入一塊兒交流
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/