一,什麼是Identity map模式
Identity map是EF獲取和緩存數據的模式。
Identity map模式指的是任何數據都只會被加載一次,以map的形式緩存,以唯一的identity來再次獲取這些數據。
在EF中,就是在一個Context的生命週期中,所有查詢過的數據都會緩存到Context的local中緩存。當再次訪問這些數據的時候,就會以主鍵(identity)從緩存中獲取這些數據。
二,關於Identity map模式的驗證示例
看看下面這段代碼運行的結果:
using (var context = new SchoolContext()) { result1 = context.Students.ToList(); result1[0].Age = -1; result2 = context.Students.ToList(); var s1 = context.Students.First(s => s.Id == 1); var s2 = context.Students.First(s => s.Id == 1); var s3 = context.Students.First(s => s.Id == 3); var s4 = context.Students.First(s => s.Id == 3); Debug.Assert(ReferenceEquals(s1, s2)); Debug.Assert(ReferenceEquals(s3, s4)); }
運行之後,會發現s1和s2是同一個引用,s3和s4也是同一個引用。
原因就是在Identity map模式來說,對於唯一的主鍵,返回的必然是同一個對象。
再來看一個更加有趣的例子
public IEnumerable<Student> GetStudents() { List<Student> result1; List<Student> result2; using (var context = new SchoolContext()) { result1 = context.Students.ToList(); result1[0].Age = -1; result2 = context.Students.ToList(); } return result2; }
實際的數據庫中的result1[0].Age是18,如果修改Age是-1, 再次執行context.Students.ToList(), 返回數據的Age並不是數據庫中的18,而是-1.
但是根據MiniProfiler的監控結果,EF的確訪問了2次數據庫
從這裏,得出的結論是:
Context在一次查詢結束後,得到的數據會保存到本地緩存,在提交之前對數據的修改都是在本地進行。
當再次Qeury的時候,Context中不存在的數據會放到Context中,Context已經存在的數據(即使被修改了),也不會被數據庫的數據覆蓋。
三. Unit of Work 模式
Unit of Work模式指的是:
所有對於context中查詢得到的實體對象的數據修改,都只會在調用SaveChanges方法後,纔會真正的保存到數據庫中。你可以在一個Context的生命週期中,修改多個實體對象的值,然後一次提交保存。
在EF中,由於Unit of Work模式,沒有辦法做選擇性的保存數據,只要是數據發生了改動,都會在SaveChnage方法中一併提交到數據庫中保存。
四,總結和注意問題
結合這兩種模式,可以看出
在一個Context的生命週期中,一個Entity只會有一個實例,任何對該實例的修改,即使這些改動沒有保存到數據庫中,修改都會影響到整個Context的生命週期。
注意問題:
1. 在使用EF的時候,理想的方式應該是 獲取數據-> 修改數據,保存數據->獲取數據……的過程。
不要在修改數據的過程中,再次請求數據,因爲這些數據很可能和數據庫中的數據不一致。
2. 在顯示層,最好使用ViewModel, 而不要直接使用EF中Model.
比如一篇博客文章中,我只想顯示前500個字給非註冊用戶看,如果使用Model, 不小心直接將文章內容的字段修改了,只保留了500個字,然後最後調用了SaveChange,用來保存文章的閱讀次數。
這樣就會導致文章內容被我不小心給丟失了。