在Entity Framework中使用儲存過程

wangsys發表於2021-09-09

本篇文章透過例項的方式,討論兩個在EF使用儲存過程的主題:如何透過實體和儲存過程的對映實現邏輯刪除;對於具有自增長型別主鍵的資料表,在進行新增操作的時候如何將正確的值反映在實體物件上。

目錄      
一、基於邏輯刪除的資料表和儲存過程定義      
二、如何過濾邏輯刪除記錄      
三、具有自增長列的儲存過程定義      
四、透過Result Columns Binding將結果集的列於實體屬性進行繫結

一、基於邏輯刪除的資料表和儲存過程定義

較之物理刪除(記錄徹底從資料表中清除掉),邏輯刪除則繼續保留該資料,只是為之進行一個刪除標記,表明該記錄已經被“刪除”了。比如透過下面的SQL,我建立了一個簡單的表T_CONTACT表,其中BIT型別的欄位IS_DELETED就為這個“刪除標記”。

   1: CREATE TABLE T_CONTACT

   

   2: (

   

   3:  [ID]             VARCHAR(50)     PRIMARY KEY,

   

   4:  [NAME]           NVARCHAR(50)    NOT NULL,

   

   5:  [IS_DELETED]     BIT             NOT NULL

   

   6: )

那麼當我們進行刪除操作的儲存過程中,不是就行Delete操作,而是進行Update操作,將IS_DELETED的值設定成1即可,這樣的儲存過程定義如下:

   1: CREATE PROCEDURE P_CONTACT_D

   

   2: (@p_id VARCHAR(50))

   

   3: AS

   

   4: BEGIN

   

   5:     UPDATE    T_CONTACT

   

   6:     SET       IS_DELETED = 1

   

   7:     WHERE     ID = @p_id

   

   8: END


二、如何過濾邏輯刪除記錄

開啟VS,透過匯入該資料表和CUD儲存過程建立.edmx模型,同時修改概念模型實體名稱(比如T_CONTACT改成Contact)和屬性名稱。並刪除屬性IS_DELETED,最終得到如右圖所示的.edmx模型。然後為Contact實體對映CUD儲存過程和相關引數,其中刪除操作的儲存過程已經定義在上面。

然後,你需要考慮這樣一個問題:由於我們進行的是邏輯刪除,被“刪除”的記錄依然儲存於資料庫中。當你進行資料查詢的時候,如果沒有顯式設定IS_DELETED=0為篩選條件的情況下,所有被“刪除”的記錄依然會被返回。進一步地講,由於我們在.edmx模型的概念實體Contact中,已經將IS_DELETED刪除掉了,所以我們在程式中不可能設定這樣一個額外的篩選條件。

實際上EF為你考慮到了這一點,你可以在直接透過EF設計器設定這樣一個篩選條件。在當前實體被選中的情況下,進入Mapping Details介面,你會發現在於資料庫表的對映中具有一個的下拉框,透過該下拉框你可以設定基於資料庫表相關列的篩選條件。如下圖所示,我設定了篩選條件“IS_DELETED = 0”來過濾掉被邏輯刪除的記錄。

基於上面的設定編寫如下的程式碼,先新增3條Contact記錄,然後將它們刪除。並在刪除前後根據ID獲取對應記錄,列印出來以驗證上面設計的篩選條件是否真的有效。

   1: static void Main(string[] args)

   

   2: {

   

   3:     string[] contractIds = new string[] {

   

   4:         Guid.NewGuid().ToString(),

   

   5:         Guid.NewGuid().ToString(),

   

   6:         Guid.NewGuid().ToString() };

   

   7:     using (EFExtensionsEntities context = new EFExtensionsEntities())

   

   8:     {

   

   9:         Contact contact1 = Contact.CreateContact(contractIds[0], "Zhang San");

   

  10:         Contact contact2 = Contact.CreateContact(contractIds[1], "Li Si");

   

  11:         Contact contact3 = Contact.CreateContact(contractIds[2], "Wang Wu");

   

  12:         context.Contacts.AddObject(contact1);

   

  13:         context.Contacts.AddObject(contact2);

   

  14:         context.Contacts.AddObject(contact3);

   

  15:         context.SaveChanges();

   

  16:

   

  17:         Console.WriteLine("Before Delete...");

   

  18:         foreach(var contact in context.Contacts.Where(c=>contractIds.Contains(c.ID)))

   

  19:         {

   

  20:             Console.WriteLine("{0}: {1}", contact.ID, contact.Name);

   

  21:         }

   

  22:         foreach (var contact in context.Contacts.Where(c => contractIds.Contains(c.ID)))

   

  23:         {

   

  24:             context.Contacts.DeleteObject(contact);

   

  25:         }

   

  26:         context.SaveChanges();

   

  27:

   

  28:         Console.WriteLine("After Delete...");

   

  29:         foreach (var contact in context.Contacts.Where(c => contractIds.Contains(c.ID)))

   

  30:         {

   

  31:             Console.WriteLine("{0}: {1}", contact.ID, contact.Name);

   

  32:         }

   

  33:     }

   

  34: }

