views:

385

answers:

4

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 yoru 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 thing, 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?

+2  A: 

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");
    }
}
Darin Dimitrov
can i use this with MVC such that if i am using a shared hosting plan, i can just upload the dll's to the bin/folder and it should work or is it more complicated than that?i agree, many things in MVC just seem unfinished. this is yet another one i am adding to the list of many. rather disappointing really.
Erx_VB.NExT.Coder
To be honest with you, I've never used this library in a shared hosting environment and cannot say for sure whether it's going to work, but it's worth a try.
Darin Dimitrov
just taking a quick look at the code, it looks absolutely beautiful. do you have/know of any other MVC must-have's? if so, do share.
Erx_VB.NExT.Coder
AutoMapper (http://automapper.codeplex.com/) and Jimmy Bogard's blog (http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx) are a must read.
Darin Dimitrov
@Darin, just installed FluentValidation, but it contains no docs on what i'm trying to do - one server-side validation function by using/accessing several properties. any ideas on how you solve this problem wiht fluentValidation? as it wasn't in the FluentValidation documentation.
Erx_VB.NExT.Coder
@Erx_VB.NExT.Coder, please see my update.
Darin Dimitrov
A: 

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.

Baddie
errm, no buddy, but i did just recently comment on a post you made about this same particular topic. also, i have somewhat gotten upto the level you've displayed... but thanks for the extra code and description as this really helps me in grasping a currently undocumented area... i was wondering if you'd know how to display the validation error message from this in a View? i tried doing a ValidatorMessage("ClassName") and similar things, but neither worked. how did you go about displaying the message?
Erx_VB.NExT.Coder
+1  A: 

Possible Dupe:

http://stackoverflow.com/questions/1607832/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.

jfar
this is the error i get when i try to to the above (pass empty string to validationmessage:Value cannot be null or empty.Parameter name: fieldName
Erx_VB.NExT.Coder
@Erx_VB.NExT.Coder - Are you sure? I'm running MVC 2 but the code behind this functionality is almost identical. I opened a new MVC 2 project and added <%: Html.ValidationMessage("") %> into the default Index.aspx. No Error. Also there is no "fieldName" parameter in the relevant MVC source file, ValidationExtensions.cs.
jfar
i am getting this error in my own project - when i try to display error in my project (not the mvc default project) i get the mentioned error. since then i have decided to use FluentValidation and it works wonders, it actually has working validation for real world scenarioes and not just only simple "lets display a lovely non-real-world example and make things look cool" examples that MS pumps out... can't believe how weak MVC is even at version 2 it is far from complete. thanks for your help, but im just way annoyed at the VS team making things look like everything is possible when it isn't.
Erx_VB.NExT.Coder
+1  A: 

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! it's just pathetic.

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...

Erx_VB.NExT.Coder