如何在 Entry.state == EntityState.Added 的位置设置自定义验证

Posted

技术标签:

【中文标题】如何在 Entry.state == EntityState.Added 的位置设置自定义验证【英文标题】:How to set custom validation just where Entry.state == EntityState.Added 【发布时间】:2015-10-09 07:43:27 【问题描述】:

如何设置自定义验证,当Entry.state == EntityState.Added 为真时,以下代码将起作用! 自定义验证码:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class UniqueEmailAddress : ValidationAttribute

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    
        this.email = (string)value;
        using (G9EntityDataBaseClass oEntity = new G9EntityDataBaseClass())
        
            if (oEntity.User.Where(u => u.email == email).Count() == 0)
            
                return ValidationResult.Success;
            
            else
            
                return new ValidationResult(ErrorMessageString);
            
        
    

使用:

[CustomValidation.UniqueEmailAddress]
public string email  set; get; 

我需要 ((NOT)EntityState.Added):

if (Entry.state != EntityState.Added)
     return ValidationResult.Success;

我应该从哪里带来Entry.state

【问题讨论】:

当该陈述为真时,您希望它触发什么,或者如果围绕它,您只需要它吗?对问题有点困惑 这段代码在哪里?它在实体内部吗? 呵呵,你希望如何对 EF 实体执行ValidationAttribute 验证? 【参考方案1】:

一种适用于验证的方法是在您的实体类中实现IValidatableObject:

public class User: IValidatableObject

Validate 方法中,您可以通过将实体对象添加到ValidationContext 来访问当时附加的实体对象的上下文。因此,您的验证可能如下所示:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

    var context = (G9EntityDataBaseClass)validationContext.Items["Context"];
    if (context.User
               .Where(u => u.email == email && u.UserID != this.UserID)
               .Any())
    
        return new ValidationResult(ErrorMessageString);
    
    else
    
        return ValidationResult.Success;
    

您可以通过在 DbContext 类中重写此方法来将上下文添加到验证上下文:

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
                       IDictionary<object, object> items)

    items.Add("Context", this);
    return base.ValidateEntity(entityEntry, items);

逻辑很简单:如果存在属于另一个用户 (u.UserID != this.UserID) 的相同 email,验证总是会失败。这适用于插入新用户 (ID = 0) 和现有用户。

顺便说一下,debatable 是否应该实际使用此处的上下文来访问数据库,但替代方案,覆盖 DbContext.ValidateEntity 也对我没有吸引力,因为这会导致长串代码充满类型每次只需要一个演员表。 为每个已验证的实体遍历此代码Validate 方法 id 仅在适用时执行。

【讨论】:

好的,所以我在我的 DbContext 中覆盖了该方法,但仍然无法正常工作。我在其中设置了一个断点,它永远不会被击中,所以validationContext的Items属性仍然为零。审查了所有内容,但没有发现问题所在……有什么想法吗? 我发现您必须手动在DbContext 上调用GetValidationErrors,以便按照link. 中的说明进行验证,因此我放弃了这个选项,我没有几乎每个动作都需要额外的代码。我将使用自定义验证属性。 @FelipeCorrea 默认情况下,上下文将在SaveChanges 上验证其实体。 那么我不知道我的情况出了什么问题,因为我的实体的 Validate 方法被调用,而 DbContext.ValidateEntity 从未被调用。这会导致您的建议失败,因为我在 itemsdictionary 中没有任何键。 我意识到发生了什么。在我的操作中,当Model.IsValid 被调用时,它会立即触发实体Validate 方法(上下文尚未添加到items 字典中)。然后,当代码到达对db.SaveChanges 的调用时,将调用ValidateEntity 方法: 这是将上下文添加到items 字典的位置,但现在它没用了,因为实体Validate 方法中需要它。我认为这个过程不再是一种解决方案来进行需要通过上下文进行查询的验证。【参考方案2】:

您需要获取实体所附加的上下文才能查看其状态。 ValidationContext 可用于将额外信息获取到验证属性类中,它在创建时需要一个 IServiceProvdier 的实现,大多数 IoC 容器要么实现它,要么拥有它的包装器。

因此,如果您在具有 IoC 容器的环境中运行并且可以控制属性类的创建(例如,通过在 MVC 中注册 DataAnnotationsModelValidator)并具有创建上下文的规则(例如,每个 HTTP 请求),您可能能够访问属性中的当前上下文并使用它来查看正在验证的实体的状态。

虽然有很多 ifs,但在不了解更多上下文的情况下,这段代码在什么框架中运行以及您试图实现什么行为,很难说这是否是一种可行的方法。

【讨论】:

【参考方案3】:

@Gert Arnold post是一种方式,如果你想使用属性,你可以通过ObjectInstance属性从ValidationContext中找到对象

var user = validationContext.ObjectInstance as User;
if(user==null) return new ValidationResult("...");
using(var db=new SomeDbContext())
    var has=db.User.Any(x=>x.Id!=user.Id && x.email==value);
    if (has) return new ValidationResult("...");
    else return ValidationResult.Success;

添加时id为0,所以在db中,没有id为0的记录,所以如果有1条记录有相同的email,则any()返回true。 更新的时候,你有一个id,所以x.id!=user.id可以除了这条记录自己。

【讨论】:

以上是关于如何在 Entry.state == EntityState.Added 的位置设置自定义验证的主要内容,如果未能解决你的问题,请参考以下文章

EF 指定字段修改

如何在 WildFly 中设置 Undertow MAX_ENTITY_SIZE

Hibernate 5 @Entity 如何 extends 另外一个类

如何获取在CoreData中取平均值的Entity属性

从源代码分析DbSet如何通过ObjectStateManager管理entity lifecycle的生命周期

如何修复“NSObjectInaccessibleException”获取请求在被 NSManagedObjectContext 使用之前无法响应 -entity