天天看點

EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

當我們利用EF Core查詢資料庫時如果我們不顯式關閉變更追蹤的話,此時實體是被追蹤的,關于變更追蹤我們下節再叙。就像我們之前在EF 6.x中讨論的那樣,不建議手動關閉變更追蹤,對于有些特殊情況下,關閉變更追蹤可能會導緻許多問題的發生。

對于EF Core 1.1中依然有四種狀态,有的人說不是有五種狀态麼,UnChanged、Added、Modified、Deleted、Detached。如果我們按照變更追蹤來劃分的話,實際上隻有四種,将Detached排除在外,Detached不會被上下文所追蹤。那麼狀态如何改變的呢?内部有一個IStateManager接口,通過此接口來對實體狀态進行管理,此時再取決于SaveChanges被調用後背後是如何進行處理,我也就稍微看了下源碼,深入的東西沒去過多研究。

Added:實體還未插入到資料庫當中,當調用SaveChanges後将修改其狀态并将實體插入到資料庫。

UnChanged:實體存在資料庫中,但是在用戶端未進行修改,當調用SaveChanges後将忽略。

Modified:實體存在資料庫中,同時實體在用戶端也進行了修改,當調用SaveChanges後将更改其狀态并更新資料持久化到資料庫。

Deleted:實體存在資料庫中,當調用SaveChanges方法後将删除實體。

在EF Core 1.1中依然存在Add、Attach、Update方法,我們通過上下文或者DbSet<TEntity>能夠看到,當将實體傳遞到這些方法中時,它們與實體追蹤可達圖緊密聯系在一起,比如說我們之前讨論的部落格的導航屬性文章的發表,當我們添加文章的發表的這個實體時,然後調用Add方法後此時文章的發表這個實體也就被添加。在EF 6.x中我們說過當我們調用Add等方法時EF内部機制将會自動調用DetectChanges,但是在EF Core 1.1中則不再調用DetectChanges方法。空口無憑,我下載下傳了源碼,如下:

上述我們沒有看到任何自動調用DetectChanges的邏輯,在EF 6.x中我們講到當調用SaveChanges時此時會回調DetectChanges,而在EF Core 1.1中同樣也是如此,是以相對于EF 6.x而言,EF Core 1.1隻是在SaveChanges時回調DetectChanges,在Add、Attacth、Update等方法則不再回調DetectChanges,這樣的話性能就會好很多。我們看到源代碼中調用SaveChanges時邏輯如下:

接下來我們再來看看當調用Add、Update等方法時到底發生了什麼。

Add:當調用Add方法時就沒什麼可說的了,此時将在圖中的對應的所有實體推入到Added狀态,也就說在調用SaveChanges時将會插入到資料庫中去。

Attach:當調用Attach方法時将在圖中的所有實體推入到UnChanged狀态,但是有一個額外情況,比如我們在一個類中添加導航屬性資料時,此時Attach的話将會使用混合模式,将此實體的狀态為UnChanged而導航屬性的狀态則是Added狀态,是以當插入到資料庫中時,這個已存在的資料将不會被儲存,隻有新添加的導航屬性資料才會被插入到資料庫中去。

Update:Update方法和Attach方法一樣隻是将其狀态修改為Modified,而将新添加的實體的修改将進行插入。

Remove:當調用Remove方法時此時它隻會影響傳遞給該方法的實體,不會去周遊實體的可到達圖。如果一個實體的狀态是UnChanged或者Modified,說明該實體已存在資料庫中,此時隻需将其狀态修改為Deleted。如果實體的狀态為Added,此時說明該實體在資料庫中不存在,此時會脫離上下文而不被跟蹤。是以Remove方法側重強調實體要被追蹤,否則的話需要首先被Attach然後将其推入到Deleted狀态。

在EF Core 1.1中多了AddRanges、UpdateRanges等方法,它們和實際調用多次調用非Range方法其實是一樣的,它内部也會去周遊實體集合并更新其狀态,如下:

我們再看SetEntityStates這個方法的實作。

EF Core内部機制的處理肯定比我們之前手動去周遊添加實體集合性能要高,意外看到一篇文章上有說僅僅隻高效一點,因為Range方法自動調用DetectChanges方法,找了半天也沒看見在哪裡調用DetectChanges,郁悶,算是一點疑惑吧。

【注意】EF團隊之前一直在承諾EF Core會更高效和更高可擴充,但是我閱讀源碼發現内部還是自動調用了DetectChanges,性能方面的話還是不算太高效,但是,但是源碼中已經明确給出,關于DetectChanges方法,未來對于這個api會進行更改或者徹底移除,源碼注釋如下:

對于變更追蹤也好,預設啟用變更追蹤也好,我們都是通過ChangeTracker屬性來擷取到,如下:

