我通过继承ValidationAttribute创建了自定义验证属性。该属性在类级别应用于我的viewmodel,因为它需要验证多个属性。
我凌驾于
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
并返回:
new ValidationResult("Always Fail", new List<string> { "DateOfBirth" });
在DateOfBirth是我的视图模型的属性之一的所有情况下。
当我运行我的应用程序时,我可以看到它被击中。 ModelState.IsValid正确设置为false但是当我检查ModelState内容时,我看到Property DateOfBirth不包含任何错误。相反,我有一个值为null的空字符串Key和一个包含我在验证属性中指定的字符串的异常。
这会导致在使用ValidationMessageFor时,我的UI中不会显示任何错误消息。如果我使用ValidationSummary,那么我可以看到错误。这是因为它与属性无关。
看起来它忽略了我在验证结果中指定了成员名的事实。
为什么会这样,我该如何解决?
要求的示例代码:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ExampleValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// note that I will be doing complex validation of multiple properties when complete so this is why it is a class level attribute
return new ValidationResult("Always Fail", new List<string> { "DateOfBirth" });
}
}
[ExampleValidation]
public class ExampleViewModel
{
public string DateOfBirth { get; set; }
}
答案 0 :(得分:13)
仍在寻找解决方案?
我今天解决了同样的问题。您必须创建自定义验证属性,该属性将验证2个日期(下面的示例)。然后你需要Adapter(验证器),它将使用你的自定义属性验证模型。最后一件事是使用属性绑定适配器。也许一些例子会比我更好地解释它:)
我们走了:
DateCompareAttribute.cs:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class DateCompareAttribute : ValidationAttribute
{
public enum Operations
{
Equals,
LesserThan,
GreaterThan,
LesserOrEquals,
GreaterOrEquals,
NotEquals
};
private string _From;
private string _To;
private PropertyInfo _FromPropertyInfo;
private PropertyInfo _ToPropertyInfo;
private Operations _Operation;
public string MemberName
{
get
{
return _From;
}
}
public DateCompareAttribute(string from, string to, Operations operation)
{
_From = from;
_To = to;
_Operation = operation;
//gets the error message for the operation from resource file
ErrorMessageResourceName = "DateCompare" + operation.ToString();
ErrorMessageResourceType = typeof(ValidationStrings);
}
public override bool IsValid(object value)
{
Type type = value.GetType();
_FromPropertyInfo = type.GetProperty(_From);
_ToPropertyInfo = type.GetProperty(_To);
//gets the values of 2 dates from model (using reflection)
DateTime? from = (DateTime?)_FromPropertyInfo.GetValue(value, null);
DateTime? to = (DateTime?)_ToPropertyInfo.GetValue(value, null);
//compare dates
if ((from != null) && (to != null))
{
int result = from.Value.CompareTo(to.Value);
switch (_Operation)
{
case Operations.LesserThan:
return result == -1;
case Operations.LesserOrEquals:
return result <= 0;
case Operations.Equals:
return result == 0;
case Operations.NotEquals:
return result != 0;
case Operations.GreaterOrEquals:
return result >= 0;
case Operations.GreaterThan:
return result == 1;
}
}
return true;
}
public override string FormatErrorMessage(string name)
{
DisplayNameAttribute aFrom = (DisplayNameAttribute)_FromPropertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
DisplayNameAttribute aTo = (DisplayNameAttribute)_ToPropertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
return string.Format(ErrorMessageString,
!string.IsNullOrWhiteSpace(aFrom.DisplayName) ? aFrom.DisplayName : _From,
!string.IsNullOrWhiteSpace(aTo.DisplayName) ? aTo.DisplayName : _To);
}
}
DateCompareAttributeAdapter.cs:
public class DateCompareAttributeAdapter : DataAnnotationsModelValidator<DateCompareAttribute>
{
public DateCompareAttributeAdapter(ModelMetadata metadata, ControllerContext context, DateCompareAttribute attribute)
: base(metadata, context, attribute) {
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (!Attribute.IsValid(Metadata.Model))
{
yield return new ModelValidationResult
{
Message = ErrorMessage,
MemberName = Attribute.MemberName
};
}
}
}
的Global.asax:
protected void Application_Start()
{
// ...
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DateCompareAttribute), typeof(DateCompareAttributeAdapter));
}
CustomViewModel.cs:
[DateCompare("StartDateTime", "EndDateTime", DateCompareAttribute.Operations.LesserOrEquals)]
public class CustomViewModel
{
// Properties...
public DateTime? StartDateTime
{
get;
set;
}
public DateTime? EndDateTime
{
get;
set;
}
}
答案 1 :(得分:2)
我不知道修复此行为的简单方法。这就是我讨厌数据注释的原因之一。对FluentValidation做同样的事情将是一个平静的事情:
public class ExampleViewModelValidator: AbstractValidator<ExampleViewModel>
{
public ExampleViewModelValidator()
{
RuleFor(x => x.EndDate)
.GreaterThan(x => x.StartDate)
.WithMessage("end date must be after start date");
}
}
FluentValidation非常棒support and integration with ASP.NET MVC。
答案 2 :(得分:0)
返回验证结果时,请使用两个参数构造函数。 将一个数组传递给context.MemberName作为唯一值。 希望这有帮助
<AttributeUsage(AttributeTargets.Property Or AttributeTargets.Field, AllowMultiple:=False)>
Public Class NonNegativeAttribute
Inherits ValidationAttribute
Public Sub New()
End Sub
Protected Overrides Function IsValid(num As Object, context As ValidationContext) As ValidationResult
Dim t = num.GetType()
If (t.IsValueType AndAlso Not t.IsAssignableFrom(GetType(String))) Then
If ((num >= 0)) Then
Return ValidationResult.Success
End If
Return New ValidationResult(context.MemberName & " must be a positive number", New String() {context.MemberName})
End If
Throw New ValidationException(t.FullName + " is not a valid type. Must be a number")
End Function
End Class
答案 3 :(得分:-1)
您需要设置ErrorMessage属性,例如:
public class DOBValidAttribute : ValidationAttribute
{
private static string _errorMessage = "Date of birth is a required field.";
public DOBValidAttribute() : base(_errorMessage)
{
}
//etc......overriding IsValid....