15

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?

Erx_VB.NExT.Coder
  • 4,838
  • 10
  • 56
  • 92
  • 2
    Why this question has been down-voted? Please leave a comment when down-voting. – Darin Dimitrov May 06 '10 at 20:36
  • i agree, why on earth would this question be down voted? it brings up a vastly under-documented issue... and the documents that do exist only solves one part of the problem, nothing on how to get the validation error to display. since, i have opted ot use fluent validation (available on codeplex) which has solved all my problems, thanks to Darin's recommendation. – Erx_VB.NExT.Coder May 11 '10 at 05:21

6 Answers6

14

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
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 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 May 06 '10 at 20:06
  • 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 May 06 '10 at 20:09
  • 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 May 06 '10 at 20:11
  • 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 May 06 '10 at 20:14
  • @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 May 06 '10 at 23:55
3

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.

Omar
  • 39,496
  • 45
  • 145
  • 213
  • 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 May 06 '10 at 20:46
3

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.

Community
  • 1
  • 1
John Farrell
  • 24,673
  • 10
  • 77
  • 110
  • 1
    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 May 07 '10 at 00:19
  • @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. – John Farrell May 07 '10 at 03:08
  • 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 May 11 '10 at 05:04
2

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

Community
  • 1
  • 1
Vipresh
  • 1,250
  • 15
  • 31
2

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.

runxc1 Bret Ferrier
  • 8,096
  • 14
  • 61
  • 100
1

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

Erx_VB.NExT.Coder
  • 4,838
  • 10
  • 56
  • 92
  • I'm not sure I understand your complaint about updating multiple parts of a view in one round trip. I'm assuming you mean with ajax/partial views? It's not a "js hack" to "cut the return html up", I mean hell, that's what UpdatePanels do in WebForms. Only now you have the ability to implement it yourself however you see fit. – rossisdead May 09 '12 at 19:46
  • @rossisdead im going to have to disagree with you on that one, one should not need to resort to js to update multiple parts of a webpage in one shot, currently, the only way to do it with mvc only is if you update the first update panel, then when that is updated, trigger another update for the next update panel or div, then when thats updated, use it to trigger another update for ur 3rd part, of course, using js to do it, but the lag on user end with having to wait for all this is what is wrong and not acceptable, you should be able to return multiple views and have them update parts needed. – Erx_VB.NExT.Coder May 10 '12 at 21:06
  • @rossisdead also, mvc futures has this multiple views thing now, scottgu told me on his blog, but i have no idea where in the futures dll it is or how to use it, there is no tutorial on it or anything to my knowledge. so if there are two parts of a webpage that are in different areas from one another, you could wrap it all around one updatepanel or div, but it defeats the purpose of having update panels or using ajax if ur returning the entire page when all you want it two small divs updated. anyone here happen to use the multiple views returning ability in the futures dll or even seen it? – Erx_VB.NExT.Coder May 10 '12 at 21:11
  • I think I misunderstood your original statement about cutting up html. I'm not suggesting you return the whole page and parse out what you need. What you would do is have an action that executes a couple partial views and return only those, then have some script that determines what parts of the page to update with each partial view. – rossisdead May 11 '12 at 01:22
  • @rossisdead yes, you are correct, you would have to write a script in JS in order to do that, and would no longer really be able to use the update panel, and would need to do it all manually. it would be good if we could use the updatepanel in such a fashion where we do not have to do this, mainly because it kills the whole MVC pattern and you find yourself not being able to comply to the advantageous design principles and patterns otherwise afforded by MVC, which is a real pity to me, not really bothered by having to write the JS, more so breaking the design/view pattern I’ve come to MVC for. – Erx_VB.NExT.Coder May 11 '12 at 21:26
  • @rossisdead also, scottgu told me he has something in the futures library where you can return multiple views at the same time, so you don't need to que requests, return entire page or return both views in one shot (join) and cut up the resulting join to spread em across the two divs. do you think we should start a new question asking if anyone is using this library and could instruct others on how to use it? Right now i'm using HTML cutting/pasting to divs but don't like it as a design principle. – Erx_VB.NExT.Coder May 11 '12 at 21:33