c#教程之通過數據綁定修改數據


通過數據綁定修改數據
 
“實體框架”提供了與數據庫的雙向通信通道。前面已經講述了如何使用“實體框架”獲 取數據,現在來看看如何修改獲取的信息,並將改動發送回數據庫。
 
 
26.2.1 更新現有數據
 
使用一個 ObjectContext 對象獲取數據時,根據數據創建的對象位於應用程序的內存緩存中。 為了更改緩存中的對象的值,采取的方式和修改任何普通對象中的值一樣——設置它們的 屬性。然而,更新內存中的對象不會更新數據庫。要在數據庫中反映出這個改動(換言之, 將改動“持久化”到數據庫中),需要生成恰當的 SQL  UPDATE 命令,並指示數據庫服務器 執行這些命令。使用“實體框架”可以輕松做到這一點。以下代碼展示了一個 LINQ to Entities 查詢,它獲取 ID 為 14 的產品,將它的名稱更改為"Bean  Curd"(產品 14 最初在 Northwind 數據庫中命名為"Tofu"),再將更改發送回數據庫。
 
NorthwindEntities northwindContext = new NorthwindEntities();
Product product = northwindContext.Products.Single(p => p.ProductID == 14);
product.ProductName = "Bean Curd";
northwindContext.SaveChanges();
 
在上述代碼中,關鍵語句是對 ObjectContext  對象的 SaveChanges  方法的調用。(記住, NorthwindEntities 派生自 ObjectContext。)對通過運行查詢來填充的一個實體對象中的信息 進行修改時,對連接(原始的查詢就是在這個連接上運行的)進行管理的 ObjectContext 對象 會跟蹤數據發生的改變。SaveChanges 方法會將這些改變傳回數據庫。在幕后,ObjectContext 對象會構建並運行一個 SQL UPDATE 語句。
 
如果獲取並修改了幾個產品,只需在最后一次修改之后調用一次 SaveChanges 就可以了。 SaveChanges  方法一次性提交所有更新。ObjectContext  對象創建一個數據庫事務處理 (database transaction),並在這個事務處理中執行所有 SQL UPDATE 語句。任何一個更新失敗, 都 會 取 消 事 務 處 理 —— SaveChanges  方 法 在 數 據 庫 中 進 行 的 所 有 更 改 都 會 回 滾 , SaveChanges 方法最后將拋出一個異常。如果所有更新成功,事務處理就生效,所有更改都 在數據庫中變成永久性的更改。注意,假如 SaveChanges 方法失敗,只有數據庫才會回滾,

 

 
 
對內存中的實體對象進行的更改仍然存在。SaveChanges 失敗時拋出的異常會提供與失敗原
因有關的一些信息。可以根據這些信息來修正錯誤,並再次調用 SaveChanges。
 
ObjectContext 類還提供了 Refresh 方法。使用這個方法,可以利用數據庫中的數據來重新填 充緩存中的 EntityObject 集合,並放棄進行的任何更改。要像下面這樣使用該方法:
 
northwindContext.Refresh(RefreshMode.StoreWins, northwindContext.Products);
 
第 一 個 參 數 是 System.Data.Objects.RefreshMode  枚 舉 的 一 個 成 員 , 如 果 指 定 的 值 是 RefreshMode. StoreWins,就會強迫用數據庫中的數據來進行刷新。第二個參數是緩存中要 要刷新的實體。
 
提示 對更改進行跟蹤是 ObjectContext 對象執行的一個代價不菲的操作。如果確定以 后 不 需 要 修 改 數 據 ( 例 如 , 假 定 程 序 生 成 的 是 一 份 只 讀 的 報 表 ) , 就 可 以 將 MergeOption 屬性設為 MergeOption.NoTracking,從而禁止對 EntityObject 的更改 進行跟蹤。如下所示:
 
 
northwindContext.Suppliers.MergeOption = MergeOption.NoTracking;
 
可對一個禁止了‚更改跟蹤‛的實體進行更改,但在調用 SaveChanges 時,這些更改不會 保存。應用程序退出時,它們會丟失。
 
 
26.2.2   處理沖突的更新
 
 
一個更新操作可能出於多種原因而失敗,其中最常見的原因就是發生了沖突。兩個用戶試 圖同時更新相同的數據,就會發生沖突。仔細思考一下在應用程序中使用“實體框架”的 后果,就會發現其實有許多地方都可能發生沖突。通過一個 ObjectContext 對象獲取數據時, 這些數據被緩存應用程序的內存中。與此同時,另一個用戶執行相同的查詢,並獲取相同 的數據。如果兩個人都修改了數據,而且都調用了 SaveChanges 方法,那么在數據庫中, 一個人就會覆蓋另一個人的修改。這種現象稱為 lost update。
 
