我有点难住了。根据我的阅读,将
DbContext.AutoDetectChangesEnabled
设置为 false
应该禁用更改跟踪,需要调用 DbContext.DetectChanges
才能识别要发送到数据库的更改。
但是,从下面的日志可以清楚地看出,即使设置设置为 false,dbContexts 更改跟踪器也会注册更改。
我错过了什么吗?
实体框架版本:5.0.0.0
DbContext 类
public class ProjectContext : DbContext {
public DbSet<Project> Projects {get;set;}
}
控制器类
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!
将
AutoDetectChangesEnabled
设置为 false
不会禁用更改跟踪。 (这就是 AsNoTracking()
扩展方法的作用。)它只是禁用 DetectChanges
的自动调用,否则会在许多 DbContext
API 方法中发生。
但是
DetectChanges
并不是参与变更跟踪的唯一方法。但是,如果您没有在需要的正确位置手动调用它,则跟踪的实体状态不完整或错误,导致数据保存不正确。
在您的情况下,即使
Added
设置为 method
,您的 AutoDetectChangesEnabled
第一部分中的状态 false
也是预期的,因为您只调用 db.Projects.Add(p)
。 (顺便说一句,您的代码中缺少该行,但我猜这只是复制和粘贴错误。)从 DbContext
API 调用方法可以正确跟踪更改,并且如果在之前状态正确,则跟踪器中的状态将是正确的。致电Add
。
或者换句话说:调用API方法不会将正确的状态变成错误的状态。但是:如果
AutoDetectChangesEnabled
是 false
,它也不会将错误状态转变为正确状态,如果 AutoDetectChangesEnabled
是 true
,就会出现这种情况。
但是,在
method
的第二部分中,您只是更改 POCO 属性值。此后,更改跟踪器状态错误 (Unchanged
),并且无需调用 DetectChanges
(手动或 - 如果 AutoDetectChangesEnabled
为 true
- 自动在 ChangeTracker.Entries
或 SaveChanges
中)它永远不会被调整。效果是更改的属性值不会保存到数据库中。
在最后一节提到状态
Unchanged
我指的是我自己的测试(以及我所期望的)。我不知道也无法重现为什么你有状态Modified
。
抱歉,如果这听起来有点令人困惑。 Arthur Vickers 可以更好地解释它。
我发现自动更改检测和禁用它时的行为相当难以理解和掌握,并且对于任何比最简单的事情更复杂的跟踪更改,我通常不会触及默认值(
AutoDetectChangesEnabled
=true
)例如在循环中批量添加实体等)。
如果有人在 Entity Framework Core 中寻找
AutoDetectChangesEnabled
,您可以在 ChangeTracker
下找到它,而不是 Configuration
用法如下:
context.ChangeTracker.AutoDetectChangesEnabled = false;
//Do something here
context.PriceRecords.Add(newPriceRecord);
context.ChangeTracker.AutoDetectChangesEnabled = true;
根据实体框架自动检测变化的文章
他们说:
通过在
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;
}
}
此代码避免了在调用
DetectChanges
和 DbSet.Add
方法时对 SaveChanges
进行不必要的调用。
通过关闭 AutoDetectChangesEnabled,我看到了巨大的性能提升。
我在列表中添加了数千条记录,关闭此功能导致运行时间从几小时缩短到几分钟。