程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 數據點 - 兵不血刃拒絕對Entity Framework的表訪問

數據點 - 兵不血刃拒絕對Entity Framework的表訪問

編輯:關於.NET

在看到實際的 Entity Framework 命令創建之後,數據庫所有者的第一反應往 往是:“什麼? 我必須提供對表的訪問權?”他們有這種反應是因為 Entity Framework 的核心功能之一便是生成 SELECT、UPDATE、INSERT 和 DELETE 等命令。

在本專欄中,我將帶領數據庫管理員了解一下 Entity Framework 如何生成命 令,然後介紹一些功能,您可以利用這些功能只允許 Entity Framework 使用視 圖與存儲過程,從而限制 Entity Framework 對您數據庫的訪問。 而與此同時, 您不會對應用程序代碼產生任何影響,也不會疏遠與團隊中開發人員之間的關系 。

認識默認命令生成

這一命令生成過程是如何實現的? Entity Framework 的要點是實體數據模型 (EDM),一個用於描述應用程序域對象的概念 模型。 Entity Framework 讓開發人員可以針對實體數據模型提出查詢,而不必 操心數據庫的具體操作。 實體數據模型的實體以及實體之間的關系以 XML 形式 定義,而開發人員基於該模型的實體來處理強類型化類。 Entity Framework 運 行時使用實體數據模型的 XML 以及其他元數據(用於描述數據庫架構以及從實體 數據模型到數據庫架構的映射關系)來溝通類與數據庫(參見圖 1)。

數據點:兵不血刃拒絕對 Entity Framework
的表訪問

圖 1 Entity Framework 運行時元數據用於生成數據庫命令

在運行時,利用特定於數據庫的 ADO.NET 提供程序,Entity Framework 將針 對實體數據模型而創建的查詢轉換為存儲查詢(例如 T-SQL),然後送至數據庫 。 Entity Framework 將查詢結果轉換為由強類型化實體類所定義的對象,如圖 2 所示。

圖 2 Entity Framework 執行查詢並處理查詢結果

在用戶處理這些對象的時候,Entity Framework 利用標識鍵跟蹤屬性以及對 象之間的關系所發生的更改。 最後,當代碼調用 Entity Framework SaveChanges 方法,從而在數據庫中永久保存更改時,Entity Framework 運行時 會讀取自己采集的所有更改跟蹤信息。 對於每個修改、添加或刪除的實體, Entity Framework 會再次讀取模型,並讓提供程序生成存儲命令,然後在一次可 逆事務中對數據庫執行這些命令。

這一段關於 Entity Framework 默認行為的描述往往會讓數據庫所有者發瘋, 但在這裡我想要強調“默認”這個詞。 Entity Framework 有許多可以改變的默 認行為。

Entity Framework 對於數據檢索請求或數據保存請求的處理方式就是這樣一 個可以修改的行為。 您不必建立依賴於 Entity Framework 的模型便能訪問您的 數據表。 您可以建立一個只知道數據庫視圖與存儲過程的模型,而不影響使用該 模型的應用程序代碼。 通過結合 Entity Framework 的存儲過程支持與其數據庫 視圖支持,您能夠以存儲過程和視圖為基礎實現所有數據庫交互。

將實體映射到數據庫視圖,而非表

建立模型有多種方法。 我將重點討論通過對舊式數據庫實施反向工程處理而 建立的模型。 對於這一過程,Visual Studio 提供了一個向導。

在此向導中,用戶可以選擇數據庫表、視圖和存儲過程。 存儲過程部分還會 列出可以放入模型的、用戶定義的標量值函數。

通常,開發人員將會選擇表,並讓向導根據這些表創建實體。 在之前討論的 更改跟蹤與 SaveChanges 過程中,Entity Framework 自動為基於表的實體生成 INSERT、UPDATE 與 DELETE 等命令。

我們先來看看如何強制 Entity Framework 針對視圖而非表來執行查詢。