下面是輸出結果,可見被刪除的記錄真的不曾出現在查詢結果中。

   1: Before Delete...

   

   2: 4032d301-80cb-4e6d-a3e7-f5560e918b4a: Li Si

   

   3: 69b8bdbb-4714-4d68-9619-f4cd587c37ef: Zhang San

   

   4: dbadfef9-d6d2-466b-8eae-392f1d731c14: Wang Wu

   

   5: After Delete...

實際上在資料庫中,這三條資料依然存在,只是邏輯刪除標識欄位IS_DELETED被標記為1。

三、具有自增長列的儲存過程定義

接下來我們來討論另一個常見的場景:如果一個表中存在一個自增長列作為該表的主鍵,當我們透過提交對應的實體物件進行記錄新增操作時,資料庫中真正的鍵值如何返回並賦值給該實體物件。為了模擬這個場景,我重新定義了資料表T_CONTACT的定義,將ID列定義成自增長列。建立該表對應的DDL如下所示:

   1: CREATE TABLE T_CONTACT

   

   2: (

   

   3:     [ID]            INT IDENTITY(1,1)    PRIMARY KEY,

   

   4:     [NAME]          NVARCHAR(50)         NOT NULL,

   

   5:     [IS_DELETED]    BIT                  NOT NULL

   

   6: )

如果你希望真正的ID能夠返回給被新增的Contact物件,在儲存過程中完成新增操作後,應該透過SELECT語句將對應的真實ID返回,這樣的儲存過程應該這樣來寫:

   1: CREATE PROCEDURE [P_CONTACT_I]

   

   2:  @p_name NVARCHAR(50)

   

   3: AS

   

   4: BEGIN

   

   5:     INSERT    INTO T_CONTACT(NAME, IS_DELETED)

   

   6:     VALUES(    @p_name, 0)

   

   7:

   

   8:     SELECT [ID]

   

   9:     FROM T_CONTACT

   

  10:     WHERE [ID] = SCOPE_IDENTITY()

   

  11: END


四、透過Result Columns Binding將結果集的列於實體屬性進行繫結

在.edmx模型的設計器中,點選右鍵並再上下文選單中選擇"Update Model From Database…”,讓VS重新載入我們修改過的儲存過程,然後你需要對儲存過程對映關係進行重新設定。由於ID的資料型別改變了,你需要修正Update和Delete儲存過程,並改變Contact的ID屬性的資料型別從String程式設計Int32。

為了讓儲存過程中SELECT語句返回的結果集體現在被提交的Contact物件上,你需要設定列名(或者透過AS運算子設定的別名)與實體型別的屬性之間的對映關係。這個關係的定義包含在儲存過程對映的Result Columns Binding列表中。如下圖所示,我設定了儲存過程返回列ID和Contact屬性ID之間的對映關係。

基於最新的.edmx模型,我們編寫如下的程式碼,分別建立三個Contact記錄。從最終的執行結果,我們可以清晰地看到,從資料庫中返回的真實ID反映在了被新增的Contact物件上了。

   1: static void Main(string[] args)

   

   2: {

   

   3:     using (EFExtensionsEntities context = new EFExtensionsEntities())

   

   4:     {

   

   5:         Contact contact = new Contact { Name = "Zhang San" };

   

   6:         context.Contacts.AddObject(contact);

   

   7:         context.SaveChanges();

   

   8:         Console.WriteLine("{0}: {1}", contact.ID, contact.Name);

   

   9:

   

  10:         contact = new Contact { Name = "Li Si" };

   

  11:         context.Contacts.AddObject(contact);

   

  12:         context.SaveChanges();

   

  13:         Console.WriteLine("{0}: {1}", contact.ID, contact.Name);

   

  14:

   

  15:         contact = new Contact { Name = "Wang Wu" };

   

  16:         context.Contacts.AddObject(contact);

   

  17:         context.SaveChanges();

   

  18:         Console.WriteLine("{0}: {1}", contact.ID, contact.Name);

   

  19:     }

   

  20: }

執行結果:

   1: 10: Zhang San

   

   2: 11: Li Si

   

   3: 12: Wang Wu


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3244/viewspace-2806438/,如需轉載,請註明出處,否則將追究法律責任。

相關文章