之所以發生這種現象,是由於“實體框架”實現了“開發式並發”(optimistic concurrency)。 換言之,當它從數據庫獲取數據時,不會在數據庫中鎖定那些數據。這種形式的並發性允 許其他用戶同時訪問相同的數據,但它假定兩個用戶同時更改相同數據的機率非常小(這 正是“開發式並發”一詞的來歷59)。
 
與開放式並發相反的是“封閉式並發”(pessimistic concurrency)。在這個方案中,所有數據 都在獲取時鎖定,其他用戶不能同時訪問它們。這樣一來,就保證了不會丟失任何更改。 但是,這個方案過於極端。
 
 
59  “開放式並發”原文是“optimistic concurrency”,這里只是按照 Microsoft 的文檔把它翻譯為“開放式並發”。其實,更貼 切的翻譯是“樂觀並發”。對應地,“封閉式並發”(pessimistic concurrency)更貼切的翻譯是“悲觀並發”。——譯者注

 

 
 
“實體框架”不直接支持封閉式並發。相反,它提供了一個折衷方案。EntityObject 類中的
每一項都有一個名為“並發模式”的屬性,它默認設為 None,但可使用“實體框架”設計 器把它更改為 Fixed。下圖展示了早先構建的實體模型。
 

 
單擊 Product 實體中的 ProductName 這一項,然后在屬性窗口中,將它的“並發模式”屬 性更改為 Fixed。
 
應用程序修改 Products EntityObject 類的一個實例的 ProductName 屬性的值時,“實體框架” 會在緩存中保留這個屬性的原始值的一個副本。設置了一個屬性的“並發模式”后,當應 用程序調用 ObjectContext 對象的 SaveChanges 方法時,“實體框架”會檢查緩存的原始值副 本,驗證數據庫中對應行的那一列自從取回之后沒有被另一個用戶更改。如果列沒有更改, 行 就 會 更 新 。 如 果 列 已 經 更 改 , SaveChanges   方 法 會 停 止 , 並 拋 出 一 個 OptimisticConcurrencyException 異常。一旦發生這個情況,SaveChanges 在數據庫中進行的 所有更改都會被撤消(undone)。不過,在應用程序的緩存中,這些更改仍然會被保留下來。
 
引發一個 OptimisticConcurrencyException 異常時,可以檢查異常對象的 StateEntries 屬性, 判 斷 是 哪 個 實 體 造 成 了 沖 突 。 這 個 屬 性 容 納 着 一 個 ObjectStateEntry  對 象 集 合 。 ObjectStateEntry 類本身包含大量屬性。其中最重要的屬性包括:Entity 屬性,它包含對造

 

 
 
成 沖 突 的 實 體 的 一 個 引 用 ; CurrentValues  屬性, 它 包 含 實 體 修 改 過 的 數 據 ; 以 及
OriginalValues 屬性,它包含最初從數據庫獲取的實體數據。
 
為了解決沖突,推薦的方案是使用 Refresh 方法從數據庫中獲取最新的數據,並用這些數據 重新加載緩存。然后,再次調用 SaveChanges。Refresh 方法會用數據庫中的最新的值重新 填充一個指定實體(作為第二個參數傳遞)。但是,如果用戶已經進行了大量更改,你可能 不想強迫用戶重新輸入它們。幸好,Refresh 方法的 RefreshMode 參數也允許你應對這種情 況。RefreshMode 是一個枚舉類型的參數,它定義了兩個值:
 
l    StoreWins     實體中已被更改的值被數據庫中的最新值覆蓋。用戶對實體的任何更改都 會丟失。
 
l    ClientWins    實體中已被更改的值不被數據庫中的值覆蓋。用戶對實體的任何更改都會 保留在緩存中,會在下次調用 SaveChanges 時傳送回數據庫。
 
在下面的代碼中,嘗試對 Products ObjectSet 中的 ProductID 為 14 的產品的名稱進行修改, 然 后 將 這 個 更 改 保 存 到 數 據 庫 。 如 果 另 一 個 用 戶 已 經 修 改 了 相 同 的 數 據 , OptimisticConcurrencyException 處理程序會刷新緩存中的原始值,但修改過的數據會在緩存 中保留修改過的樣子,然后再次調用 SaveChanges。
 
NorthwindEntities northwindContext = new NorthwindEntities();
try
{
Product product = northwindContext.Products.Single(p => p.ProductID == 14);
product.ProductName = "Bean Curd";
northwindContext.SaveChanges();
}
catch (OptimisticConcurrencyException ex)
{
northwindContext.Refresh(RefreshMode.ClientWins, northwindContext.Products);
northwindContext.SaveChanges();
}
 
 
 
重要提示:
 
“實體框架”在檢測到第一個沖突時,會停止並拋出 OptimisticConcurrencyException 異常。
如果更改了多行,后續的 SaveChanges 調用可能檢測到更多的沖突。
 
除此之外,在 OptimisticConcurrencyException 異常處理程序中,在 Refresh 和 SaveChanges
這兩個調用之間,有極小的機率另一個用戶更改了數據。在商業應用程序中,這個異常也 應該捕捉。

 

 
 
26.2.3 添加和刪除數據
 
除了修改現有的數據,“實體框架”還允許將新項添加到一個 ObjectSet  集合中,以及從
ObjectSet 集合刪除項。
 
用“實體框架”生成一個實體模型時,每個實體的定義都包括一個名為 Create XXX 的工廠 方法(其中  XXX 是實體類的名稱)。可利用這個工廠方法創建一個新實體。創建新實例時, 這個方法要求為基礎數據庫的每個必須的(非 NULL 的)列傳遞實參。然后,可用實體類公 開的屬性設置附加的列值(下例中的 UnitPrice 和 QuantityPerUnit)。為了將新實體添加到一 個 ObjectSet  集合中,要使用 AddObject  方法。要將新實體保存到數據庫中,要在 ObjectContext 對象上調用 SaveChanges 方法。
 
下例新建一個 Product 實體,並把它添加到由 NorthwindEntities 上下文對象維護的集合中的 產品列表中。代碼還為 SupplierID 為 1 的供貨商添加了對新對象的一個引用。(“實體框架” 提供 Add 方法的目的是幫助維護實體之間的關系。)SaveChanges 方法將新產品插入數據庫。
 
NorthwindEntities northwindContext = new NorthwindEntities();
 
Product newProduct = Product,CreateProduct(0, "Fried Bread", false);
newProduct.UnitPrice = 55;
newProduct.QuantityPerUnit = "10 boxes";
 
ObjectSet<Product> products = northwindContext.Products;
products.AddObject(newProduct);
 
Supplier supplier = northwindContext.Suppliers.Single(s => s.SupplierID == 1);
supplier.Products.Add(newProduct);
northwindContext.SaveChanges();
 
注意  在這個例子中,CreateProduct 方法的第一個參數是 ProductID。在 Northwind 數據 庫中,ProductID 是一個 IDENTITY 列。調用 SaveChanges 時,SQL  Server 會為 這個列生成它自己的唯一的值,並丟棄你指定的值。
 
從 ObjectSet 集合中刪除實體對象是非常簡單的。只需調用 DeleteObject 方法,並指定要刪
除的實體。以下代碼刪除 ProductID 大於或等於 79 的所有產品。產品是在調用 SaveChanges
方法時,才實際地從數據庫中刪除的。
 
NorthwindEntities northwindContext = new NorthwindEntities();
 
var productList = from p in northwindContext.Products where p.productID >= 79
select p;
 
ObjectSet<Product> products = northwindContext.Products;
foreach (var product in productList)
{

 

products.DeleteObject(product);
}
 
northwindContext.SaveChanges();
 
如果表和其他表有關系,那么在刪除行時要小心,否則可能在更新數據庫時產生引用完整 性錯誤。例如,在 Northwind 數據庫中,如果刪除當前正在供應產品的一個供貨商,更新 就會失敗。必須先刪除供貨商的所有產品。為此,可以使用 Supplier 類的 Remove 方法。(和 Add 方法相似,Remove 方法也是由“實體框架”提供的。) 添加或刪除了數據之后,如果在保存更改時發生錯誤,SaveChanges  方法會拋出一個 UpdateException 異常。應該准備好捕捉這個異常。
 
現在,我們已掌握了足夠多的知識來完成 Suppliers 應用程序。
 
Ø   寫代碼來修改、刪除和創建產品
 
1.  返回正在編輯 Suppliers  應用程序的 Visual  Studio  2010  窗口。在設計視圖中顯示
SupplierInfo.xaml。
 
