閱讀42 返回首頁    go iPhone_iPad_Mac_手機_平板_蘋果apple


在Entity Framework中使用存儲過程(三):邏輯刪除的實現與自增長列值返回

本篇文章通過實例的方式,討論兩個在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 

image打開VS,通過導入該數據表和CUD存儲過程創建.edmx模型,同時修改概念模型實體名稱(比如T_CONTACT改成Contact)和屬性名稱。並刪除屬性IS_DELETED,最終得到如右圖所示的.edmx模型。然後為Contact實體映射CUD存儲過程和相關參數,其中刪除操作的存儲過程已經定義在上麵。

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

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

image

基於上麵的設置編寫如下的代碼,先添加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。

image

接下來我們來討論另一個常見的場景:如果一個表中存在一個自增長列作為該表的主鍵,當我們通過提交對應的實體對象進行記錄添加操作時,數據庫中真正的鍵值如何返回並賦值給該實體對象。為了模擬這個場景,我重新定義了數據表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 

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

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

image

基於最新的.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

在Entity Framework中使用存儲過程(一):實現存儲過程的自動映射
在Entity Framework中使用存儲過程(二):具有繼承關係實體的存儲過程如何定義?
在Entity Framework中使用存儲過程(三):邏輯刪除的實現與自增長列值返回
在Entity Framework中使用存儲過程(四):如何為Delete存儲過程參數賦上Current值?
在Entity Framework中使用存儲過程(五):如何通過存儲過程維護多對多關係?


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-27 12:03:39

  上一篇:go  Percona Server 5.7有哪些性能提升?
  下一篇:go  在Entity Framework中使用存儲過程(四):如何為Delete存儲過程參數賦上Current值?