熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

數據層組件設計與數據傳遞一

2022-06-13   來源: .NET編程 

  摘要

  學習向 應用程序公開數據的最佳方式以及如何實現一個有效的策略以便在分布式應用程序的層間傳遞數據

  簡介

  在設計分布式應用程序時需要確定如何訪問和表示與該應用程序相關聯的業務數據本文提供一些指導原則以幫助您選擇公開數據保持數據和在應用程序的層間傳遞數據的最佳方式

  圖 所示為分布式應用程序中的常見層本文區分業務數據與使用這些數據的業務過程並且僅在需要明確說明時討論業務過程層同樣本文僅在直接涉及數據表示方式(例如 Microsoft? ASPNET Web 頁面公開業務數據的方式)時討論表示層 中使用了兩個新術語數據訪問邏輯組件和業務實體組件本文後面將解釋這些術語

  

  圖 分布式應用程序中數據的訪問與表示

  多數應用程序將數據存儲在關系數據庫中除此之外還有其他數據存儲方式但本文重點討論 NET 應用程序與關系數據庫交互的方式而並不專門討論它如何與平面文件非關系數據庫等其他數據存儲中的數據進行交互

  本文明確區分保持邏輯與數據本身將保持邏輯與數據區分開來的原因如下
獨立的數據保持組件可以將應用程序與數據源名稱連接信息字段名等數據庫相關內容隔離開
現在的許多應用程序都采用 XML Web ServicesMicrosoft 消息隊列(亦稱 MSMQ)等松散耦合的基於消息的技術這些應用程序通常通過傳遞業務文檔而不是傳遞對象進行通信

  為區分保持邏輯與數據本身本文提出了兩種不同的組件類型
數據訪問邏輯組件數據訪問邏輯組件從數據庫中檢索數據並把實體數據保存回數據庫中數據訪問邏輯組件還包含實現數據相關操作所需的所有業務邏輯
業務實體組件數據用來表示產品訂單等現實世界中的業務實體在應用程序中表示這種業務實體的方法非常多例如 XMLDataSet面向對象的自定義類等這取決於應用程序的物理和邏輯設計限制本文後面將詳細討論各種設計方案

  數據訪問邏輯組件

  數據訪問邏輯組件代表調用程序提供對數據庫執行以下任務的方法
在數據庫中創建記錄
讀取數據庫中的記錄並把業務實體數據返回給調用程序
使用調用程序提供的修改後的業務實體數據更新數據庫中的記錄
刪除數據庫中的記錄

  執行上述任務的方法通常稱為CRUD方法這是由各項任務的首字母組成的一個縮寫詞

  數據訪問邏輯組件還提供對數據庫實現業務邏輯的方法例如數據訪問邏輯組件可能包含一個查找目錄中本月銷售額最高的產品的方法

  通常數據訪問邏輯組件訪問一個單一數據庫並封裝了針對該數據庫中一個表或一組相關表的數據相關操作例如可以定義一個數據訪問邏輯組件來處理數據庫中的 Customer 表和 Address 表同時定義另一個數據訪問邏輯組件來處理 Orders 表和 OrderDetails 表本文後面將討論將數據訪問邏輯組件映射到數據庫表的設計決策

  表示業務實體

  每個數據訪問邏輯組件都處理一種特定類型的業務實體例如Customer 數據訪問邏輯組件處理 Customer 業務實體表示業務實體的方式很多這取決於諸如以下因素
是否需要把業務實體數據與 Microsoft Windows® 窗體或 ASPNET 頁面中的控件綁定在一起?
是否需要對業務實體數據執行排序或搜索操作?
應用程序是每次處理一個業務實體還是通常處理一組業務實體?
是本地部署還是遠程部署應用程序?
XML Web services 是否使用該業務實體?
性能可縮放性可維護性編程方便性等非功能性要求的重要程度如何?

  本文將概述以下實現選項的優缺點