放入模型的數據庫視圖也會成為實體。 Entity Framework 跟蹤這些實體所發 生的更改,就像它跟蹤映射到表的實體那樣。 使用視圖的時候,關於標識鍵有一 點需要注意。 數據庫表可能會有一個或更多被標為表主鍵的列。 默認情況下, 向導會根據表的主鍵創建實體的標識鍵。 在創建映射到視圖(沒有主鍵)的實體 的時候,向導會根據表中所有不可為空的值創建一個組合鍵,從而盡力推斷該標 識鍵。 假設有一個視圖有四個不可為空的列,即ContactID、FirstName、 LastName 和 TimeStamp,然後根據這個視圖創建一個實體。

所生成的四個屬性將被標為 EntityKey(設計器利用鍵圖標標示 EntityKey 屬性),這表示該實體有一個由這四個屬性組成的 EntityKey。

我們只需要 ContactID 這個屬性來唯一標識此實體。 因此,在模型創建完成 之後,您可以利用設計器將其他三個屬性的 EntityKey 屬性更改為 False,從而 只將 ContactID 設為指定的 EntityKey。

或者(如有可能),您還可以事先規劃好,讓設計出的數據庫視圖提供正確的 、不可為空的列。

確定鍵之後,Entity Framework 可以唯一標識每個實體,因而能對這些實體 執行更改跟蹤,然後在調用 SaveChanges 的時候將更改永久保存到數據庫中。

用您自己的存儲過程取代命令生成

對於將更改永久保存到數據庫中的操作,您可以覆蓋默認命令生成,在需要永 久保存到數據庫的時候讓 Entity Framework 使用您自己的 Insert、Update 和 Delete 存儲過程。 此舉稱為“存儲過程映射”。讓我們來看看這是如何實現的 。

您在 EDM 向導(或之後在更新向導)中選定應放入模型的任何存儲過程,都 會變成模型 XML 元數據中用於描述數據庫架構的那個部分中的一個函數。 它不 會自動成為概念模型的一個組成部分,在設計圖面中不會有任何表示。

下面是我的某個數據庫中 Person 表的一個簡單的 Insert 存儲過程。

ALTER procedure [dbo].[InsertPerson]
       @FirstName nchar(50),
       @LastName nchar(50),
       @Title nchar(50)
AS
INSERT INTO [Entity FrameworkWorkshop].[dbo].[Person]
       ([FirstName]
       ,[LastName]
       ,[Title]      )
    VALUES
(@FirstName,@LastName,@Title)
SELECT @@IDENTITY as PersonID

此存儲過程不僅執行數據庫插入操作,隨後它還返回 SQL Server 為新行創建 的主鍵值。

如果您在向導中選擇此過程,那麼它在模型的數據庫架構中將表示為以下函數 :

<Function Name="InsertPerson" Aggregate="false"  BuiltIn="false"
  NiladicFunction="false" IsComposable="false"
  ParameterTypeSemantics="AllowImplicitConversion"  Schema="dbo">
  <Parameter Name="FirstName" Type="nchar" Mode="In" />
  <Parameter Name="LastName" Type="nchar" Mode="In" />
  <Parameter Name="Title" Type="nchar" Mode="In" />
</Function>

然後您可以使用設計器的“映射詳細信息”窗口將這個 InsertPerson 函數映 射到根據 Person 表創建的 Person 實體,如圖 3 所示。

圖 3 將存儲過程映射到實體

請注意,在圖 3 中,PersonID 屬性映射到存儲過程的返回值。 這一映射將 會導致在數據庫中執行插入操作之後,Entity Framework 便會立即利用數據庫生 成的鍵來更新內存中的 Person 對象。

映射函數時的一個重要要求是:函數中的每個參數必須映射到實體中的某個屬 性。 不允許將某個公式或值映射到參數。 然而,開發人員有許多機會自定義表 示這些實體的 Microsoft .NET Framework 類。

您也可以映射 Update 函數和 Delete 函數。 雖然不是必須映射所有三個操 作(Insert、Update 和 Delete),但開發人員必須注意文檔中所述有關只映射 部分函數的某些規則。

在圖 3 中,請注意在屬性的右側還有兩列(因為列寬的原因而縮略顯示): “使用原始值”和“影響的行數”。 Entity Framework 支持最優並發,而您可 以使用這些屬性對 Update 函數和 Delete 函數提供並發檢查。 

