如何在MVC中的数据注释中进行条件验证
本文关键字:条件 验证 注释 MVC 数据 | 更新日期: 2023-09-27 18:27:27
我有以下型号:
public class PasswordResetModel {
public bool PasswordCreationStatus { get; set; }
[Display(Name = "AcctNumber", ResourceType = typeof(Resources.Register))]
[ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
//Account number is numeric
[RegularExpression(@"^[0-9]+$", ErrorMessageResourceType = typeof(Resources.Register),
ErrorMessageResourceName = "AccountNumberRegularExpr")]
[ScriptIgnore]
public int? AccountNumber { get; set; }
[Display(Name = "BirthDate", ResourceType = typeof(Resources.Register))]
[ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
[ScriptIgnore]
public string BirthDate { get; set; }
[Display(Name = "SSN", ResourceType = typeof(Resources.Register))]
[ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
//Accepts only four digits
[RegularExpression(@"^'d{4}$", ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
[StringLength(4, ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
[ScriptIgnore]
public string SSN { get; set; }
}
我想确保至少输入3个(账号、SSN和出生日期)中的任何一个。ValidateMustHaveAtleteOne类如下所示。但我只想在PasswordCreationStatus为false的情况下进行验证。
ValidateMustHaveAtLeastOne类如下所示:
[AttributeUsage(AttributeTargets.Property)]
public class ValidateMustHaveAtLeastOne : ValidationAttribute, IClientValidatable {
#region Construnctor
public ValidateMustHaveAtLeastOne(string groupName, string validationMessage) {
GroupName = groupName;
ValidationMessage = validationMessage;
}
#endregion
#region Properties
/// <summary>
/// Name of the group of the properties
/// </summary>
public string GroupName { get; private set; }
/// <summary>
/// Vaidation message
/// </summary>
public string ValidationMessage { get; private set; }
#endregion
#region Public overrides
/// <summary>
/// Validates the group of properties.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="context">The context information about the validation operation.</param>
/// <returns>An instance of the ValidationResult class.</returns>
protected override ValidationResult IsValid(object value, ValidationContext context) {
foreach (var property in GetGroupProperties(context.ObjectType)) {
var propertyValue = property.GetValue(context.ObjectInstance, null);
if (propertyValue != null) {
return null;
}
}
return new ValidationResult(ValidationMessage);
}
#endregion
#region Implementation of IClientValidateable
/// <summary>
/// To enable client side implementation of same validtion rule.
/// </summary>
/// <param name="metadata">The model metadata.</param>
/// <param name="context">The controller context.</param>
/// <returns>The client validation rules for this validator.</returns>
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
var groupProperties = GetGroupProperties(metadata.ContainerType);
List<string> propertyNames = new List<string>();
foreach (var property in groupProperties) {
propertyNames.Add(property.Name);
}
var rule = new ModelClientValidationRule {
ErrorMessage = this.ValidationMessage
};
rule.ValidationType = string.Format("group", GroupName.ToLower());
rule.ValidationParameters["propertynames"] = string.Join(",", propertyNames);
yield return rule; // yield key word is used as return type is declared as IEnumerable<ModelClientValidationRule>
}
#endregion
/// Returns the group of properties that implements this attribute
/// </summary>
/// <param name="type">Type of the Model</param>
/// <returns>List of properties</returns>
private IEnumerable<PropertyInfo> GetGroupProperties(Type type) {
var propertyInfo = new List<PropertyInfo>();
foreach (PropertyInfo property in type.GetProperties()) {
if (property.GetCustomAttributes(typeof(ValidateMustHaveAtLeastOne), false).GetLength(0) > 0) {
propertyInfo.Add(property);
}
}
return propertyInfo;
}
}
我认为这是可能的最低限度的增强。如有任何帮助,我们将不胜感激。提前谢谢。
更新Validation以验证依赖属性是否与目标值匹配,然后进行验证。
[AttributeUsage(AttributeTargets.Property)]
public class ValidateMustHaveAtLeastOne : ValidationAttribute, IClientValidatable {
#region Construnctor
public ValidateMustHaveAtLeastOne(string groupName, string validationMessage, string dependentProperty, object targetValue) {
GroupName = groupName;
ValidationMessage = validationMessage;
DependentProperty = dependentProperty;
TargetValue = targetValue;
}
#endregion
#region Properties
/// <summary>
/// Name of the group of the properties
/// </summary>
public string GroupName { get; private set; }
/// <summary>
/// Vaidation message
/// </summary>
public string ValidationMessage { get; private set; }
/// <summary>
/// Gets or sets the dependent property.
/// </summary>
/// <value>
/// The dependent property.
/// </value>
public string DependentProperty { get; set; }
/// <summary>
/// Gets or sets the target value.
/// </summary>
/// <value>
/// The target value.
/// </value>
public object TargetValue { get; set; }
#endregion
#region Public overrides
/// <summary>
/// Validates the group of properties.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="context">The context information about the validation operation.</param>
/// <returns>An instance of the ValidationResult class.</returns>
protected override ValidationResult IsValid(object value, ValidationContext context) {
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this.DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && this.TargetValue == null)
|| (dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
{
foreach (var property in GetGroupProperties(context.ObjectType)) {
var propertyValue = property.GetValue(context.ObjectInstance, null);
if (propertyValue != null) {
return null;
}
}
return new ValidationResult(ValidationMessage);
}
}
return ValidationResult.Success;
}
#endregion
#region Implementation of IClientValidateable
/// <summary>
/// To enable client side implementation of same validtion rule.
/// </summary>
/// <param name="metadata">The model metadata.</param>
/// <param name="context">The controller context.</param>
/// <returns>The client validation rules for this validator.</returns>
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
var groupProperties = GetGroupProperties(metadata.ContainerType);
List<string> propertyNames = new List<string>();
foreach (var property in groupProperties) {
propertyNames.Add(property.Name);
}
var rule = new ModelClientValidationRule {
ErrorMessage = this.ValidationMessage
};
rule.ValidationType = string.Format("group", GroupName.ToLower());
rule.ValidationParameters["propertynames"] = string.Join(",", propertyNames);
string depProp = this.BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on; if it's a bool, format it javascript style
string targetValue = (this.TargetValue ?? string.Empty).ToString();
if (this.TargetValue.GetType() == typeof(bool))
{
targetValue = targetValue.ToLower();
}
rule.ValidationParameters["dependentproperty"] = depProp;
rule.ValidationParameters["targetvalue"] = targetValue;
yield return rule; // yield key word is used as return type is declared as IEnumerable<ModelClientValidationRule>
}
#endregion
/// Returns the group of properties that implements this attribute
/// </summary>
/// <param name="type">Type of the Model</param>
/// <returns>List of properties</returns>
private IEnumerable<PropertyInfo> GetGroupProperties(Type type) {
var propertyInfo = new List<PropertyInfo>();
foreach (PropertyInfo property in type.GetProperties()) {
if (property.GetCustomAttributes(typeof(ValidateMustHaveAtLeastOne), false).GetLength(0) > 0) {
propertyInfo.Add(property);
}
}
return propertyInfo;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
// This will have the name of the current field appended to the beginning, because the TemplateInfo's context has had this fieldname appended to it.
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField, StringComparison.OrdinalIgnoreCase))
{
depProp = depProp.Substring(thisField.Length);
}
return depProp;
}
}
用法
[ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "", "PasswordCreationStatus", false)]
您可以使用已完成(测试)的库在某些条件下进行验证。
有很多这样的库,但最著名的可能是万无一失的
在上面的链接中,您可以看到它的用法和文档。
我认为以下验证属性适合您的情况。
[RequiredIfTrue("PasswordCreationStatus")]
您可以添加一个计算的属性,该属性只有在至少提供一个属性时才有效:
public class PasswordResetModel
{
public bool PasswordCreationStatus { get; set; }
[Display(Name = "AcctNumber", ResourceType = typeof(Resources.Register))]
//Account number is numeric
[RegularExpression(@"^[0-9]+$", ErrorMessageResourceType = typeof(Resources.Register),
ErrorMessageResourceName = "AccountNumberRegularExpr")]
[ScriptIgnore]
public int? AccountNumber { get; set; }
[Display(Name = "BirthDate", ResourceType = typeof(Resources.Register))]
[ScriptIgnore]
public string BirthDate { get; set; }
[Display(Name = "SSN", ResourceType = typeof(Resources.Register))]
//Accepts only four digits
[RegularExpression(@"^'d{4}$", ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
[StringLength(4, ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
[ScriptIgnore]
public string SSN { get; set; }
[Required(ErrorMessage = "Must Have At Least One of the properties")]
public string MustHaveAtLeastOneIsValid {
get{
return this.SSN != null || this.BirthDate != null || this.AccountNumber.HasValue ? "valid": null;
}
}
}