XML使用 XML 字符串或 XML 文檔對象模型 (DOM) 對象來表示業務實體數據XML 是一種開放而靈活的數據表示格式可用於集成各種類型的應用程序
DataSetDataSet 是緩存在內存中的表它是從關系數據庫或 XML 文檔中獲得的數據訪問邏輯組件可以使用 DataSet 來表示從數據庫中檢索到的業務實體數據您可以在應用程序中使用該 DataSet
有類型的 DataSet有類型的 DataSet 是從 ADONET DataSet 類繼承而來的類它為訪問表和 DataSet 中的列提供了具有嚴格類型的方法事件和屬性
業務實體組件這是一種自定義類用於表示各種業務實體類型您可以定義保存業務實體數據的字段並定義將此數據向客戶端應用程序公開的屬性然後使用在該類中定義的字段來定義方法以封裝簡單的業務邏輯此選項並不通過 CRUD 方法實現與基礎數據訪問邏輯組件的數據傳遞而是通過客戶端應用程序直接與數據訪問邏輯組件進行通信以執行 CRUD 操作
帶有 CRUD 行為的業務實體組件按上述方法定義一個自定義實體類並實現調用與此業務實體相關聯的基礎數據訪問邏輯組件的 CRUD 方法

注意如果希望以一種更加面向對象的方式使用數據可以使用另一種替代方法即定義一個基於公共語言運行庫的反射功能的對象保持層您可以創建一個使用反射功能來讀取對象屬性的架構並使用映射文件來描述對象與表之間的映射然而要有效地實現上述方法需要大量的基礎結構代碼投入對於 ISV 和解決方案提供商來說這種投入或許可以接受但對於大多數組織則不可行有關這方面的討論超出了本文的范圍這裡不再論述

  技術因素

  圖 所示為影響數據訪問邏輯組件和業務實體實現策略的一些技術因素本文將分別討論這些技術因素並提供相關建議

  

  圖 影響數據訪問邏輯組件和業務實體設計的技術因素

  將關系數據映射到業務實體

  數據庫通常包含許多表這些表之間的關系通過主鍵和外鍵來實現當定義業務實體以在 應用程序中表示這些數據時必須確定如何把這些表映射到業務實體

  請考慮圖 所示的假想零售商數據庫

  

  圖 假想的關系數據庫中的表關系

  下表總結了示例數據庫中的關系類型

   關系類型 示例 說明 一對多 CustomerAddress



CustomerOrder 一個客戶可以有多個地址例如送貨地址帳單接收地址聯系地址等

一個客戶可以有多個訂單 多對多 OrderProduct 一個訂單可以包含許多產品每種產品由 OrderDetails 表中的單獨一行表示同樣一種產品也可以出現在許多訂單中

  當定義業務實體以在數據庫中建立信息模型時應考慮要如何在您的應用程序中使用這些信息應當標識封裝您的應用程序的功能的核心業務實體而不是為每個表定義單獨的業務實體

  該假想零售商的應用程序中的典型操作如下
獲取(或更新)客戶的有關信息(包括地址)
獲取客戶的訂單列表
獲取特定訂單的訂購項目列表
創建新訂單
獲取(或更新)一個或一組產品的有關信息

  為滿足這些應用程序要求該應用程序要處理三個邏輯業務實體CustomerOrder 和 Product對於每個業務實體都將定義一個單獨的數據訪問邏輯組件如下所示
Customer 數據訪問邏輯組件此類將為檢索和修改 Customer 表和 Address 表中的數據提供服務
Order 數據訪問邏輯組件此類將為檢索和修改 Order 表和 OrderDetails 表中的數據提供服務
Product 數據訪問邏輯組件此類將為檢索和修改 Product 表中的數據提供服務

  圖 所示為這些數據訪問邏輯組件與它們所表示的數據庫中的表之間的關系

  

  圖 定義向 NET 應用程序公開關系數據的數據訪問邏輯組件

  將關系數據映射到業務實體的建議

  要將關系數據映射到業務實體請考慮以下建議