在運行時,如有用戶創建了一個新的 Person 類型,並且隨後觸發了 SaveChanges 方法,Entity Framework 則會在元數據中看到 Insert 函數映射( 基於在圖 3 中定義的映射)。 它將發出下列命令以執行存儲過程,而不會動態 生成自己的 INSERT 命令:

exec [dbo].[InsertPerson]  @FirstName=N'Julie',@LastName=N'Lerman',
@Title=N'Ms.'

彌補隔閡以及避免 Entity Framework 訪問表

Entity Framework 將會生成命令以永久保存基於視圖的各實體所返回的數據 ,但視圖可能無法更新。 如果視圖無法更新,您可以將 Insert、Update 和 Delete 存儲過程映射到實體,並能完成完整的數據檢索與數據永久保存操作,而 不必提供對數據庫表的直接訪問權限。

您可以只創建與表相匹配的數據庫視圖,也可以只創建用於更新表列的存儲過 程。 您也可以擁有更為復雜的視圖,以及包含用以執行更新操作的高級邏輯的復 雜存儲過程。 您甚至可以將您的某些讀取存儲過程替換為視圖,讓開發人員能夠 針對視圖創建查詢,而這是存儲過程無法做到的。

關於這一查詢創建功能的示例是,應用程序可以針對 CustomersInPastYear 實體提出一個查詢請求,從而利用客戶的 LastName 屬性進一步篩選視圖:

from c in context.CustomersInPastYearS
  where c.LastName.StartsWith("B")
  select c;

這樣,在數據庫中便會執行下列命令:

SELECT
[Extent1].[CustomerID] AS [CustomerID], [Extent1].[FirstName] AS  [FirstName],
[Extent1].[LastName] AS [LastName], [Extent1].[EmailAddress] AS  [EmailAddress],
[Extent1].[TimeStamp] AS [TimeStamp]
FROM (SELECT
    [CustomersInPastYear].[CustomerID] AS [CustomerID],
    [CustomersInPastYear].[FirstName] AS [FirstName],
    [CustomersInPastYear].[LastName] AS [LastName],
    [CustomersInPastYear].[EmailAddress] AS [EmailAddress],
    [CustomersInPastYear].[TimeStamp] AS [TimeStamp]
    FROM [dbo].[CustomersInPastYear] AS [CustomersInPastYear])  AS [Extent1]
WHERE [Extent1].[LastName] LIKE N'B%'

.NET 編譯器會接受針對某個存儲過程(已映射到模型中)而創建的類似查詢 。 然而,Entity Framework 會對數據庫執行該存儲過程,將所有結果返回給應 用程序,然後將篩選器應用於內存中由存儲過程所返回的對象。 這樣便有可能造 成資源浪費以及對性能造成不利影響,而不為開發人員所知曉。

圖 4 中是一個存儲過程,它利用參與了 Customers­InPastYear 視圖的相同 列更新 Customer 表。 這個存儲過程可以充當 CustomersInPastYear 實體的 Update 函數。

圖 4 UpdateCustomerFirstNameLastNameEmail 存儲過程

ALTER PROCEDURE UpdateCustomerFirstNameLastNameEmail
@FirstName nvarchar(50),
@LastName nvarchar(50),
@Email nvarchar(50),
@CustomerId int,
@TimeStamp timestamp

AS

UPDATE Customer
   SET [FirstName] = @FirstName
    ,[LastName] = @LastName
    ,[EmailAddress] = @Email
  WHERE CustomerID=@CustomerId AND TimeStamp=@TimeStamp

  SELECT TimeStamp
  FROM Customer
  WHERE CustomerID=@CustomerId

現在,您可以將此存儲過程映射到實體。 圖 5 中的映射將初始 TimeStamp 送至存儲過程,然後利用“結果列綁定”捕獲存儲過程所返回的經過更新的 TimeStamp。

圖 5 將存儲過程映射到基於視圖的實體

總而言之,只要模型設計得當,基於視圖的實體有相應的標識鍵,並且函數得 到正確映射,就不需要對利用 Entity Framework 實現數據訪問策略的應用程序 公開數據庫表。 數據庫視圖和存儲過程可以為 EDM 以及 Entity Framework 提 供它們要與您的數據庫成功進行交互所需要的所有條件。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved