原博文
http://www.cnblogs.com/fly_dragon/archive/2011/06/05/2073084.html
ObjectContext的處理機制
ObjectContext是Entity Framework封裝了數據庫訪問的上下文,以及實體的映射關系元數據信息等。
EF幫我們封裝好了這么一個統一的接口。讓我們所有的操作都只通過這個一個實體上下文就可以實現了增刪查改等所有對應數據庫的操作。
當然,我們要了解EF的生成SQL的機制我們才能更好的使用EF幫我們生成效率更高的SQL腳本。
看一個實例:下圖所示項目截圖與實體模型圖(一個簡單的例子)
static void Main(string[] args) { SchoolDBEntities schoolDB = new SchoolDBEntities(); Student student = new Student(); student.Address = "北京上地"; student.Name = "飛龍"; student.Phone = "110"; schoolDB.Student.AddObject(student); schoolDB.SaveChanges(); }
就是簡單的網數據庫中添加一條數據。
我們在上面代碼紅色背景地方加上斷點【schoolDB是EF自動幫我們生成的繼承自ObjectContext的上下文】並對schoolDB進行快速監視。
截圖如下:
由於圖篇幅有限,只截取了部分視圖。在此我就簡單介紹一下幾個比較關鍵的屬性。
(1):Connection,相信大家一下子就能猜到,當然它封裝了EF連接數據庫的XxxConnection(如:SqlConnection)。這個就不啰嗦了。
(2):ObjectStateManage,它職責是維護實體類型實例和關系實例的對象狀態和標識管理。也是EF上下文中非常重要的一個屬性。它幫我們把添加的實體放到添加隊列里,把修改的實體放到修改的隊列里,當然還有刪除等的。每個實體做了修改時,EF幫我們把實體放到相應的隊列中並修改相應的實體的狀態(EntityState),當調用ObjectContext的SaveChanges()方法時,EF根據隊列的情況以及EDMX元數據映射的信息生成最終的SQL腳本。
(3):上圖中我們看到_addedEntityStore就是我們剛才所說的添加實體的一個集合,而且這里面的每個實體對應的EntityState(標志實體在內存中的狀態,是個Enum類型)都是Added狀態,當然這表示添加,最終生成SQL時是Insert Into... 當然還有刪除的隊列、修改的隊列這個大家自己看一下就可以了。
(4):EntityState,實體對象的狀態。標志我們開發人員對實體的相應的操作,如下表格是實體的相關狀態以及說明(摘自MSDN)
成員名稱 | 說明 |
---|---|
Detached | 對象存在,但沒有被跟蹤。 在創建實體之后、但將其添加到對象上下文之前,該實體處於此狀態。 An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded by using a NoTrackingMergeOption. 沒有 ObjectStateEntry 實例與狀態為 Detached 的對象關聯。 |
Unchanged | 自對象附加到上下文中后,或自上次調用 SaveChanges 方法后,此對象尚未經過修改。 |
Added | 對象為新對象,並且已添加到對象上下文,但尚未調用 SaveChanges 方法。 在保存更改后,對象狀態將更改為 Unchanged。 狀態為 Added 的對象在 ObjectStateEntry 中沒有原始值。 |
Deleted | 對象已從對象上下文中刪除。 在保存更改后,對象狀態將更改為 Detached。 |
Modified | 對象上的一個標量屬性已更改,但尚未調用 SaveChanges 方法。 在不帶更改跟蹤代理的 POCO 實體中,調用 DetectChanges 方法時,已修改屬性的狀態將更改為 Modified。 在保存更改后,對象狀態將更改為 Unchanged。 |
注:
對象上下文必須知道對象狀態才能將更改保存回數據源。 ObjectStateEntry 對象存儲 EntityState 信息。 ObjectContext 的 SaveChanges 方法根據每個對象的 EntityState 處理附加到上下文的實體和更新數據源。 有關更多信息,請參見Adding, Modifying, and Deleting Objects。
對象上下文中的對象狀態由 ObjectStateManager 管理。 若要查找對象的狀態,請調用以下 ObjectStateManager 方法之一:TryGetObjectStateEntry、GetObjectStateEntry 或GetObjectStateEntries。 ObjectStateEntry 的 State 屬性定義該對象的狀態。
總結:
EF是通過針對開發人員對實體做的修改,直接維護ObjectContext的實例中的實體操作集合並對單個實體對應的狀態進行修改。最終根據此集合以及狀態再加上表實體映射的元數據信息生成最終的SQL腳本。所以,我們在對應多個ObjectContext實例進行操作時要注意,調用實例自己的SaveChanges()方法時,它只會對自己實例內存空間的操作映射回數據庫,而其他ObjectContext實例中的實體集合的修改都不受影響。而且EF自動幫我們做了緩存的處理,當我們第一次查詢某個實體時它會自動幫我們從數據庫取出數據,並裝配成實體類交給我們開發人員,當第二次獲取相同數據時,它會先從緩存中查找,如果已經存在數據了就立即返回,不會查詢數據庫。這就造成了一個問題,當ObjectContext實例如果一直不被銷毀,那它的緩存會一直膨脹下去,所以在開發應用時,用單例直接處理EF的上下文也不是很合適。最好的方式應該是 在一次處理請求中(web開發)使用同一個ObjectContext實例即可,避免了多個上下文實例的維護,而且也不至於上下文實例日益膨脹。
EF實體中的修改
說到現在才進入正題,那我們怎么來進行修改呢?
不推薦方式一:
思路:先從ObjectContext取出實體,然后將前台傳過來的DTO屬性對應賦值到我們的實體上,然后調用ObjectContext的保證修改方法。
但是這種方式是最不提倡的,因為這樣每次修改前都得先將數據查出來,經過SqlProfiler追蹤,這么一個操作要對數據庫進行兩次的連接。這是不可忍受的!
推薦方式二:
思路:無需先查出實體,因為我們知道EF通過ObjectStateManage來控制添加、修改、刪除隊列以及實體的狀態,我們所有可以通過在直接將DTO轉化成實體,然后將實體對應的隊列中,並且我們手動的將實體的狀態處理好,再調用ObjectContext的保證修改方法,這樣就避免了先查詢后修改,兩次數據庫連接的問題了。實例代碼如下:
static void Main(string[] args) { SchoolDBEntities schoolDB = new SchoolDBEntities(); //假設:網絡傳一個StudentDTO過來 ,將此DTO轉化成 數據庫實體 Student student = new Student(); student.Id = 1; // 假設DTO傳過來的值,主鍵必須存在,不然會報錯的 student.Address = "北京上地1"; student.Name = "飛龍1"; student.Phone = "1101"; //先將實體附加到實體上下文中 schoolDB.Student.Attach(student); //手動修改實體的狀態 schoolDB.ObjectStateManager.ChangeObjectState(student, EntityState.Modified); //保存回數據庫 schoolDB.SaveChanges(); }
總算把這塊描述完了!希望對初學者有用!歡迎高手指正錯誤!