2013-05-31 84 views
28

我有點難住。從我讀過的將DbContext.AutoDetectChangesEnabled設置爲false應禁用更改跟蹤,需要調用DbContext.DetectChanges以識別要發送到數據庫的更改。DbContext AutoDetectChangesEnabled設置爲false檢測更改

但是,從下面的日誌中可以清楚看到,即使設置爲false,更改仍由dbContexts更改跟蹤器註冊。

我錯過了什麼嗎?

實體框架版本:5.0.0.0

的DbContext類

public class ProjectContext : DbContext { 
    public DbSet<Project> Projects {get;set;} 
} 

Controller類

private ProjectContext db = new ProjectContext(); 



public method(){ 
    Project p = new Project("uniqueName"); 
    db.Configuration.AutoDetectChangesEnabled = false; 
    db.Projects.Add(p); 
    DebugChangeTracker(); 
    db.SaveChanges(); 

    db.Projects.First().ProjectName = "a differentName!"; 
    DebugChangeTracker(); 
    db.SaveChanges(); 
} 

記錄方法

private void DebugChangeTracker() 
    { 
     var path = "C:\\mypath\\"; 
     path = path + Util.GetMsSinceEpoch().ToString() + "changeTracker.log"; 

     using (StreamWriter sw = new StreamWriter(path)) 
     { 
      var changeTracker = db.ChangeTracker; 
      var entries = changeTracker.Entries(); 
      foreach (var x in entries) 
      { 

       var name = x.Entity.ToString(); 
       var state = x.State; 

       sw.WriteLine(""); 
       sw.WriteLine("***Entity Name: " + name + 
          "is in a state of " + state); 
       var currentValues = x.CurrentValues; 
       sw.WriteLine("***CurrentValues***"); 
       PrintPropertyValues(currentValues,sw); 
       if (state != EntityState.Added) 
       { 
        sw.WriteLine("***Original Values***"); 
        PrintPropertyValues(x.OriginalValues,sw); 
       } 
      } 
     } 
    } 

首先登錄

***Entity Name: Models.Projectis in a state of Added 
***CurrentValues*** 
ProjectId:0 
ProjectName:uniqueName 

第二登錄

***Entity Name: Models.Projectis in a state of Modified 
***CurrentValues*** 
ProjectId:1 
ProjectName:uniqueName 
***Original Values*** 
ProjectId:1 
ProjectName:a differentName! 

回答

38

設置AutoDetectChangesEnabledfalse不會禁用更改跟蹤。 (這就是AsNoTracking()擴展方法所要做的。)它只會禁用DetectChanges的自動調用,否則這些調用會發生在很多DbContext API方法中。

但是DetectChanges不是參與變更追蹤的唯一方法。但是,如果您沒有在需要的地方手動調用它,則跟蹤的實體狀態不完整或錯誤導致錯誤地保存數據。

在你的情況你method的第一部分的狀態Added預計,即使AutoDetectChangesEnabled設置爲false,因爲你只能叫db.Projects.Add(p)。 (在你的代碼中缺少該行btw,但我想這只是一個複製和粘貼錯誤)。調用DbContext API中的方法會正確跟蹤更改,並且如果在調用之前狀態正確,跟蹤器中的狀態將正確Add

或換句話說:調用API方法不會將正確的狀態轉換爲錯誤的狀態。但是:如果AutoDetectChangesEnabledfalse它也不會將錯誤的狀態轉換爲正確的狀態,如果AutoDetectChangesEnabledtrue將會是這種情況。

但是,在method的第二部分中,您只是更改了POCO屬性值。在此之後變更跟蹤狀態是錯誤的(Unchanged)和無以DetectChanges呼叫(手動或 - 如果AutoDetectChangesEnabledtrue - 在ChangeTracker.EntriesSaveChanges自動),它將永遠不會進行調整。結果是更改後的屬性值不會保存到數據庫中。

在上一節提到的狀態Unchanged我指的是我自己的測試(也是我期望的)。我不知道,也不能重現爲什麼你有狀態Modified

對不起,如果這聽起來有點混亂。 Arthur Vickers can explain it better.

我發現自動變化檢測和行爲禁用它,而很難理解和掌握,我平時不碰默認(AutoDetectChangesEnabled = true)對於任何跟蹤那些比簡單的事情變得更加複雜的變化(當像循環中的批量添加實體等)。

+0

我有幾次讀一遍,但是這並幫助回答我問題相當多,謝謝!抱歉關於複製和粘貼錯誤;我會更新後代的問題。 – Jesse

+1

不幸的是,「在循環中批量添加實體」是您希望禁用更改跟蹤的時候。這是一個_massive_加速(樣本大小爲1,在我的應用程序中進行了測試,但它是添加~3000行的兩次運行之間的唯一區別)。 –

+1

@EdS .:批量添加是「最簡單的事情」之一,我的意思是我實際上會*禁用自動更改檢測。 – Slauma

1

根據實體Framework Automatic Detect Changes's Article

他們說:

您可以通過從文章在這個例子中some cases

樣子把它關閉得到顯著性能改進

using (var context = new BloggingContext()) 
{ 
    try 
    { 
     context.Configuration.AutoDetectChangesEnabled = false; 

     // Make many calls in a loop 
     foreach (var blog in aLotOfBlogs) 
     { 
      context.Blogs.Add(blog); 
     } 
    } 
    finally 
    { 
     context.Configuration.AutoDetectChangesEnabled = true; 
    } 
} 

此代碼可避免在調用DbSet.AddSaveChanges方法時發生的不必要的調用DetectChanges

+0

如果您在finally塊中重新打開它,它是否會自動做它會做什麼,如果它已經開始,但更快? –

2

如果有人在實體框架的核心尋找AutoDetectChangesEnabled你可以找到它ChangeTracker下insted的的Configuration

用法,如:

context.ChangeTracker.AutoDetectChangesEnabled = false; 

//Do something here 
context.PriceRecords.Add(newPriceRecord); 

context.ChangeTracker.AutoDetectChangesEnabled = true;