《Entity Framework 6 Recipes》中文翻譯系列 (41) ------ 第七章 使用對象服務之標識關係中使用依賴實體與異步查詢保存

翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇 

7-7  標識關係中使用依賴實體

問題html

  你想在標識關係中插入,更新和刪除一個依賴實體。數據庫

解決方案多線程

  假設你有如圖7-8所示的模型。實體LineItem的實體鍵是一個複合鍵。由InvoiceNumber和ItemNumber複合而成。InvoiceNumber同是也是一個外鍵。框架

圖7-8. Invoie和LineItem是一個標識關係,這是由於實體LineItem的複合實體鍵異步

 

  當實體的一個屬性,既是實體鍵又是外鍵時,就能夠說這個實體在一個標識關係中。在咱們的模型裏,實體LineItem的實體鍵,它的標識,同時也是相對於實體Invoice的外鍵。實體LineItem被稱做依賴實體(depaendent entity),Invoice被稱做主實體(principal entity)。async

  實體框架在處理如何刪除標識關係中的依賴實體時,有所不一樣。由於依賴實體不能離開標識關係而存在。簡單地從主實體的集合中刪除依賴實體,在實體框架中的結果是,刪除的依賴實體被標記爲刪除狀態。另外,刪除主實體,會連同依賴實體一直被標記爲刪除。這讓咱們想起數據庫中級聯刪除。固然,實體框架還容許顯式刪除依賴實體。 代碼清單7-5演示了這三種不一樣的場景。ide

代碼清單7-5. 刪除依賴實體學習

 1  public static class Recipe7Program
 2     {
 3         public static void Run()
 4         {
 5             using (var context = new Recipe7Context())
 6             {
 7 
 8                 var invoice1 = new Invoice
 9                                    {
10                                        BilledTo = "Julie Kerns",
11                                        InvoiceDate = DateTime.Parse("9/19/2013")
12                                    };
13                 var invoice2 = new Invoice
14                                    {
15                                        BilledTo = "Jim Stevens",
16                                        InvoiceDate = DateTime.Parse("9/21/2013")
17                                    };
18                 var invoice3 = new Invoice
19                                    {
20                                        BilledTo ="Juanita James",
21                                        InvoiceDate = DateTime.Parse("9/23/2013")
22                                    };
23                 context.LineItems.Add(new LineItem
24                                           {
25                                               Cost = 99.29M,
26                                               Invoice = invoice1
27                                           });
28                 context.LineItems.Add(new LineItem
29                                           {
30                                               Cost = 29.95M,
31                                               Invoice = invoice1
32                                           });
33                 context.LineItems.Add(new LineItem
34                                           {
35                                               Cost = 109.95M,
36                                               Invoice = invoice2
37                                           });
38                 context.LineItems.Add(new LineItem
39                                           {
40                                               Cost = 49.95M,
41                                               Invoice = invoice3
42                                           });
43                 context.SaveChanges();
44 
45                 // 顯示LineItems
46                 Console.WriteLine("Original set of line items...");
47                 DisplayLineItems();
48 
49                 //從invoice1的集合中移除一個LineItem
50                 var item = invoice1.LineItems.ToList().First();
51                 invoice1.LineItems.Remove(item);
52                 context.SaveChanges();
53                 Console.WriteLine("\nAfter removing a line item from an invoice...");
54                 DisplayLineItems();
55 
56                 // 移除 invoice2
57                 context.Invoices.Remove(invoice2);
58                 context.SaveChanges();
59                 Console.WriteLine("\nAfter removing an invoice...");
60                 DisplayLineItems();
61 
62                 //單獨移除一個LineItem
63                 context.LineItems.Remove(invoice1.LineItems.First());
64                 context.SaveChanges();
65                 Console.WriteLine("\nAfter removing a line item...");
66                 DisplayLineItems();
67 
68                 //單獨更新一個LineItem
69                 var item2 = invoice3.LineItems.ToList().First();
70                 item2.Cost = 39.95M;
71                 context.SaveChanges();
72                 Console.WriteLine("\nAfter updating a line item from an invoice …");
73                 DisplayLineItems();
74             }
75         }
76 
77         static void DisplayLineItems()
78         {
79             bool found = false;
80             using (var context = new Recipe7Context())
81             {
82                 foreach (var lineitem in context.LineItems)
83                 {
84                     Console.WriteLine("Line item: Cost {0}", 
85                                        lineitem.Cost.ToString("C"));
86                     found = true;
87                 }
88             }
89             if (!found)
90                 Console.WriteLine("No line items found!");
91         }
92 
93     }

代碼清單7-5的輸出以下:ui