在ChangeTracker中也有一個重要的方法那就是如下:

我們暫且起名為跟蹤圖吧,它是對實體狀态的完全控制,比如我們在将資料插入到資料庫之前想設定其某一個值為臨時值,我們就可以通過該方法來實作。

在EF Core 1.1其餘的就是關于Add、Update等方法的異步操作了,對于操作資料庫不至于阻塞的情況也還是挺好的。

關于EF Core 1.1中一些基本的知識我們過了一遍,下面我們來看看這些方法到底該如何高效使用呢?

關于這個方法就沒有太多叙述的了,對應的則是異步方法。我們重點看看其他的方法。

當我們根據主鍵去更新所有實體這個so easy了,我們在Blog表添加如下資料。

EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

(1)更新方式一

現在我們查出Id=1的實體,然後将Name進行修改如下:

上述我們直接查詢出來主鍵對應的實體然後修改其值,最後送出更新其實體的對應修改的屬性。最後順理成章的資料字段進行了修改

EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用詳解

我們知道因為查詢出來的實體在未關閉變更追蹤的情況下始終都是被追蹤的,是以必須進行對應修改,但是要是下面的情況呢。

在用戶端對資料進行了修改,我們需要根據主鍵Id進行對應屬性修改,當然不希望多此一舉的話,我們可以根據主鍵Id去查詢對應的實體,然後将屬性進行指派最後送出修改儲存到資料庫中,大概就演變成如下情況。

誠然上述方法能達到我的目的,其實還有簡便的方法,如下:

(2)更新方式二

既然有簡單的方法為何我們不用呢,這樣的場景就是更新指定屬性,以往的情況都是自己封裝一個Update方法,然後利用反射去包含需要修改的屬性接着更改其屬性的狀态為修改,最後送出修改即可。是的,這就是我們說的方法,但是,但是在EF Core 1.1中完全不需要我們去封裝,我們需要做的隻是封裝成一個通用方法即可,内置實作EF Core已經幫我們實作,我們來看看。

很熟悉吧,我們在基倉儲接口給出這樣一個接口,接着我們來實作此接口,如下:

是不是夠簡單粗暴,開源就是好啊,查找資料時發現老外已經給出了具體實作,當直接調用時居然發現已經給我們封裝了,接下來我們再來修改指定的屬性就變成了如下:

上述隻是示範,實際項目當中時我們隻需給出我們修改的主鍵和實體即可。如果是修改實體集合的話,再重載一個周遊就ok。到這裡你是不是發現已經非常完美了,還有更完美的解決方案,請繼續往下看。

(3)更新方式三

其實在ASP.NET Core MVC中有比上面進一步還爽的方式通過利用TryUpdateModelAsync方法來實作,此方法有多個重載來實作,完全不需要我們去封裝。如下:

上述三種更新方式各有其應用場景,如果必須要總結的話就主要是第二種方式和第三種方式該如何取舍,第二種方式通過我們手動封裝的方式不需要再進行查詢,直接更改其狀态進行送出更新即可,而第三種方式需要進行查詢才會被追蹤最終送出更新,看個人覺得哪種方式更加合适就取哪種吧。關于EF Core 1.1中對于資料更新我們就講解完了,我們再來看看删除。

對于上述和更新一樣如果該實體已經被變更追蹤,直接調用内置的方法Delete方法即可,大部分場景下是根據主鍵去删除資料。這裡有兩種方式供我們選擇,請往下看。

(1)删除方式一

我們查詢出需要删除的實體,然後通過調用Remove(這裡我封裝了)方法将其辨別為Deleted狀态進行删除,當查詢資料我們可以關閉變更追蹤,一來資料量大的話對記憶體壓力不會太大,二來因為調用Remove方法會将其辨別為Deleted狀态也會被追蹤,不會有任何問題。

(2)删除方式二【推薦】

為了盡量減少請求時間,我們能一步完成的何必要用兩步呢,我們完全可以直接執行個體化一個實體,将其主鍵指派,最後修改其狀态為Deleted,最終将持久化到資料庫中删除對應的資料。如下:

最後還剩下一個查詢沒有講述,這個和添加方法一樣,比較簡單我們稍微過一下即可。由于在EF Core中不再支援延遲加載,是以我們需要通過Include顯式擷取我們需要的導航屬性,比如如下:

如果有多個導航屬性,我們接着進行ThenInclude,如下:

為了避免這樣多次ThenInclude,友善調用我們進行如下封裝即可:

此時我們隻需要進行對應調用即可,大概如下:

本文轉自xsster51CTO部落格,原文連結:http://blog.51cto.com/12945177/1932189 ,如需轉載請自行聯系原作者