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 的解決方案是很是理想的。可是,對於小規模的 桌面(臺式機)系統來講,它們顯得過於昂貴。
 
根據以上描述,你可能以爲爲桌面應用程序構建多任務解決方案時,成本效益最好的 方式就是使用協做式多線程模型。然而,多線程方案的主要目的是加強應用程序的響應能 力——在單處理器計算機上,它確保每一個任務都公平地得到處理器時間。在多處理器系統 上,這個方案並不十分恰當,由於它不具有在不一樣處理器上分佈負載的能力,因此伸縮性 不好。雖然安裝多個處理器的臺式機還十分昂貴,並且極其少見,但這並非一個問題, 狀況正在逐漸發生改變,下一節將進一步解釋這個問題。數據庫

相關文章
相關標籤/搜索