Original set of line items...
Line item: Cost $99.29
Line item: Cost $29.95
Line item: Cost $109.95
Line item: Cost $49.95
After removing a line item from an invoice...
Line item: Cost $29.95
Line item: Cost $109.95
Line item: Cost $49.95
After removing an invoice...
Line item: Cost $29.95
After removing a line item...
Line item: Cost $49.95
After updating a line item...
Line item: Cost $39.95

原理spa

  代碼清單7-5使用三種方法刪除LineItem。第一種從Invoice的集合中刪除。由於一個LineItem依賴Invoice的標識,實體框架標記引用的LineItme對象爲刪除狀態。第二種是刪除invoice對象,實體框架會標記全部的依賴於此對象的lineItme爲刪除狀態。最後一種,是直接調用Remove()方法從上下文對象中的LineItems實體集中刪除最後剩下的一個lineItem對象。

  你能夠修改依賴實體的,除了標識關係中的屬性之外的全部屬性。在咱們的模型中,能夠修改實體LineItem中的Cost屬性。但不能修改Invoice導航屬性。

  當咱們保存一個標識關係中的主實體對象時,它的實體鍵值是從數據庫中產生(由存儲層產生值),並被寫回到主實體對象,以及它的全部依賴實體中的。這得確保全部的操做在數據庫上下文中都是同步進行的。

 

7-8  使用數據庫上下文插入實體

問題

  你想使用數據上下文將模型中實體插入到數據庫中。

解決方案

  假設你有如圖7-9所示的模型。

圖7-9 一個包含實體employee和task的模型

 

  圖7-9中的模型表示員工和他們的任務。你想插入一個新的員工和它的任務到數據庫中。爲了插入一個員工,建立一個Employee的實例並調用上下文對象中Employees實體集中的方法Add()。爲了添加員工的一個任務,建立一個Task的實例,並將它添加到employee對象的Tasks集合中。 你還必須調用Add()方法將employee和task添加到數據庫上下文中。最後,調用SaveChanges()方法,將這些變化持久化到數據庫中。

  代碼清單7-6演示了,使用Add()方法添加新對象到上下文中,並調用SaveChanges()方法持久化到數據庫中。

代碼清單7-6. 插入新實體到數據庫

 1  public static class Recipe8Program
 2     {
 3         public static void Run()
 4         {
 5             using (var context = new Recipe8Context())
 6             {
 7                 var employee1 = new Employee
 8                 {
 9                     EmployeeNumber = 629,
10                     Name = "Robin Rosen",
11                     Salary = 106000M
12                 };
13                 var employee2 = new Employee
14                 {
15                     EmployeeNumber = 147,
16                     Name = "Bill Moore",
17                     Salary = 62500M
18                 };
19                 var task1 = new Task { Description = "Report 3rd Qtr Accounting" };
20                 var task2 = new Task { Description = "Forecast 4th Qtr Sales" };
21                 var task3 = new Task { Description = "Prepare Sales Tax Report" };
22 
23                 //在Employees實體集上使用Add()方法
24                 context.Employees.Add(employee1);
25 
26                 //添加兩個新的task到employee1的Tasks中
27                 employee1.Tasks.Add(task1);
28                 employee1.Tasks.Add(task2);
29 
30                 // 添加一個task到employee2的Tasks中,並使用Add()方法添加task到上下文中
31                 employee2.Tasks.Add(task3);
32                 context.Tasks.Add(task3);
33 
34                 //持久化到數據庫
35                 context.SaveChanges();
36             }
37 
38             using (var context = new Recipe8Context())
39             {
40                 foreach (var employee in context.Employees)
41                 {
42                     Console.WriteLine("Employee: {0}'s Tasks", employee.Name);
43                     foreach (var task in employee.Tasks)
44                     {
45                         Console.WriteLine("\t{0}", task.Description);
46                     }
47                 }
48             }
49             
50         }
51     }

代碼清單7-6的輸出以下:

Employee: Bill Moore's Tasks
    Prepare Sales Tax Repor
Employee: Robin Rosen's Tasks
    Report 3rd Qtr Accounti
    Forecast 4th Qtr Sales

原理

  在代碼清單7-6中,咱們使用實體集Employees和Tasks中的Add()方法,添加實體到數據庫上下文中。

  當你添加一個實體到上下文中,實體框架會爲這個新加入的實體,建立一個臨時實體鍵。 實體框架使用這個臨時的鍵來惟一標識該實體。當實體被持久化到數據庫後,這個臨時的實體鍵,會被一個真正的鍵值給替換。若是添加到數據庫中的兩個實體被分配相同的實體鍵,實體框架會拋出一個異常。這種狀況通常發生在客戶端或者存儲生成過程,給實體分配了相同的鍵值。

  對於外鍵關聯,你能夠使用關聯實體的實體鍵分配給實體的外鍵屬性。雖然涉及臨時鍵值,但實體框架會在實體被保存到數據庫後,正確地修正鍵和關係。

  你還能夠使用Attach()方法來添加一個實體到上下文中。這是一個分爲兩步的過程,首先是調用Attach()方法。它將添加實體到上下文中,可是變化跟蹤器一開始將實體標記爲Unchanged狀態。若是這時調用SaveChanges()方法,它不會將實體保存到數據庫。第二步是,將實體遞給上下文的Entry()方法,得到一個DBEntityEntry實例,並設置它的屬性State爲新的狀態:EntityState.Added。這時調用SaveChanges()方法會將新實體保存到數據庫。

 

