EF 4.1、POCO:如果 AutoDetectChanges=false,则不会更新引用类型属性
EF 4.1、POCO:我关闭了 AutoDetectChanges (Configuration.AutoDetectChangesEnabled = false) 以加快数据更新速度。然后我运行Add或Attach,实体状态更改为EntityState.Modified。所有这些都会导致对数据库中其他对象的引用未更新。但是,所有标量属性均已成功更新。
显示的 Profiler 为每个标量属性生成 SQL 更新操作,但不为引用类型生成 SQL 更新操作,尽管我确实在代码中更改了它的值。我的模型中的每种类型的实体都会出现此问题。
添加操作或附加与EntityState.Added都可以正常工作。如果我重新打开 AutoDetectChanges,更新记录的一切都会按预期正常工作。
请帮助我找出问题所在。我找不到关于 EF 检测更改的任何好的综合文档。
更新
我被要求提供一些代码示例来重现该问题。域:
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public virtual string City { get; set; }
}
DataContext:
public class DataContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Address> Address { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Client>().HasOptional(c => c.Address);
}
}
向 Clint 表添加一条记录,向 Address 添加一条记录。将客户端指向地址。然后运行以下代码:
using (var cntx = new DataContext())
{
cntx.Configuration.AutoDetectChangesEnabled = false; // Reason of problem
var client = cntx.Clients.First();
client.Name = "Anna"; // This property will be updated
client.Address = null; // This property will not be updated
cntx.Clients.Attach(client);
cntx.Entry(client).State = EntityState.Modified;
cntx.SaveChanges();
}
此代码确实会生成如下 SQL 脚本:
update [dbo].[Clients] set [Name] = 'Anna'
where ([Id] = 1)
将 AutoDetectChangesEnabled 设置为 true 并再次运行代码,这次一切正常:
update [dbo].[Clients]
set [Name] = 'Anna', [Address_Id] = null
where (([Id] = 1) and [Address_Id]=1)
请注意,如果将 Address 的值从特定值更改为 null,或返回特定值,这并不重要值,或一个具体值到另一具体值,当 AutoDetectChanges=false 时,不会跟踪任何更改。看起来像 EF 错误。
EF 4.1, POCO: I turned off AutoDetectChanges (Configuration.AutoDetectChangesEnabled = false) to speed up data update. Then I run Add or Attach with entity state changed to EntityState.Modified. All this causes references to other objects not being updated in database. However all scalar properties are updated successfully.
Profiler shown EF generates SQL update operation for every scalar property, but not for reference type, though I really changed its value in my code. This issue reproduced for every type of entity in my model.
Add operation or Attach with EntityState.Added both work fine. If I turn AutoDetectChanges back on, everything works fine as expected for updated records too.
Help me please to figure out what's wrong. I can not find any good comprehensive documentation on EF's Detect Changes.
UPDATE
I was asked to put some example of code to reproduce the issue. Domain:
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public virtual string City { get; set; }
}
DataContext:
public class DataContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Address> Address { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Client>().HasOptional(c => c.Address);
}
}
Add one record to Clint table and one to Address. Point Client to the Address. Then run the following code:
using (var cntx = new DataContext())
{
cntx.Configuration.AutoDetectChangesEnabled = false; // Reason of problem
var client = cntx.Clients.First();
client.Name = "Anna"; // This property will be updated
client.Address = null; // This property will not be updated
cntx.Clients.Attach(client);
cntx.Entry(client).State = EntityState.Modified;
cntx.SaveChanges();
}
This code does generates SQL script like this:
update [dbo].[Clients] set [Name] = 'Anna'
where ([Id] = 1)
Set AutoDetectChangesEnabled to true and run the code again, this time everything alright:
update [dbo].[Clients]
set [Name] = 'Anna', [Address_Id] = null
where (([Id] = 1) and [Address_Id]=1)
Note it does not matter if you change Address's value from specific value to null, or back to specific value, or one concrete value to other concrete value, any change is not tracked while AutoDetectChanges=false. Seems like EF bug.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我找到了设置引用属性值的方法,即使在 AutoDetectChangesEnabled=false 的情况下也可以使用:
但是我绝对不喜欢它。 1)代码看起来很难看; 2)您必须有权访问上下文才能使其工作,这不是我的情况,我希望将此属性设置在只能访问 DbContext 的存储库之外。还有其他更简单的方法让 EF 知道属性值已更改吗?
更新:好的,我找到了更简单的解决方法:只需在运行 cntx.SaveChanges() 之前运行 cntx.ChangeTracker.DetectChanges() 即可。它帮助 EF 生成正确的 SQL 更新脚本
Well, I found out the way to set Reference property values that works even with AutoDetectChangesEnabled=false:
However I definitely do not like it. 1) Code looks ugly; 2) You have to have access to context to make it work, which is not my case, I'd like this property being set outside of repository which only has access to DbContext. Is any other simpler way to let EF know the property value is changed?
Updated: Ok, I found simpler workaround: just run cntx.ChangeTracker.DetectChanges() before running cntx.SaveChanges(). It helps EF generate correct SQL update script