花些時間來分析您的應用程序的邏輯業務實體並為之建立模型不要為每個表定義一個單獨的業務實體建立應用程序的工作方式模型的方法之一是使用統一建模語言 (UML)UML 是一種形式設計注釋用於在面向對象的應用程序中建立對象模型並獲取有關對象如何表示自動過程人機交互以及關聯的信息
不要定義單獨的業務實體來表示數據庫中的多對多表可以通過在數據訪問邏輯組件中實現的方法來公開這些關系例如前面示例中的 OrderDetails 表沒有映射到單獨的業務實體而是通過在 Order 數據訪問邏輯組件中封裝 OrderDetails 表來實現 Order 與 Product 表之間的多對多關系
如果具有返回特定業務實體類型的方法請把這些方法放在該類型對應的數據訪問邏輯組件中例如當檢索一個客戶的全部訂單時返回值為 Order 類型因此應在 Order 數據訪問邏輯組件中實現該功能反之當檢索訂購某特定產品的全部客戶時應在 Customer 數據訪問邏輯組件中實現該功能
數據訪問邏輯組件通常訪問來自單一數據源的數據當需要聚合多個數據源的數據時建議分別為訪問每個數據源定義一個數據訪問邏輯組件這些組件可以由一個能夠執行聚合任務的更高級業務過程組件來調用建議采用這種方法的原因有二
事務管理集中在業務過程組件中不需要由數據訪問邏輯組件顯式控制如果通過一個數據訪問邏輯組件訪問多個數據源則需要把該數據訪問邏輯組件作為事務處理的根這會給僅讀取數據的功能帶來額外的系統開銷
通常並不是應用程序的所有區域都需要聚合並且通過分離對數據的訪問您可以單獨使用該類型也可以在必要時將其用作聚合的一部分

  實現數據訪問邏輯組件

  數據訪問邏輯組件是一個無狀態類也就是說所交換的所有消息都可以獨立解釋調用之間不存在狀態數據訪問邏輯組件為訪問單一數據庫(某些情況下可以是多個數據庫例如水平數據庫分區)中的一個或多個相關表提供方法通常數據訪問邏輯組件中的這些方法將調用存儲過程以執行相應操作

  數據訪問邏輯組件的主要目標之一是從調用應用程序中隱藏數據庫的調用及格式特性數據訪問邏輯組件為這些應用程序提供封裝的數據訪問服務具體地說數據訪問邏輯組件處理以下實現細節
管理和封裝鎖定模式
正確處理安全性和授權問題
正確處理事務處理問題
執行數據分頁
必要時執行數據相關路由
為非事務性數據的查詢實現緩存策略(如果適用)
執行數據流處理和數據序列化

  本節後面將詳細討論其中的某些問題

  數據訪問邏輯組件的應用方案

  圖 所示為從各種應用程序類型(包括 Windows 窗體應用程序ASPNET 應用程序XML Web Services 和業務過程)中調用數據訪問邏輯組件的方式根據應用程序的部署方式這些調用可以是本地的也可以是遠程的

  

  


數據訪問邏輯組件的應用方案

實現數據訪問邏輯組件類

  數據訪問邏輯組件使用 ADONET 執行 SQL 語句或調用存儲過程

  如果您的應用程序包含多個數據訪問邏輯組件可以使用數據訪問助手組件來簡化數據訪問邏輯組件類的實現該組件可以幫助管理數據庫連接執行 SQL 命令以及緩存參數數據訪問邏輯組件仍然封裝訪問特定業務數據所需的邏輯而數據訪問助手組件則專注於數據訪問 API 的開發和數據連接配置從而幫助減少代碼的重復當使用 Microsoft SQL Server&#; 數據庫時可在您的應用程序中將其用作一個通用的數據訪問助手組件 所示為使用數據訪問助手組件幫助實現數據訪問邏輯組件的方法

  

  圖 使用數據訪問助手組件實現數據訪問邏輯組件

  當存在所有數據訪問邏輯組件公用的實用程序功能時可以定義一個基本類以從中繼承和擴展數據訪問邏輯組件

  將數據訪問邏輯組件類設計為可以為不同類型的客戶端提供一致的接口如果將數據訪問邏輯組件設計為與當前及潛在的業務過程層的實現要求相兼容可以減少必須實現的附加接口接觸面或映射層的數目

  要支持廣泛的業務過程和應用程序請考慮以下技術以便將數據傳入和傳出數據訪問邏輯組件方法