7-9  異步查詢和保存

問題

  你想在執行查詢和持久化變動時,保持應用程序的響應性。

解決方案

  假設你有POCO實體,Account和Transaction,你想使用Code-First建模,如代碼清單7-7所示。

 1  public class Account
 2     {
 3         public int AccountNumber { get; set; }
 4         public string AccountHolder { get; set; }
 5 
 6         public virtual ICollection<Transaction> Transactions { get; set; } 
 7     }
 8 
 9     public class Transaction
10     {
11         public int AccountNumber { get; set; }
12         public int TransactionNumber { get; set; }
13         public DateTime TransactionDate { get; set; }
14         public decimal Amount { get; set; }
15     }

  實體Transaction顯然是實體Account的依賴實體,因此咱們經過建立一個EntityTypeConfiguration子類來爲每一個實體配置關係。如代碼清單7-8所示。

代碼清單7-8. 配置實體Account和Transaction

 1  public class AccountTypeConfiguration : EntityTypeConfiguration<Account>
 2     {
 3         public AccountTypeConfiguration()
 4         {
 5             HasKey(a => a.AccountNumber);
 6 
 7             Property(a => a.AccountNumber)
 8                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
 9 
10             HasMany(a => a.Transactions)
11                 .WithRequired();
12         }
13     }
14 
15     public class TransactionTypeConfiguration : EntityTypeConfiguration<Transaction>
16     {
17         public TransactionTypeConfiguration()
18         {
19             HasKey(t => new {t.AccountNumber, t.TransactionNumber});
20 
21             Property(t => t.TransactionNumber)
22                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
23         }
24     }

  最後,在代碼清單7-9中,咱們建立DbContext的子類並重寫OnModelCreating方法,在這個方法中,咱們添加實體配置到模型構建器的配置集合中。

代碼清單7-9. 建立DbContext的子類

 1  public class Recipe9Context : DbContext
 2     {
 3         public DbSet<Account> Accounts { get; set; }
 4         public DbSet<Transaction> Transactions { get; set; }
 5 
 6         public Recipe9Context() : base("name=EF6CodeFirstRecipesContext")
 7         {
 8             
 9         }
10 
11         protected override void OnModelCreating(DbModelBuilder modelBuilder)
12         {
13             base.OnModelCreating(modelBuilder);
14 
15             modelBuilder.Configurations.Add(new AccountTypeConfiguration());
16             modelBuilder.Configurations.Add(new TransactionTypeConfiguration());
17         }
18     }

  爲了異步查詢和保存,咱們將分別使用LINQ to Entities方法ForEachAsync(),和上下文DbContext的方法SaveChangesAsync()。代碼清單7-10演示了這兩個方法的用法。

