C# 如何在Entry.state==EntityState.Added的位置设置自定义验证
如何设置自定义验证,当C# 如何在Entry.state==EntityState.Added的位置设置自定义验证,c#,asp.net-mvc,entity-framework,validation,C#,Asp.net Mvc,Entity Framework,Validation,如何设置自定义验证,当Entry.state=EntityState.Added为真时,下面的代码可以工作! 自定义验证代码: [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] class UniqueEmailAddress : ValidationAttribute { protected override ValidationResult IsValid(object value, Validati
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);
}
}
}
}
使用:
我需要((不是)EntityState.Added):
我应该从何处携带
Entry.state
您需要获取实体附加到的上下文以查看其状态。ValidationContext可用于向validation属性类中获取额外信息,它在创建大多数IoC容器时都需要IServiceProvdier的实现,大多数IoC容器都为其实现或具有包装器
因此,如果您在具有IoC容器的环境中运行,并且可以控制属性类的创建(例如,通过在MVC中注册DataAnnotationsModelValidator),并且具有创建上下文的规则(例如,每个HTTP请求)您可以访问属性中的当前上下文,并使用它查看正在验证的实体的状态
虽然有很多ifs,但如果不了解更多的上下文、此代码运行的框架以及您试图实现的行为,很难说这是否是一种可行的方法。在实体类中实现了一种有效的验证方法:
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;
}
}
逻辑很简单:如果存在属于另一个用户的相同的电子邮件(u.UserID!=this.UserID
),验证总是失败。这适用于插入新用户(ID=0)和现有用户
顺便说一句,问题是您是否应该实际使用这里的上下文来访问数据库,但是替代方法,重写对我也没有吸引力,因为这会导致长串代码充满类型转换,每次只需要一个类型转换。这段代码针对每个被验证的实体进行遍历。Validate
方法id仅在适用时执行。@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条记录具有相同的电子邮件,则any()返回true。
更新时,您有一个id,所以x.id=user.id可以删除此记录本身。当该语句为真时,您希望它触发什么,还是仅在它周围时才需要它?有点困惑这个代码在哪里?它在实体中吗?嗯,您希望如何对EF实体执行ValidationAttribute
验证?好的,所以我在我的DbContext中重写了该方法,仍然不起作用。我在其中设置了一个断点,但它从未被命中,因此validationContext的Items属性仍然为零。回顾了所有事情,没有发现问题所在。。。有什么想法吗?我发现您必须手动调用DbContext
上的GetValidationErrors
,以便它按照本文中的说明进行验证。因此,我放弃了此选项,我不希望在几乎所有操作中都需要额外的代码。我将使用自定义验证属性。@FelipeCorrea默认情况下,上下文将在SaveChanges
上验证其实体。然后我不知道我的情况有什么问题,因为调用了我实体的validate
方法,并且从未调用DbContext.ValidateEntity
。这会导致您的推荐失败,因为我在项目
字典中没有任何键。我意识到发生了什么。在我的操作中,当调用Model.IsValid
时,它会中间触发实体Validate
方法(上下文尚未添加到项
字典)。然后,当代码到达对db.SaveChanges
的调用时,调用ValidateEntity
方法:这里是将上下文添加到项
字典中的地方,但是现在它没有用了,因为它是实体验证
方法中需要的。我认为这个过程不再是通过上下文进行查询所需的验证的解决方案。
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;
}
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
IDictionary<object, object> items)
{
items.Add("Context", this);
return base.ValidateEntity(entityEntry, items);
}
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;
}