將業務實體數據傳遞給數據訪問邏輯組件中的方法您可以用多種不同的格式傳遞數據作為一系列標量值作為 XML 字符串作為 DataSet 或作為自定義業務實體組件
從數據訪問邏輯組件中的方法返回業務實體數據您可以用多種不同的格式返回數據作為輸出參數標量值作為 XML 字符串作為 DataSet作為自定義業務實體組件或作為數據讀取器

  以下各節將說明用於將業務實體數據傳入和傳出數據訪問邏輯組件的各種方式以及每種方式的優缺點這些信息有助於您根據自己特定的應用程序方案做出相應選擇

  將標量值作為輸入和輸出傳遞

  這種方法的優點如下
抽象調用程序只需要知道定義業務實體的數據而不需要知道業務實體的具體類型或具體結構
序列化標量值本身支持序列化
內存使用效率高標量值只傳遞實際需要的數據
性能當處理實例數據時標量值具有比本文所述的其他方法更高的性能

  這種方法的缺點如下
緊密耦合與維護架構的更改可能需要修改方法簽名這會影響調用代碼
實體集合要向數據訪問邏輯組件保存或更新多個實體必須進行多次單獨的方法調用這在分布式環境中會給性能帶來很大影響
支持開放式並發要支持開放式並發必須在數據庫中定義時間戳列並將其作為數據的一部分   將 XML 字符串作為輸入和輸出傳遞

  這種方法的優點如下
松散耦合調用程序只需要知道定義業務實體的數據和為業務實體提供元數據的架構
集成采用 XML 可以支持以各種方式(例如 應用程序BizTalk Orchestration 規則和第三方業務規則引擎)實現的調用程序
業務實體集合一個 XML 字符串可以包含多個業務實體的數據
序列化字符串本身支持序列化

  這種方法的缺點如下
需要重新分析 XML 字符串必須在接收端重新分析 XML 字符串很大的 XML 字符串會影響性能
內存使用效率低XML 字符串比較繁瑣因而在需要傳遞大量數據時會降低內存使用效率
支持開放式並發要支持開放式並發必須在數據庫中定義時間戳列並將其作為 XML 數據的一部分   將 DataSet 作為輸入和輸出傳遞

  這種方法的優點如下
固有功能DataSet 提供了內置功能可以處理開放式並發(以及數據適配器)並支持復雜的數據結構此外有類型的 DataSet 還提供了數據驗證支持
業務實體集合DataSet 是為處理復雜的關系集合而設計的因此不需要再編寫自定義代碼來實現這一功能
維護更改架構不會影響方法簽名然而如果使用的有類型的 DataSet 和程序集具有嚴格名稱則必須按照新版本重新編譯數據訪問邏輯組件類或在全局程序集緩存中使用發布者策略或在配置文件中定義一個 <bindingRedirect> 元素
序列化DataSet 本身支持 XML 序列化並且可以跨層序列化

  這種方法的缺點如下
性能實例化和封送處理 DataSet 會增加運行時負擔
表示單個業務實體DataSet 是為處理一組數據而設計的如果您的應用程序主要處理實例數據則標量值或自定義實體是更好的方法後者不會影響性能   將自定義業務實體組件作為輸入和輸出傳遞

  這種方法的優點如下
維護更改架構不會影響數據訪問邏輯組件方法簽名然而如果業務實體組件包含在嚴格命名的程序集中就會出現與有類型的 DataSet 同樣的問題
業務實體集合可以將自定義業務實體組件的數組和集合傳入和傳出方法

  這種方法的缺點如下