2.  在 XAML 窗格中,修改 productsList 控件的定義,讓它捕捉 KeyDown 事件,並調用一 個名為 productsList_KeyDown 的事件方法(這是該事件方法的默認名稱)。
 
3.  在代碼和文本編輯器窗口中,為 productsList_KeyDown 方法添加以下加粗的代碼:
 
private void productsList_KeyDown(object sender, KeyEventArgs e)
{
swi t c h (e.Key)
{
cas e Key.Enter: editProduct(this.productsList.SelectedItem as Product);
bre a k ;
cas e Key.Insert: addNewProduct();
bre a k ;
cas e Key.Delete: deleteProduct(this.productsList.SelectedItem as Product);
bre a k ;
}
}
 
這個方法檢查用戶按下的鍵。如果按的是 Enter 鍵,代碼就調用 editProduct 方法,將
產品細節作為參數傳遞。如果按的是 Insert 鍵,代碼就調用 addNewProduct 方法,在 當前供貨商的產品列表中創建並添加一個新產品。如果按的是 Delete  鍵,就調用 deleteProduct  方法刪除產品。 后面 的步驟 會 寫 editProduct 、 addNewProduct  和
deleteProduct 方法。
 
4.  返 回 設 計 視 圖 。 在 XAML  窗 格 中 , 修 改 productsList  控 件 的 定 義 來 捕 捉 MouseDoubleClick(鼠標雙擊)事件,並調用一個名為 productsList_MouseDoubleClick 的事件方法。(同樣地,這是事件方法的默認名稱。)

 

 
 
5.   在 “ 代碼和文本編輯器 ” 窗口 中,將以下加粗的語句添加到
productsList_MouseDoubleClick 方法中:
 
private void productsList_MouseDoubleClick(object sender, KeyEventArgs e)
{
edi t Pr o du c t( th is. p ro d uc t sL is t.S e le c te d It e a s Product);
}
這個方法直接調用 editProducts 方法。根據習慣,用戶在雙擊了數據之后,自然就是想 編輯它。
 
6.   將 deleteProduct 方法添加到 SupplierInfo 類,如下所示:
 
