【问题标题】:Entity Framework SaveChanges two behaviours depending on how I add to DbContext实体框架 SaveChanges 两种行为,具体取决于我添加到 DbContext 的方式
【发布时间】:2014-02-07 10:49:48
【问题描述】:

我已经覆盖了我的 db.SaveChanges(),因此我可以在 FluentValidation 验证器实际尝试保存之前调用它。

我为每个标有 IValidatableEntity 的实体都有一个验证器,如果实体匹配,它将调用它并传入 objectStateEntry。

public virtual IEnumerable<string> SaveChanges(User user)
{
     List<string> validationErrors = new List<string>();
     if (this.Configuration.ValidateOnSaveEnabled)
     {
         foreach (var entry in ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager
              .GetObjectStateEntries(System.Data.Entity.EntityState.Added | System.Data.Entity.EntityState.Deleted | System.Data.Entity.EntityState.Modified | System.Data.Entity.EntityState.Unchanged)
              .Where(entry => (entry.Entity is IValidatableEntity)))
              {
                  validationErrors.AddRange(((IValidatableEntity)entry.Entity).Validate(entry));
              }
         }

    if (!validationErrors.Any())
    { .....

我遇到的问题是我得到两种不同的行为,具体取决于我如何将对象添加到 dbContext。我猜是因为它只将聚合根标记为被修改并且只给它一个条目?

// Example A - Calls the Organisation Validator Only
 organisation.Client.Add(client); 

// Example B - Calls the Client Validator - which is correct
db.Client.Add(client);

是否有让 EF 自动检测子属性已更改(添加/修改)并调用它们的方法?如果没有,它会破坏我的验证模型,我希望更新聚合根并让 EF 调用必要的子验证,因为它们应该有唯一的条目。

我是否必须在我的 Fluent 验证中链接验证器才能捕获这些?我不希望我的流利的验证器必须检查可能数百个子实体的情况。 (有些包含数据库查找等)。

谢谢

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    尝试在覆盖的SaveChanges 方法的开头调用DetectChanges(它必须在调用GetObjectStateEntries 之前):

    this.ChangeTracker.DetectChanges();
    

    添加Client 的两行之间的区别在于organisation.Client.Add(client) 不直接调用任何EF 代码(它只是将一个项目添加到POCO 中的集合中),而db.Client.Add(client) 可以,DbSet&lt;T&gt;.Add方法会自动调用变更检测来更新实体状态。

    在第一种情况下,如果您在 SaveChanges 之前未调用任何 EF 方法,base.SaveChanges 将检测更改作为最后的位置,以确保所有实体状态正确并保存所有更改。但是base.SaveChanges 对您覆盖的SaveChanges 中的代码来说太晚了,因为它是在您评估GetObjectStateEntries 之后。此时添加的client 的实体状态仍可能是Detached(即不存在于状态管理器中)而不是Added。为了解决这个问题,您必须尽早手动调用DetectChanges 以检索GetObjectStateEntries 中的最终实体状态。

    【讨论】:

    • 非常感谢 slauma,这一切都说得通。你们都给出了很好的答案,我不知道谁先发帖。但是正如 ken 提到的那样,您的帖子假设您首先做了。
    • @PeterLea:将鼠标悬停在答案下方的“x 小时前”文本上时,您始终可以看到确切的时间戳。
    【解决方案2】:

    我假设organisation是一个简单的POCO,所以下面的代码:

    organisation.Client.Add(client);
    

    只需在ICollection 的 POCO 中添加另一个 POCO。 EF 无法检测到您正在向上下文中添加实体。

    另一方面,下面的代码:

    db.Client.Add(client);
    

    在与实体框架相关的ICollection (DbCollectionEntry) 的实现中直接添加一个 POCO,并负责所谓的更改跟踪(除其他外)。这要归功于在运行时生成的动态代理类型(请参阅https://stackoverflow.com/a/14321968/870604)。

    因此,您必须手动检测更改(请参阅@Slauma 答案)。另一种选择是使用代理对象而不是您的organisation POCO。这可以通过调用:

    var newOrganisation = dbContext.Set<Organisation>().Create();
    

    上面的代码当然适用于新的organisation 实例。

    【讨论】:

    • 谢谢肯。精彩的回答我希望我能分享赏金。对不起,如果我弄错了,你先回答了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多