实体框架SaveChanges两种行为取决于我添加到DbContext的方式

时间:2014-02-07 10:49:49

标签: c# entity-framework

我已经覆盖了我的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验证中链接验证器才能捕获这些验证器?我不想要一个我流利的Validator必须检查可能数百个子实体的情况。 (有些包含数据库查找等)。

由于

2 个答案:

答案 0 :(得分:2)

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

this.ChangeTracker.DetectChanges();

添加Client的两行之间的区别在于organisation.Client.Add(client)不会直接调用任何EF代码(它只是将项目添加到POCO中的集合中)db.Client.Add(client)并且DbSet<T>.Add方法将自动调用更改检测以更新实体状态。

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

答案 1 :(得分:1)

我认为organisation是一个简单的POCO,所以代码如下:

organisation.Client.Add(client);

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

另一方面,以下代码:

db.Client.Add(client);

直接在ICollectionDbCollectionEntry)的实施中添加POCO,该实施与实体框架相关,并负责所谓的更改跟踪(以及其他内容) 。这要归功于运行时生成的动态代理类型(参见https://stackoverflow.com/a/14321968/870604)。

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

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

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