支持開放式並發要方便地支持開放式並發必須在數據庫中定義時間戳列並將其作為實例數據的一部分
集成限制當使用自定義業務實體組件作為數據訪問邏輯組件的輸入時調用程序必須知道業務實體的類型而這會限制不使用 NET 的調用程序的集成然而如果調用程序使用自定義業務實體組件作為數據訪問邏輯組件的輸出則上述問題並不會限制集成例如Web 方法可以返回從數據訪問邏輯組件返回的自定義業務實體組件並使用 XML 序列化自動將該業務實體組件序列化為 XML   將數據讀取器作為輸出返回

  這種方法的優點如下
性能當需要快速呈現數據時這種方法具有性能優勢並且可以使用表示層代碼部署數據訪問邏輯組件

  這種方法的缺點如下
遠程建議不要在遠程方案中使用數據讀取器因為它可能會使客戶端應用程序與數據庫保持長時間的連接   配合使用數據訪問邏輯組件與存儲過程

  可以使用存儲過程執行數據訪問邏輯組件支持的許多數據訪問任務

  優點
存儲過程通常可以改善性能因為數據庫能夠優化存儲過程使用的數據訪問計劃並為以後的重新使用緩存該計劃
可以在數據庫內分別設置各個存儲過程的安全保護管理員可以授予客戶端執行某個存儲過程的權限而不授予任何基礎表訪問權限
存儲過程可以簡化維護因為修改存儲過程通常比修改所部署的組件中的硬編碼 SQL 語句要容易然而隨著在存儲過程中實現的業務邏輯的增多上述優勢會逐漸減弱
存儲過程增大了從基礎數據庫架構進行抽象的程度存儲過程的客戶端與存儲過程的實現細節和基礎架構是彼此分離的
存儲過程會降低網絡流量應用程序可以按批執行 SQL 語句而不必發出多個 SQL 請求

  盡管存儲過程具有上述優點但仍有某些情況不適合使用存儲過程

  缺點
如果邏輯全部在存儲過程中實現那麼涉及廣泛業務邏輯和處理的應用程序可能會給服務器帶來過重負荷這類處理包括數據傳輸數據遍歷數據轉換和大計算量操作應把這類處理移到業務過程或數據訪問邏輯組件中與數據庫服務器相比它們具有更好的可縮放性
不要把所有業務邏輯都放在存儲過程中如果必須在 T SQL 中修改業務邏輯應用程序的維護和靈活性將成為問題例如支持多個 RDBMS 的 ISV 應用程序不應當分別為每個系統維護存儲過程
通常存儲過程的編寫與維護是一項專門技能並非所有開發人員都能夠掌握這會造成項目開發計劃的瓶頸

  配合使用數據訪問邏輯組件與存儲過程的建議

  配合使用數據訪問邏輯組件與存儲過程時請考慮以下建議
公開存儲過程數據訪問邏輯組件應當是向存儲過程名稱參數字段等數據庫架構信息公開的僅有組件業務實體實現應不需要知道或依賴於數據庫架構
使存儲過程與數據訪問邏輯組件相關聯每個存儲過程只應被一個數據訪問邏輯組件調用並應與調用它的數據訪問邏輯組件相關聯例如假設一個客戶向一個零售商訂貨您可以編寫一個名為 OrderInsert 的存儲過程用於在數據庫中創建訂單在您的應用程序中必須確定是從 Customer 數據訪問邏輯組件還是從 Order 數據訪問邏輯組件調用該存儲過程Order 數據訪問邏輯組件處理所有與訂單相關的任務而 Customer 數據訪問邏輯組件處理客戶姓名地址等客戶信息因此最好使用前者
命名存儲過程為要使用的數據訪問邏輯組件定義存儲過程時所選擇的存儲過程名稱應當強調與之相關的數據訪問邏輯組件這種命名方法有助於識別哪個組件調用哪個存儲過程並為在 SQL 企業管理器中邏輯分組存儲過程提供了一種方法例如可以事先編寫名為 CustomerInsertCustomerUpdateCustomerGetByCustomerIDCustomerDelete 的存儲過程供 Customer 數據訪問邏輯組件使用然後提供 CustomerGetAllInRegion 等更具體的存儲過程以支持您的應用程序的業務功能

注意不要在存儲過程名稱前面使用前綴 sp_這會降低性能當調用一個以 sp_ 開頭的存儲過程時SQL Server 始終會先檢查 master 數據庫即使該存儲過程已由數據庫名稱進行限定

解決安全性問題如果接受用戶輸入以動態執行查詢請不要通過沒有使用參數的連接值來創建字符串如果使用 sp_execute 執行結果字符串或者不使用 sp_executesql 參數支持則還應避免在存儲過程中使用字符串連接

  管理鎖定和並發

  某些應用程序在更新數據庫數據時采用後進有效(Last in Wins) 法使用後進有效法更新數據庫時不會將更新與原始記錄相比較因此可能會覆蓋掉自上次刷新記錄以來其他用戶所做的所有更改然而有時應用程序卻需要在執行更新之前確定數據自最初讀取以來是否被更改

  數據訪問邏輯組件可以實現管理鎖定和並發的代碼管理鎖定和並發的方法有兩種
保守式並發為進行更新而讀取某行數據的用戶可以在數據源中對該行設置一個鎖定在該用戶解除鎖定之前其他任何用戶都不能更改該行
開放式並發用戶在讀取某行數據時不鎖定該行其他用戶可以在同一時間自由訪問該行當用戶要更新某行數據時應用程序必須確定自該行被讀取以來其他用戶是否進行過更改嘗試更新已經過更改的記錄會導致並發沖突

  使用保守式並發

  保守式並發主要用於數據爭用量大以及通過鎖定來保護數據的成本低於發生並發沖突時回滾事務的成本的環境如果鎖定時間很短(例如在編程處理的記錄中)則實現保守式並發效果最好

  保守式並發要求與數據庫建立持久連接並且因為記錄可能被鎖定較長時間因此當用戶與數據進行交互時不能提供可縮放的性能

  使用開放式並發

  開放式並發適用於數據爭用量低或要求只讀訪問數據的環境開放式並發可以減少所需鎖定的數量從而降低數據庫服務器的負荷提高數據庫的性能

  開放式並發在 中被廣泛使用以滿足移動和脫機應用程序的需要在這種情況下長時間鎖定數據是不可行的此外保持記錄鎖定還要求與數據庫服務器的持久連接這在脫機應用程序中是不可能的

  測試開放式並發沖突

  測試開放式並發沖突的方法有多種
使用分布式時間戳分布式時間戳適用於不要求協調的環境在數據庫的每個表中添加一個時間戳列或版本列時間戳列與對表內容的查詢一起返回當試圖更新時數據庫中的時間戳值將與被修改行中的原始時間戳值進行比較如果這兩個值匹配則執行更新同時時間戳列被更新為當前時間以反映更新如果這兩個值不匹配則發生開放式並發沖突
保留原始數據值的副本在查詢數據庫的數據時保留原始數據值的一個副本在更新數據庫時檢查數據庫的當前值是否與原始值匹配
原始值保存在 DataSet 中當更新數據庫時數據適配器可以使用該原始值執行開放式並發檢查
使用集中的時間戳在數據庫中定義一個集中的時間戳表用於記錄對任何表中的任何行的更新例如時間戳表可以顯示以下信息 日下午 : 約翰更新了表 XYZ 中的行

  集中的時間戳適用於簽出方案以及某些脫機客戶端方案其中可能需要明確的鎖定所有者和替代管理此外集中的時間戳還可以根據需要提供審核

  手動實現開放式並發

  請考慮以下 SQL 查詢

  SELECT Column Column Column FROM Table

  要在更新 Table 的行時測試開放式並發沖突可以發出以下 UPDATE 語句

   UPDATE Table Set Column = @NewValueColumn
Set Column = @NewValueColumn
Set Column = @NewValueColumn
WHERE Column = @OldValueColumn AND
Column = @OldValueColumn AND
Column = @OldValueColumn

  如果原始值與數據庫中的值匹配則執行更新如果某個值被修改WHERE 子句將無法找到相應匹配從而更新將不會修改該行您可以對此技術稍加變化即只對特定列應用 WHERE 子句使得如果自上次查詢以來特定字段被更新則不覆蓋數據

注意請始終返回一個唯一標識查詢中的一行的值例如一個主關鍵字以用於 UPDATE 語句的 WHERE 子句這樣可以確保 UPDATE 語句更新正確的行

  如果數據源中的列允許空值則可能需要擴展 WHERE 子句以便檢查本地表與數據源中匹配的空引用例如以下 UPDATE 語句將驗證本地行中的空引用(或值)是否仍然與數據源中的空引用(或值)相匹配

   UPDATE Table Set Column = @NewColumnValue
WHERE (@OldColumnValue IS NULL AND Column IS NULL) OR Column =
@OldColumnValue

  使用數據適配器和 DataSet 實現開放式並發

  可以配合使用 DataAdapterRowUpdated 事件與前面所述技術以通知您的應用程序發生了開放式並發沖突每當試圖更新 DataSet 中的修改過的行時都將引發 RowUpdated 事件可以使用 RowUpdated 事件添加特殊處理代碼包括發生異常時的處理添加自定義錯誤信息以及添加重試邏輯

  RowUpdated 事件處理程序接收一個 RowUpdatedEventArgs 對象該對象具有 RecordsAffected 屬性可以顯示針對表中的一個修改過的行的更新命令會影響多少行如果把更新命令設置為測試開放式並發則當發生開放式並發沖突時RecordsAffected 屬性將為 設置 RowUpdatedEventArgsStatus 屬性以表明要采取的操作例如可以把該屬性設置為 UpdateStatusSkipCurrentRow 以跳過對當前行的更新但是繼續更新該更新命令中的其他行有關 RowUpdated 事件的詳細信息請參閱 Working with DataAdapter Events

  使用數據適配器測試並發錯誤的另一種方法是在調用 Update 方法之前把 DataAdapterContinueUpdateOnError 屬性設置為 true完成更新後調用 DataTable 對象的 GetErrors 方法以確定哪些行發生了錯誤然後使用這些行的 RowError 屬性找到特定的詳細錯誤信息有關如何處理行錯誤的詳細信息請參閱 Adding and Reading Row Error Information

  以下代碼示例顯示了 Customer 數據訪問邏輯組件如何檢查並發沖突該示例假設客戶端檢索到了一個 DataSet 並修改了數據然後把該 DataSet 傳遞給了數據訪問邏輯組件中的 UpdateCustomer 方法UpdateCustomer 方法將通過調用以下存儲過程來更新相應的客戶記錄僅當客戶 ID 與公司名稱未被修改時存儲過程才能更新該客戶記錄

   CREATE PROCEDURE CustomerUpdate
{
@CompanyName varchar(
@oldCustomerID varchar(
@oldCompanyName varchar(
}
AS
UPDATE Customers Set CompanyName = @CompanyName
WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName
GO

  在 UpdateCustomer 方法中以下代碼示例將一個數據適配器的 UpdateCommand 屬性設置為測試開放式並發然後使用 RowUpdated 事件測試開放式並發沖突如果遇到開放式並發沖突應用程序將通過設置要更新的行的 RowError 來表明開放式並發沖突注意傳遞給 UPDATE 命令中的 WHERE 子句的參數值被映射到 DataSet 中各相應列的原始值

  

  // CustomerDALC 類中的 UpdateCustomer 方法
public void UpdateCustomer(DataSet dsCustomer)
{
// 連接到 Northwind 數據庫
SqlConnection cnNorthwind = new SqlConnection(
Data source=localhost;Integrated security=SSPI;Initial
Catalog=northwind);

  // 創建一個數據適配器以訪問 Northwind 中的 Customers 表
SqlDataAdapter da = new SqlDataAdapter();

  // 設置數據適配器的 UPDATE 命令調用存儲過程UpdateCustomer
daUpdateCommand = new SqlCommand(CustomerUpdate cnNorthwind);
daUpdateCommandCommandType = CommandTypeStoredProcedure;

  // 向數據適配器的 UPDATE 命令添加兩個參數
// 為 WHERE 子句指定信息(用於檢查開放式並發沖突)
daUpdateCommandParametersAdd(@CompanyName SqlDbTypeNVarChar
CompanyName);

  // 將 CustomerID 的原始值指定為第一個 WHERE 子句參數
SqlParameter myParm = daUpdateCommandParametersAdd(
@oldCustomerID SqlDbTypeNChar
CustomerID);
myParmSourceVersion = DataRowVersionOriginal;

  // 將 CustomerName 的原始值指定為第二個 WHERE 子句參數
myParm = daUpdateCommandParametersAdd(
@oldCompanyName SqlDbTypeNVarChar
CompanyName);
myParmSourceVersion = DataRowVersionOriginal;

  // 為 RowUpdated 事件添加一個處理程序
daRowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);

  // 更新數據庫
daUpdate(ds Customers);

  foreach (DataRow myRow in dsTables[Customers]Rows)
{
if (myRowHasErrors)
ConsoleWriteLine(myRow[] + + myRowRowError);
}
}

  // 處理 RowUpdated 事件的方法 如果登記該事件但不處理它
// 則引發一個 SQL 異常
protected static void OnRowUpdated(object sender SqlRowUpdatedEventArgs
args)
{
if (argsRecordsAffected ==
{
argsRowRowError = 遇到開放式並發沖突;
argsStatus = UpdateStatusSkipCurrentRow;
}
}

  當在一個 SQL Server 存儲過程中執行多個 SQL 語句時出於性能原因可以使用 SET NOCOUNT ON 選項此選項將禁止 SQL Server 在每次執行完一條語句時都向客戶端返回一條消息從而可以降低網絡流量然而這樣將不能像前面的代碼示例那樣檢查 RecordsAffected 屬性RecordsAffected 屬性將始終為 另一種方法是在存儲過程中返回 @@ROWCOUNT 函數(或將它指定為一個輸出參數)@@ROWCOUNT 包含了存儲過程中上一條語句完成時的記錄數目並且即使使用了 SET NOCOUNT ON該函數也會被更新因此如果存儲過程中執行的上一條 SQL 語句是實際的 UPDATE 語句並且已經指定 @@ROWCOUNT 作為返回值則可以對應用程序代碼進行如下修改

  

  // 向數據適配器的 UPDATE 命令添加另一個參數來接收返回值
// 可以任意命名該參數
myParm = daUpdateCommandParametersAdd(@RowCount SqlDbTypeInt);
myParmDirection = ParameterDirectionReturnValue;

  // 將 OnRowUpdated 方法修改為檢查該參數的值
// 而不是 RecordsAffected 屬性
protected static void OnRowUpdated(object sender SqlRowUpdatedEventArgs
args)
{
if (argsCommandParameters[@RowCount]Value ==
{
argsRowRowError = 遇到開放式並發沖突;
argsStatus = UpdateStatusSkipCurrentRow;
}
}


COM 互操作性

  如果希望數據訪問邏輯組件類能夠被 COM 客戶端調用則建議按前面所述的原則定義數據存取邏輯組件並提供一個包裝組件然而如果希望 COM 客戶端能夠訪問數據訪問邏輯組件請考慮以下建議
將該類及其成員定義為公共
避免使用靜態成員
在托管代碼中定義事件源接口
提供一個不使用參數的構造函數
不要使用重載的方法而使用多個名稱不同的方法
使用接口公開常用操作
使用屬性為類和成員提供附加 COM 信息
NET 代碼引發的所有異常中包含 HRESULT 值
在方法簽名中使用自動兼容的數據類型
From:http://tw.wingwit.com/Article/program/net/201311/13419.html

    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.