private void deleteProduct(Product product)
{
MessageBoxResult response = MessageBox.Show( String.Format("Delete {0}", product.ProductName),
"Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (response == MessageBoxResult.Yes)
{
this.northwindContext.Products.DeleteObject(product);
saveChanges.IsEnabled = true;
}
}
 
這個方法提示用戶確認刪除當前選中的產品。if  語句調用 Products  ObjectSet  集合的
DeleteObject 方法。最后,該方法激活 saveChanges 按鈕。在稍后的一步中,將為這個 按鈕添加功能,將 Products ObjectSet 中的更改發送回數據庫。
 

 

7.   選擇“項目”“|

添加類”命令。在“添加新項–Suppliers”對話框中,選擇“窗口(WPF)”

 

模板,在“名稱”文本框中輸入  ProductForm.xaml,然后單擊“添加”按鈕。
 
有幾個辦法都可以添加和編輯產品;ListView 控件中的列是只讀的文本項,但可以創 建一個自定義列表視圖,在其中包含文本框或其他控件來允許用戶輸入。然而,最簡 單的辦法是創建另一個窗體讓用戶編輯或添加產品細節。
 
8.   在設計視圖中,單擊 ProductForm 窗體。在“屬性”窗口中,將 ResizeMode 屬性設為
NoResize,Height 設為 225,Width 設為 515。
 
9.   在窗體上添加三個 Label 控件、三個 TextBox 控件和兩個 Button 控件。在“屬性”窗 口中,根據下表來設置這些控件的屬性。
 
控  件 屬  性
 
Label1
 
Content
 
Product Name
Height 23
Width 120

 

 
 
   
Margin
 
17, 20, 0, 0
 
VerticalAlignment
 
Top
 
HorizontalAlignment
 
Left
label2 Content Quantity Per Unit
 
Height
 
23
Width 120
Margin 17, 60, 0, 0
 
VerticalAlignment
 
Top
HorizontalAlignment Left
label3 Content Unit Price
 
Height
 
23
Width 120
Margin 17, 100, 0, 0
 
VerticalAlignment
 
Top
HorizontalAlignment Left
textBox1 Name productName
 
Height
 
21
Width 340
Margin 130, 24, 0, 0
 
VerticalAlignment
 
Top
HorizontalAlignment Left
textBox2 Name quantityPerUnit
 
Height
 
21
Width 340
Margin 130, 64, 0, 0
 
VerticalAlignment
 
Top
HorizontalAlignment Left
textBox3 Name unitPrice
 
Height
 
21
Width 120
Margin 130, 104, 0, 0
 
VerticalAlignment
 
Top
HorizontalAlignment Left
button1 Name ok
 
Content
 
OK
Height 23
Width 75

 

 
 
   
Margin
 
130, 150, 0, 0
 
VerticalAlignment
 
Top
 
HorizontalAlignment
 
Left
button2 Name cancel
 
Content
 
Cancel
Height 23
Width 75
 
Margin
 
300, 150, 0, 0
VerticalAlignment Top
HorizontalAlignment Left
 
現在,Supplier Information 窗體在設計視圖中的樣子應該如下圖所示。
 

 
10.  雙擊 OK 按鈕為 Click 事件創建處理程序。在顯示了 ProductForm.xaml.cs  的“代碼和 文本編輯器”窗口中,添加以下加粗顯示的代碼:
 
private void ok_Click(object sender, RoutedEventArgs e)
{
i f (String.IsNullOrEmpty(this.productName.Text))
{
M es s ag e B ox. S ho w (" T h p rod u c t must have a name", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
r et u rn ;
}
 
d ec i ma l result;
i f (!Decimal.TryParse(this.unitPrice.Text, out result))
{
M es s ag eB ox. S ho w (" T h p ric e must be a valid number", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
ret u rn ;
}
 
i f (result < 0)
{
M es s ag eB ox. S ho w (" T h p ric e must not be less than zero", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
r et u rn ;

 

 
 
}
 
t hi s .D i al og Res u l t = true;
}
 
應用程序將調用 ShowDialog 方法,將窗體顯示成一個模態對話框。用戶單擊窗體上的
一個按鈕后,如果 Click 事件代碼設置了 DialogResult 屬性,窗體就會自動關閉。 用戶單擊 OK 按鈕后,這個方法會對用戶輸入的信息執行一些簡單的校驗。數據庫的
Quantity Per Unit 列接受空值,所以用戶在窗體上可以不填寫這個字段。如果輸入一個
有效的產品名和價格,方法會將窗體的 DialogResult 屬性設為 true。這個值會傳回給
ShowDialog 方法調用。
 
11.  回到正在顯示 ProductForm.xaml 的設計視圖。從中選擇 Cancel 按鈕,在“屬性”窗口 中將它的 IsCancel 屬性設為 true(勾選復選框)。
如果用戶單擊了 Cancel 按鈕,它會自動關閉窗體,並將其值為 false 的一個 DialogResult
返回給 ShowDialog 方法。
 
12.  切換到正在顯示 SupplierInfo.xaml.cs  文件的“代碼和文本編輯器”窗口,將以下
addNewProduct 方法添加到 SupplierInfo 類:
 
private void addNewProduct()
{
ProductForm pf = new ProductForm();
pf.Title = "New Product for " + supplier.CompanyName;
if (pf.ShowDialog().Value)
{
Product newProd = new Product(); newProd.ProductName = pf.productName.Text; newProd.QuantityPerUnit = pf.quantityPerUnit.Text; newProd.UnitPrice = Decimal.Parse(pf.unitPrice.Text); this.supplier.Products.Add(newProd); this.productsInfo.Add(newProd);
saveChanges.IsEnabled = true;
}
}
 
addNewProduct 方法創建 ProductForm 窗體的一個新實例,在這個窗體的 Title 屬性中
添加供貨商的名稱,然后調用 ShowDialog 方法將窗體顯示成一個模態對話框。如果用 戶輸入了有效的數據,並單擊 OK 按鈕,則 if 塊中的代碼會創建一個新的 Product 對
象,並在其中填充來自 ProductForm 實例的信息。然后,方法把這這個對象添加到當
前供貨商的 Products 集合中,同時把它添加到窗體的 ListView 控件顯示的列表中。最 后,代碼激活 Save  Changes 按鈕。在后面的一步中,將為這個按鈕的 Click 事件添加
處事件處理程序,以便將更改存回數據庫。
 
13.  將以下 editProduct 方法添加到 SupplierInfo 類:
 
private void editProduct(Product prod)

 

{
ProductForm pf = new ProductForm(); pf.Title = "Edit Product Details"; pf.productName.Text = product.ProductName; pf.quantityPerUnit.Text = product.QuantityPerUnit; pf.unitPrice.Text = product.UnitPrice.ToString();
 
if (pf.ShowDialog().Value)
{
product.ProductName = pf.productName.Text; product.QuantityPerUnit = pf.quantityPerUnit.Text; product.UnitPrice = Decimal.Parse(pf.unitPrice.Text); saveChanges.IsEnabled = true;
}
}
 
editProduct 方法也創建 ProductForm 窗體的一個實例。這一次除了設置 Title 屬性,還
要使用當前所選產品的信息填充窗體上的字段。窗體顯示之后,用戶可以編輯這些值。 如果單擊 OK 按鈕關閉窗體,if 塊中的代碼會將新值復制回當前選定的產品,然后激
活 Save  Changes 按鈕。注意,這一次不需要手動更新 productsInfo 列表中的當前項,
因為 Product 類會將它的數據發生的變化自動通知給 ListView 控件。
 
14.  回到正在顯示 SupplierInfo.xaml 文件的設計視圖。雙擊 Save Changes 按鈕創建 Click
事件處理方法。
 
15.  在“代碼和文本編輯器”窗口中,將以下 using 語句添加到文件頂部:
 
using System.Data;
using System.Data.Objects;
 
這些命名空間包含了“實體框架”使用的許多類型。
 
16.  將加粗顯示的代碼添加到 saveChanges_Click 方法:
 
private void saveChanges_Click(object sender, RoutedEventArgs e)
{

 

try
{
 
 
 
}


 
 
thi s .n o rt h wi nd Con t ex t .S a ve Ch ang e s( ) ;
sav e Ch a ng e s. Is Ena b le = false;

 

cat c h (OptimisticConcurrencyException)
{
thi s .n o rt h wi nd Con t ex t .R e fr es h(R e fr e sh M od e. Cli e nt W in s , northwindContext.Products); this.northwindContext.SaveChanges();
}
cat c h (UpdateException uEx)
{
Mes s ag e Bo x .S ho w(u E x. I nn e rE xc ept i on . Me s sa ge " E rr o r saving changes");

 

thi s .n o rt h wi nd Con t ex t .R e fr es h(R e fr e sh M od e. Sto r eW i ns , northwindContext.Products);
}
cat c h (Exception ex)
{
Mes s ag e Bo x .S ho w(e x .M e ss a ge "Er r o r saving changes");
thi s .n o rt h wi nd Con t ex t .R e fr es h(R e fr e sh M od e. Sto r eW i ns , northwindContext.Products);
}
}
 
這個方法調用 ObjectContext 對象的 SaveChanges 方法,將所有更改發送回數據庫。異常處
理程序捕捉可能發生的任何異常。OptimisticConcurrencyException 處理程序使用早先描述的 策略刷新緩存,並再次保存更改。UpdateException 處理程序向用戶報告錯誤,然后指定 RefreshMode.StoreWins 參數,用數據庫的信息刷新緩存。(這導致用戶的更改被丟棄。)注 意,這個異常最有意義的數據保留在異常的 InnerException 屬性中(雖然你可能不想向用戶 顯示信息的類型)。如果發生其他類型的異常,Exception 處理程序顯示一條簡單消息並用 來自數據庫的信息刷新緩存。
 
Ø   測試 Suppliers 應用程序
 
1.  在“調試”菜單中選擇“開始執行(不調試)”命令,從而生成並運行應用程序。等窗 體顯示由 Exotic Liquids 提供的產品后,單擊產品 3(Aniseed Syrup)並按 Enter 鍵或雙擊 行。隨后應出現 Edit Product Details 窗體。將 Unit Price 值更改為  12.5 並單擊 OK 按鈕。 驗證新的價格被復制回列表視圖。
 
2.  按 Insert 鍵。隨后應出現 New Product for Exotic Liquids 窗體。隨便輸入一個產品名、 單位量(quantity per unit)和價格(unit price),然后單擊 OK 按鈕。驗證新產品已添加到 列表視圖中。
Product ID 列中的值應該是 0。這個值是數據庫的一個 identity 列,所以在保存更改時,
SQL Server 會為這個列生成它自己的一個唯一值。
 
3.  單擊 Save Changes 按鈕。數據保存后,新產品的 ID 應該在列表視圖中顯示。
 
4.  單擊新產品並按 Delete 鍵。在確認對話框中單擊“是”。驗證產品從窗體中消失。再 次單擊 Save Changes 按鈕,驗證操作成功。 試驗為其他供貨商添加、刪除和編輯產品。可連續進行多個修改,最后再單擊 Save Changes 按鈕。SaveChanges 方法會保存自從數據取回或者上一次保存以來的所有更 改。
 
提示 如果不慎刪除或覆蓋了想要保留的一個產品,請直接關閉應用程序,不要單擊 Save Changes 按鈕。注意,如果直接退出而不保存更改,程序不會向用戶發出警告。 另外,也可以在程序中添加一個 Discard  Changes(放棄更改)按鈕,讓它調用 northwindContext ObjectContext 對象的 Refresh 方法,從而用數據庫中的值重新填

 

 
 
充表。如上一個練習中的異常處理程序所示。
 
5.   關閉窗體,返回 Visual Studio 2010。
 
 
 
本章講述了如何使用“實體框架”為數據庫生成一個實體模型。講述了如何將窗體控件綁 定到實體集合,從而在 WPF 應用程序中使用實體模型。還講述了如何使用 LINQ  to Entities,通過一個實體模型訪問數據
 
l    如果希望繼續學習第 27 章的學習,請繼續運行 Visual Studio 2010,然后閱讀第 27 章。
l    如果希望現在就退出 Visual Studio 2010,請選擇“文件”|“退出”命令。如果看到 “保存”對話框,請單擊“是”按鈕保存項目。
 
第 26 章快速參考
 
目  標 操  作
使用“實體框架”創建實體類 使用“ADO.NET 實體數據模型”模板在項目中添加一個新類。 使用“實體模型向導”連接數據庫,並從中選擇想要建模的表
在 WPF 控件中顯示來自實體對象或集 合的數據 為控件的一個恰當的屬性定義綁定。如果控件顯示一個對象列 表,就將控件的 DataContext 屬性設為一個實體對象集合。如 果控件顯示單個對象的數據,就將控件的 DataContext 屬性設 為一個實體對象,並在綁定的 Path 屬性中指定要顯示實體對象 的哪個屬性的值
使用“實體框架”修改數據庫中的信 息 首先采取以下操作之一:
● 要更新數據庫的表中的一行,請將該行的數據取回到一個 實體對象中,然后將新值賦給實體對象的恰當屬性
● 要在數據庫的表中插入一個新行,請使用為實體類生成的 CreateXXX 工廠方法創建對應的實體對象的一個新實例(其中 XXX 是實體名稱)。設置它的屬性,然后調用恰當的 ObjectSet 集合的 AddObject 方法,將新的實體對象作為參數傳遞
●    要從數據庫的表中刪除一行,請調用恰當的 ObjectSet 集合 的 DeleteObject 方法,將要刪除的實體對象作為參數傳遞
 
 
之后,在完成了所有更改之后,調用 ObjectContext  對象的
SaveChanges 方法,將這些更改送回數據庫
使用“實體框架”檢測更新數據庫時 的沖突 寫一個處理程序處理 OptimisticConcurrencyException.異常。在 異常處理程序中,調用 ObjectContext 對象的 Refresh 方法,從 數據庫獲取最新的信息來刷新緩存中的原始值,最后再次調用

 

 
 
SaveChanges 方法
 
 
第 VI 部分 使用 Visual Studio 2010 構
建專業解決方案
 
 
 
 
 
 
 
 
 
4 第 27 章 任務並行庫入門
4 第 28 章 執行並行數據訪問
4 第 29 章 創建和使用 Web 服務
 
 
第 27 章 任務並行庫入門
 
 
 
本章旨在教會你:
l    理解在應用程序中實現並行操作所帶來的好處
l    了解為什么說任務並行庫(TPL)提供了一個理想的平台來實現多線程應用程序
l    在應用程序中使用 Task 類創建和運行並行操作
l    使用 Parallel 類並行化一些常用的編程構造
l    為線程使用任務,增強 GUI 應用程序的響應能力和吞吐能力
l    取消長時間運行的任務,處理並行操作引發的異常
 
到目前為止,你已學習了如何使用 Microsoft V isual C#構建應用程序提供一個圖形用戶界面, 並對數據庫中的數據進行管理。這些是現代系統的常用功能。然而,隨着技術的進步,用 戶的需求也在進步。用戶賴以執行日常操作的應用程序需要提供更復雜的解決方案。在本 章最后一部分,將探討.NET Framework 4.0 引入的一些高級功能。具體地說,本章會講述如 何使用“任務並行庫”(Task  Parallel  Library)增強應用程序的並發性。下一章將講述如何 將.NET Framework 提供的並行擴展與 LINQ 組合使用,以提高數據吞吐能力。在最后一章, 將探討如何使用 Windows Communication Foundation(WCF)構建分布式解決方案,以便集

 

 
 
成多台計算機上運行的服務。作為一個附贈,我在電子版(英文)形式的附錄中60描述了 如何使用“動態語言運行時”(Dynamic Language Runtime)生成 C#應用程序和組件,它們
能和使用其他語言生成的服務進行互操作。這些語言在.NET Framework  提供的結構的外部 工作,包括 Python 和 Ruby 等。
通過本章前面的學習,你知道了如何使用 C#編寫以單線程形式運行的程序。所謂“單線程”, 是指在任何給定的時刻,一個程序只能執行一條指令。這並非總是應用程序的最優運行方 式。例如,第 23 章講過,如果程序等待用戶單擊 WPF 窗體上的一個按鈕,那么等待期間 也許能做其他一些工作。然而,如果單線程程序必須執行一個耗時的、處理器密集型的計 算,便不能響應用戶在窗體上輸入數據或者單擊一個菜單項的操作。對於應用程序來說, 應用程序就像是死掉了一樣。只有在計算完成之后,UI 才會重新有響應。能同時運行多個 任務的應用程序可以更好地利用計算機的可用資源,可以運行得更快,而且能保證響應能 力。除此之外,有的任務如果划分為並行的執行路徑,那么運行速度也許會更快。第 23 章 講述了 WPF 如何利用線程提升圖形用戶界面中的響應能力。在本章中,將介紹如何利用“任 務並行庫”在程序中實現多任務處理的一個更常規的形式,它能應用於計算密集型的應用 程序,而非只能應用於那些關心 UI 管理的應用程序。
 
 
27.1 為何使用並行處理來執行多任務處理
 
如前所述,在應用程序中執行多任務處理主要是出於兩個方面的原因:
l   增強響應能力  將程序划分到並發執行的線程中,並允許每個線程輪流運行一小段時 間,就可以向應用程序的用戶營造出程序一次執行多個任務的“假象”。這是傳統的協 作式多線程模型,許多有經驗的 Windows 用戶都很熟悉它。然而,它並不是真正的多 任務處理,因為處理器是在多個線程之間共享的。另外,協作式的本質要求每個線程 執行的代碼都要具有恰當的行為方式。如果一個線程壟斷了 CPU 和資源,讓其他線程 一直沒有機會運行,這種方式的好處就喪失殆盡了。有的時候,很難按照這個模型寫 出一個具有良好行為的應用程序。
l   增強伸縮性 由於能有效地利用處理資源,並用這些資源減少執行一個應用程序的各 個不同所需的時間,所以能增強伸縮性。61開發者可以判斷應用程序的哪些部分能並 行執行,並相應作出安排。隨着計算資源越來越多,更多的任務可以並行運行。就在 不久之前,這個模型還只適用於安裝了多個 CPU 的系統,或者能夠在聯網的計算機之 間進行分布處理的系統。在這兩種情況下,都必須使用一個模型對並行任務進行協調。 Microsoft 提供了 Windows 的一個特別版本,名為 High  Performance  Compute  (HPC)
Server  2008,它允許企業構建服務器集群,以便並行分布和執行任務。開發人員可使 用 Microsoft 實現的 Message Passing Interface (MPI),基於並行任務來構建應用程序。
 
 
60  這個附錄在譯者博客上提供。網址是  http://transbot.blog.163.com——譯者注
 
61  在少量時間里做更多工作的能力,就是所謂的“伸縮性”。作為一個伸縮性好的服務器,理論上應該 CPU 越多,一個耗時 操作所需的時間就越短。通俗地說,在多個 CPU 之間並行執行,執行時間將根據 CPU 的數量成比例地縮短。——譯者注


 
 
MPI  是一種著名的、跟語言無關的通信協議。這些並行任務相互之間通過發送消息來
進行協作。對於大規模的、計算限制(compute-bound)的工程和科學應用程序來說, 基於 Windows HPC Server 2008 和 MPI 的解決方案是非常理想的。但是,對於小規模的 桌面(台式機)系統來說,它們顯得過於昂貴。
 
根據以上描述,你可能覺得為桌面應用程序構建多任務解決方案時,成本效益最好的 方式就是使用協作式多線程模型。然而,多線程方案的主要目的是增強應用程序的響應能 力——在單處理器計算機上,它確保每個任務都公平地獲得處理器時間。在多處理器系統 上,這個方案並不十分恰當,因為它不具備在不同處理器上分布負載的能力,所以伸縮性 很差。雖然安裝多個處理器的台式機還十分昂貴,而且極其少見,但這並不是一個問題, 情況正在逐漸發生改變,下一節將進一步解釋這個問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM