IValidatableObject对EF导航属性没用?

时间:2012-04-24 14:35:23

标签: .net validation ef-code-first entity-framework-4.3

仅当实现实体DbEntityEntry.State与“Unchanged”不同时,才会调用IValidatableObject.Validate。只是更改导航属性不会改变状态,因此验证永远不会发生。

为什么微软总会发布半生不熟的测试版?

我甚至无法手动检测导航属性更改:

var changes = context.ChangeTracker.Entries()
    .Where(e => e.State != EntityState.Unchanged)
    .ToArray();

返回一个空数组。

1 个答案:

答案 0 :(得分:5)

这里有一些有趣的观点。 EntityFramework独立跟踪对实体更改的导航属性更改。 context.ChangeTracker.Entries()仅返回对实体的更改,而不返回关系。这就是你没有看到这些的原因。如果您真的想查看关系及其更改方式,可以下载到ObjectContext并执行以下操作:

var objectContext = ((IObjectContextAdapter) ctx).ObjectContext;
foreach(var relationshipEntry in objectContext.ObjectStateManager
                                              .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted)
                                              .Where(e => e.IsRelationship))
{
    EntityKey entityKey1, entityKey2;

    if (relationshipEntry.State == EntityState.Added)
    {
        entityKey1 = (EntityKey)relationshipEntry.CurrentValues[0];
        entityKey2 = (EntityKey)relationshipEntry.CurrentValues[1];            
    }
    else
    {
        entityKey1 = (EntityKey)relationshipEntry.OriginalValues[0];
        entityKey2 = (EntityKey)relationshipEntry.OriginalValues[1];                        
    }

    var entity1 = objectContext.GetObjectByKey((EntityKey)entityKey1);
    var entity2 = objectContext.GetObjectByKey((EntityKey)entityKey2);
}

对我来说更有趣的场景是当关系发生变化时需要重新验证实体的场景。我假设您没有外键 - 否则您的实体将被标记为已修改,因为更改导航proprerty会更改外键的值,而外键又会将实体标记为已修改。无论如何,我有三个有效实体 - 改变关系可能使实体(或模型)无效的情况是什么?另请注意,验证本身仅验证给定实体,但从不遵循导航属性来验证相关实体。 最后,如果您真的需要在关系发生变化时验证实体,我认为您有4个选项:

  • 尝试添加外键,以便更改关系将更改应将实体标记为已修改的外键(免责声明:我没有尝试过)

  • 覆盖DbContext.ShouldValidateEntity()方法以验证所有实体而不是仅修改实体(这是过滤逻辑发生的地方)。请注意,它可能会对性能产生一些负面影响。以下是您需要添加到从DbContext派生的类中的代码:

        protected  override bool ShouldValidateEntity(DbEntityEntry entityEntry)
        {
            return (entityEntry.State & EntityState.Deleted) == 0;
        }
  • 覆盖DbContext.SaveChanges(),以便为所有已修改的实体以及修改后的关系中涉及的所有实体调用验证(使用上面的代码,您很可能只对已添加的关系条目感兴趣)

  • 手动修改集合时,将实体标记为已修改。请注意,它可能会导致向数据库发送不需要的更新,因此根据您跟踪的实体数量,仅验证除已删除实体之外的所有实体可能要便宜得多

您可以在此处找到有关验证和验证自定义的更多详细信息: http://blogs.msdn.com/b/adonet/archive/2010/12/15/ef-feature-ctp5-validation.aspx http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx (是的,它适用于CTP和EF 4.1,但它仍然存在)