Basically, what the title says. I have several properties that combine together to really make one logical answer, and i would like to run a server-开发者_如何转开发side validation code (that i write) which take these multiple fields into account and hook up to only one validation output/error message that users see on the webpage.
I looked at scott guthries method of extending an attribute and using it in your dataannotations declarations, but, as i can see, there is no way to declare a dataannotations-style attribute on multiple properties, and you can only place the declarations (such as [Email], [Range], [Required]) over one property :(.
i have looked at the PropertiesMustMatchAttribute in the default mvc 2.0 project that appears when you start a new project, this example is as useful as using a pair of pins to check your motor oil - useless!
i have tried this method, however, creating a class level attribute, and have no idea how to display the error from this in my aspx page. i have tried html.ValidationMessage("ClassNameWhereAttributeIsAdded") and a variety of other things, and it has not worked. and i should mention, there is NOT ONE blog post on doing validation at this level - despite this being a common need in any project or business logic scenario!
can anyone help me in having my message displayed in my aspx page, and also if possible a proper document or reference explaining validation at this level?
Now that you've looked at Data Annotations and arrived to the conclusion that they are not adapted to your scenario I would suggest you looking at FluentValidation, its integration with ASP.NET MVC and the way you would unit test your validation logic - you won't be disappointed (I really have nothing against Data annotations, they are great for blog posts and tutorials but once you are confronted to real world applications you quickly realize the limits).
UPDATE:
As requested in the comments section here's an example of using the FluentValidation framework with one server-side validation function accessing multiple properties (please don't do this as it is ugly and there's a better way):
class AuthInfo
{
public string Username { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
class AuthInfoValidator : AbstractValidator<AuthInfo>
{
public override ValidationResult Validate(AuthInfo instance)
{
var result = base.Validate(instance);
if (string.IsNullOrEmpty(instance.Username))
{
result.Errors.Add(new ValidationFailure("Username", "Username is required"));
}
if (string.IsNullOrEmpty(instance.Password))
{
result.Errors.Add(new ValidationFailure("Password", "Password is required"));
}
if (string.IsNullOrEmpty(instance.ConfirmPassword))
{
result.Errors.Add(new ValidationFailure("ConfirmPassword", "ConfirmPassword is required"));
}
if (instance.Password != instance.ConfirmPassword)
{
result.Errors.Add(new ValidationFailure("ConfirmPassword", "Passwords must match"));
}
return result;
}
}
The more natural way to do this is the following (it is also immune to property rename as it contains no magic strings):
class AuthInfoValidator : AbstractValidator<AuthInfo>
{
public AuthInfoValidator()
{
RuleFor(x => x.Username)
.NotEmpty()
.WithMessage("Username is required");
RuleFor(x => x.Password)
.NotEmpty()
.WithMessage("Password is required");
RuleFor(x => x.ConfirmPassword)
.NotEmpty()
.WithMessage("ConfirmPassword is required");
RuleFor(x => x.ConfirmPassword)
.Equal(x => x.Password)
.WithMessage("Passwords must match");
}
}
I'm not sure, but are you the one that keeps down voting my answers/questions for no apparent reason (Or b/c of my views on VB.NET)?
Anyway, PropertiesMustMatchAttribute
is just a good implementation of using values of a specific property on an object. If you need to run some logic using multiple fields of an object you can do so with the following, similar to what PropertiesMustMatchAttribute
does.
Below would be the main part of a ValidationAttribute
that accesses object properties to run some logic.
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
// Get the values of the properties we need.
// Alternatively, we don't need to hard code the property names,
// and instead define them via the attribute constructor
object prop1Value = properties.Find("Person", true).GetValue(value);
object prop2Value = properties.Find("City", true).GetValue(value);
object prop3Value = properties.Find("Country", true).GetValue(value);
// We can cast the values we received to anything
Person person = (Person)prop1value;
City city = (City)prop2value;
Country country = (Country)prop3value;
// Now we can manipulate the values, running any type of logic tests on them
if(person.Name.Equals("Baddie") && city.ZIP == 123456)
{
return country.name.Equals("Australia");
}
else
{
return false;
}
}
PropertiesMustMatchAttribute
is just using Reflection
to accomplish a common task. I tried to break up the code to make it more readable/easier to understand.
Possible Dupe:
Writing a CompareTo DataAnnotation Attribute
The answers to your questions should be there and at the very least it points to this blog post:
http://byatool.com/mvc/custom-data-annotations-with-mvc-how-to-check-multiple-properties-at-one-time/
To get the error message to display you must use:
<%: Html.ValidationMessage("") %>
What happens is because your validating on a class level your getting a ModelState with a key, aka property name usually, with an empty string.
When creating class level attribute by sub classing from ValidationAttribute , if the validation fails there is no corresponding key in the ModelState => it will be an empty string but a work around is provided in link provided below which will help U to display the error message in ur View by using just the html.ValidationMessage("urpropertyname") instead of what U tried html.ValidationMessage("ClassNameWhereAttributeIsAdded") .
Unable to set membernames from custom validation attribute in MVC2
I know that this has already been answered but it appears that MVC3 has added another way to accomplish this if you want to continue using DataAnotations Attributes.
Your class can implement the IValidatableObject interface which provides a Validate method. Just another way to skin a cat.
i've done the following, using FluentValidator, if it is useful to anyone, thanks to Darin's recommendations:
Public Class tProductValidator
Inherits AbstractValidator(Of tProduct)
Public Sub New()
Const RetailWholsaleError As String = "You need to enter either a Retail Price (final price to user) or a Wholesale Price (price sold to us), but not both."
Dim CustomValidation As System.Func(Of tProduct, ValidationFailure) =
Function(x)
If (x.RetailPrice Is Nothing AndAlso x.WholesalePrice Is Nothing) OrElse (x.RetailPrice IsNot Nothing AndAlso x.WholesalePrice IsNot Nothing) Then
Return New ValidationFailure("RetailPrice", RetailWholsaleError)
End If
Return Nothing
End Function
Custom(CustomValidation)
End Sub
End Class
as usual, i was fooled into thinking MVC was a complete framework, where real world scenarios were possible. how on earth do they publish such things and not even mention its limitations i don't understand, we developers then run into issues that are a nightmare to get around or we have to rely on 3rd party stuff to do what is intrinsically MVC responsibility, and if 3rd party items weren't available - then what?
this is not the 1st serious fall back i've found in mvc 2.0 either, the list keeps growing.
sure the team keeps giving these 'cool' examples of how simple things are using imaginary non-real world examples, and when it comes to the real stuff it's like "heck we dont know or care, nor will we tell you about it, you'll just have to run into it and sort it out yourself" type deal
oh, and updating multiple parts of a view in one round trip is still not possible unless you use a js hack and cut the return html up and assign it to different div's... you can't even return multiple views or at least update multiple areas of a page natively (in one round trip), its just saddening.
Maybe when MVC reaches version 3.0 it may actually be complete, fingers crossed, as the dotnet framework wasn't realistically 'complete' until clr 3.5 & including linq/EF to SQL...
精彩评论