我已经覆盖了我的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必须检查可能数百个子实体的情况。 (有些包含数据库查找等)。
由于
答案 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);
直接在ICollection
(DbCollectionEntry
)的实施中添加POCO,该实施与实体框架相关,并负责所谓的更改跟踪(以及其他内容) 。这要归功于运行时生成的动态代理类型(参见https://stackoverflow.com/a/14321968/870604)。
因此您必须手动检测更改(请参阅@Slauma答案)。另一种选择是使用代理对象而不是organisation
POCO。这可以通过调用:
var newOrganisation = dbContext.Set<Organisation>().Create();
上述代码当然适用于新的organisation
实例。