代碼清單7-10. 異步查詢和保存

  1  public static class Recipe9Program
  2     {
  3         public static async Task Run()
  4         {
  5             using (var context = new Recipe9Context())
  6             {
  7                 var account1 = new Account
  8                 {
  9                     AccountHolder = "Robert Dewey",
 10                     Transactions = new HashSet<Transaction>()
 11                                                           {
 12                                                               new Transaction
 13                                                                   {
 14                                                                       TransactionDate = Convert.ToDateTime("07/05/2013"),
 15                                                                       Amount = 104.00M
 16                                                                   },
 17                                                              new Transaction
 18                                                                  {
 19                                                                      TransactionDate = Convert.ToDateTime("07/12/2013"),
 20                                                                      Amount = 104.00M
 21                                                                  },
 22                                                              new Transaction
 23                                                                  {
 24                                                                      TransactionDate = Convert.ToDateTime("07/19/2013"),
 25                                                                      Amount = 104.00M
 26                                                                  }
 27                                                           }
 28                 };
 29                 var account2 = new Account
 30                 {
 31                     AccountHolder = "James Cheatham",
 32                     Transactions = new List<Transaction>
 33                                                           {
 34                                                               new Transaction
 35                                                                   {
 36                                                                       TransactionDate = Convert.ToDateTime("08/01/2013"),
 37                                                                       Amount = 900.00M
 38                                                                   },
 39                                                               new Transaction
 40                                                                   {
 41                                                                       TransactionDate = Convert.ToDateTime("08/02/2013"),
 42                                                                       Amount = -42.00M
 43                                                                   }
 44                                                           }
 45                 };
 46                 var account3 = new Account
 47                 {
 48                     AccountHolder = "Thurston Howe",
 49                     Transactions = new List<Transaction>
 50                                                           {
 51                                                               new Transaction
 52                                                                   {
 53                                                                       TransactionDate = Convert.ToDateTime("08/05/2013"),
 54                                                                       Amount = 100.00M
 55                                                                   }
 56                                                           }
 57                 };
 58 
 59                 context.Accounts.Add(account1);
 60                 context.Accounts.Add(account2);
 61                 context.Accounts.Add(account3);
 62                 context.SaveChanges();
 63 
 64                 //爲每一個account添加每個月的服務費
 65                 foreach (var account in context.Accounts)
 66                 {
 67                     var transactions = new List<Transaction>
 68                                        {
 69                                            new Transaction
 70                                                {
 71                                                    TransactionDate = Convert.ToDateTime("08/09/2013"),
 72                                                    Amount = -5.00M
 73                                                },
 74                                            new Transaction
 75                                                {
 76                                                    TransactionDate = Convert.ToDateTime("08/09/2013"),
 77                                                    Amount = -2.00M
 78                                                }
 79                                        };
 80 
 81                     Task saveTask = SaveAccountTransactionsAsync(account.AccountNumber, transactions);
 82 
 83                     Console.WriteLine("Account Transactions for the account belonging to {0} (acct# {1})", account.AccountHolder, account.AccountNumber);
 84                     await saveTask;
 85                     await ShowAccountTransactionsAsync(account.AccountNumber);
 86                 }
 87 
 88 
 89             }
 90 
 91         }
 92 
 93         private static async Task SaveAccountTransactionsAsync(int accountNumber, ICollection<Transaction> transactions)
 94         {
 95             using (var context = new Recipe9Context())
 96             {
 97                 var account = new Account { AccountNumber = accountNumber };
 98                 context.Accounts.Attach(account);
 99                 context.Entry(account).Collection(a => a.Transactions).Load();
100 
101                 foreach (var transaction in transactions.OrderBy(t => t.TransactionDate))
102                 {
103                     account.Transactions.Add(transaction);
104                 }
105 
106                 await context.SaveChangesAsync();
107 
108             }
109 
110         }
111 
112         private static async Task ShowAccountTransactionsAsync(int accountNumber)
113         {
114             Console.WriteLine("TxNumber\tDate\tAmount");
115             using (var context = new Recipe9Context())
116             {
117                 var transactions = context.Transactions.Where(t => t.AccountNumber == accountNumber);
118                 await transactions.ForEachAsync(t => Console.WriteLine("{0}\t{1}\t{2}", t.TransactionNumber, t.TransactionDate, t.Amount));
119             }
120         }
121     }

原理

  示例中使用的異步結構是.NET4.5引入的,用來下降日常編寫異步代碼的複雜性。當咱們調用SaveAccountTransactionsAsync()方法時,咱們將它分配給Task對象。它調用方法並向調用者返回執行權。同時,異步部分,SaveAccounTransactionsAsync()也在執行。調用ShowAccountTransactionsAsync()方法的代碼與調用SaveAccountTransactionsAsync()方法相似。當等待兩個方法的調用返回時,執行權返回到await語句的下一行代碼。

  重要的是,要知道.NET4.5中的異步模型是單線程,不是多線程,因此,代碼 await SaveAccountTransactionsAsync()會被掛起,直到這個方法返回。另外須要知道的是 ,任何方法在調用一個async方法時,它本身必須被async修改符標記,而且返回一個Task類型或者Task<T>類型。

  代碼清單7-10的輸出以下:

Account Transactions for the account belonging to Robert Dewey (acct# 1)
TxNumber Date Amount
1 7/5/2013 12:00:00 AM 104.00
2 7/12/2013 12:00:00 AM 104.00
3 7/19/2013 12:00:00 AM 104.00
7 8/9/2013 12:00:00 AM -5.00
8 8/9/2013 12:00:00 AM -2.00
Account Transactions for the account belonging to James Cheatham (acct# 2)
TxNumber Date Amount
4 8/1/2013 12:00:00 AM 900.00
5 8/2/2013 12:00:00 AM -42.00
9 8/9/2013 12:00:00 AM -5.00
10 8/9/2013 12:00:00 AM -2.00
Account Transactions for the account belonging to Thurston Howe (acct# 3)
TxNumber Date Amount
6 8/5/2013 12:00:00 AM 100.00
11 8/9/2013 12:00:00 AM -5.00
12 8/9/2013 12:00:00 AM -2.00

至此,第七章結束,下篇咱們開始第八章。感謝你的閱讀。


 

 

 

實體框架交流QQ羣:  458326058,歡迎有興趣的朋友加入一塊兒交流

謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/

相關文章
相關標籤/搜索