ObjectStateManager – 對象狀態管理器
我們已經知道如何增加、更新和刪除實體記錄,並將更改數據庫記錄。Entity Framework 通過Object Context 控制的ObjectStateManager 對象來跟蹤變更,ObjectStateManager 將跟蹤所有對實體對象的變更,在調用SaveChanges() 方法時,執行相應的T-SQL腳本。
ObjectStateManager比LINQ to SQL中DataContext 的變更跟蹤功能更先進。下面,我們詳細了解如何顯示有用的變更跟蹤信息。
ObjectStateManager 有2個有趣的方法:
GetObjectStateEntries() – 返回給定狀態下ObjectStateEntry 集合對象。
GetObjectStateEntry() – 返回給定實體的ObjectStateEntry 對象。
一個ObjectStateEntry 對象包含了一些有用的數據,根據狀態不同(Added, Modified, Deleted)有不同的變化。
在下面的示例中,首先檢索特定的Product記錄,修改其中3個屬性,並調用GetObjectStateEntries(EntityState.Modified) 方法,返回所有更新實體的列表,並進一步遍歷ObjectStateEntry集合,顯示實體名稱,Key/Value,初始值和當前值。
示例代碼如下:
Product product1 = context.Product.FirstOrDefault(p => p.ProductID == 1004);
if (product1 != null)
{
product1.Color = "Black";
product1.StandardCost = 20;
product1.ListPrice = 25;
}
var objectStateEntries = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in objectStateEntries)
{
Console.WriteLine("{0} - {1} - {2}",
entry.EntityKey.EntityContainerName,
entry.EntityKey.EntitySetName.ToString(),
entry.EntityKey.EntityKeyValues.First().Key + " = " +
entry.EntityKey.EntityKeyValues.First().Value);
for (int i = 0; i < entry.OriginalValues.FieldCount; i++)
{
Console.WriteLine("\t {0} -> {1}", entry.OriginalValues[i], entry.CurrentValues[i]);
}
}
輸出結果如下:
AdventureWorksLTEntities - Product - ProductID = 1004
1004 -> 1004
def -> def
abc -> abc
Black -> Black
20.0000 -> 20
25.0000 -> 25
->
->
2008-11-3 11:33:30 -> 2008-11-3 11:33:30
->
->
->
->
5b2cfc8c-8344-4431-8c2c-651358cce331 -> 5b2cfc8c-8344-4431-8c2c-651358cce331
2008-11-3 11:33:30 -> 2008-11-3 11:33:30
MergeOption.NoTracking 選項
如果僅僅檢索數據,並不需要更新數據,則可以通過使用MergeOption.NoTracking 取消變更跟蹤。這樣,就不會使用ObjectStateManager,減少執行查詢的時間,所有返回的實體將是分離的狀態(detached state)。在ASP.NET web application 或在WinForms / WPF Grids 控件中以只讀方式顯示數據時,NoTracking 是一個比較好的選擇。
在使用對象服務(Object Services)和Entity SQL時,需要調用ObjectQuery 的一個重載構造函數,其中第三個參數是MergeOption 枚舉。默認的行為是 AppendOnly,可以改變為 NoTracking。
示例代碼如下:
var sql = "SELECT VALUE model FROM AdventureWorksLTEntities.Model AS model";
var query = new ObjectQuery<Model>(sql, context, MergeOption.NoTracking);
foreach (var mod in query)
Console.WriteLine("{0} {1} {2}", mod.ModelID, mod.Name, mod.ModifiedDate);
輸出結果如下:
1 Classic Vest 2003-6-1 0:00:00
2 Cycling Cap 2001-6-1 0:00:00
3 Full-Finger Gloves 2002-6-1 0:00:00
4 Half-Finger Gloves 2002-6-1 0:00:00
5 HL Mountain Frame 2001-6-1 0:00:00
6 HL Road Frame 1998-5-2 0:00:00
在使用LINQ to Entities 或者與Entity SQL 一起使用的CreateQuery<T> 工廠方法時,不能直接傳入NoTracking 選項,你需要對整個EntitySet 設置MergeOption 選項。
示例代碼如下:
var categories = from c in this.context.Category
where c.ParentCategory.Name != null
orderby c.Name
select c;
context.Category.MergeOption = MergeOption.NoTracking;
return categories.ToList();