Data annotation to check dependent property is value - c#

My requirement is to validate a field depending on Ajax return success or failure message.
I have hidden property IsValidEmployeenumber is set 'true' or 'false' from Ajax call.
[XXX('IsValidEmployeeNumber', 'true')] //Please suggest
public string EmployeeNumber { get; set; }
public string IsValidEmployeeNumber { get; set; }
May I know is there a native MVC data annotation available that will take dependent property name and expected value.
Thanks,

No. There is no such native annotation. You can make your own, which will take as a paramter the name of the property on which the validation depends, and then when applying, pass the appropriate property name.
The way to do this is to create a custom annotation class deriving from ValidationAttribute, and making a constructor which takes as a parameter the name of the dependent property, and then override the IsValid method, in which you get the value of the dependent property using reflection, and write your custom validation logic.
Now, this will work for server side validation. But you need to do a lot more if you need client side validation as well, which is beyond the scope of this question.

Related

Best Practices ViewModel Validation in ASP.NET MVC

I am using DataAnnotations to validate my ViewModel on client side with jquery.validate.unobtrusive and on server side in ASP.NET MVC application.
Not so long time ago, I figured out that I can write validation like this:
[Required(ErrorMessage = "{0} is required")]
public string Name { get; set; }
That way I can easily define some general strings in config or in resources and always use it in DataAnnotations. So it will be easier to change validation messages in my whole application in future.
Also I know that there is a FluentValidation library that allows to add validation rules to already existing ViewModel. I know that there is a problem with Add/Edit ViewModels that could have similar fields but different ValidationRules.
Another problem that comes from client validation is that html newly added to DOM (using ajax request) should be parsed to enable validation. This is how I do it:
$('#some-ajax-form').data('validator', null);
$.validator.unobtrusive.parse('#some-ajax-form');
So I have some questions:
Is there some other useful practises that could help centralize all validation rules in application?
What's is a best way to solve Add/Edit ViewModel Validation problem? Can I use DataAnnotations with FluentValidation or separate Add and Edit ViewModels still is a best option?
Is there any better way to initialize validation on new DOM elements that received with ajax call other that I mention?
I'm not asking how to create my own DataValidators I know how to do it. I seeking of ways how to use them in more productive and easy maintainable way.
To answer your 3th question first: No there is no easier way then what you are doing. Two lines of code to get it working can hardly be easier. Although there is a plug-in you could use, like explained in the question unobtrusive validation not working with dynamic content
Your first question, how to centralize validation, I normally use a separate class file to store all my validation rules. This way I don't have to browse through every single class file to find the rules, but have them all in one place. If that's better, is matter of choice. The main reason I started to use it, is to be able to add validation to auto-generated classes, like classes from the Entity Framework.
So I have a file called ModelValidation.cs in my data layer, and have code for all my models like
/// <summary>
/// Validation rules for the <see cref="Test"/> object
/// </summary>
/// <remarks>
/// 2015-01-26: Created
/// </remarks>
[MetadataType(typeof(TestValidation))]
public partial class Test { }
public class TestValidation
{
/// <summary>Name is required</summary>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>Text is multiline</summary>
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
}
Now as you noticed I don't provide the actual error message. I use conventions by Haacked to add the messages. It makes it simple to add localized validation rules.
It basically comes down to a recource file containing something like:
Test_Name = "Provide name"
Test_Name_Required = "Name is required"
And these messages and naming will be used when you call regular MVC view code like
<div class="editor-container">
<div class="editor-label">
#Html.LabelFor(model => model.Name) <!--"Provide name"-->
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name) <!--"Name is required"-->
</div>
</div>
Your second question, about different validation for add/edit can be handled in two ways. The best way, would be to use views as they are actually intended. That means you don't pass your actual models to the views, but you create a view model that contains only the data. So you have a view model for Create with the proper validation rules and a view model for Edit with the proper rules, and when they pass you insert the result in your actual model.
This however requires a lot more code and manual work, so I can imagine you're not really willing to do it like this.
Another option would be to use conditional validation like explained by viperguynaz. Now instead of a boolean, my classes that require a change between edit/add have a primary key Id int. So I check if Id>0 to determine if it is an edit or not.
UPDATE:
If you want to update validation on every ajax call, you could use jQuery ajaxComplete. This will revalidate all forms after every ajax request.
$( document ).ajaxComplete(function() {
$('form').each(function() {
var $el = $(this);
$el.data('validator', null);
$.validator.unobtrusive.parse($el);
})
});
If this is something you want, depends on how often you receive a form via AJAX. If you have a lot of AJAX request, like polling a status every 10seconds, than you don't want this. If you have an occasional AJAX request, that mostly contains a form, then you could use it.
If your AJAX returns a form you want to validate, then yes, it is good practise to update the validation. But I guess a better question would be "Do I really need to send the form by AJAX?"
AJAX is fun and useful, but it should be used with care and thought.
Like others have said, there is no such tricks, no easy way to centralize your validations.
I have a couple of approaches that might interest you. Take note that this is how "we" solved the same problem before. Its up to you if you can find our solution maintainable and productive.
I know that there is a problem with Add/Edit ViewModels that could have similar fields but different ValidationRules.
Inheritance Approach
You can achieve centralized validation using a base class, and use subclasses for specific validations.
// Base class. That will be shared by the add and edit
public class UserModel
{
public int ID { get; set; }
public virtual string FirstName { get; set; } // Notice the virtual?
// This validation is shared on both Add and Edit.
// A centralized approach.
[Required]
public string LastName { get; set; }
}
// Used for creating a new user.
public class AddUserViewModel : UserModel
{
// AddUser has its own specific validation for the first name.
[Required]
public override string FirstName { get; set; } // Notice the override?
}
// Used for updating a user.
public class EditUserViewModel : UserModel
{
public override string FirstName { get; set; }
}
Extending the ValidationAttribute Approach
Using custom ValidationAtribute, you can achieve centralized validation. This is only the basic implementation, I am just showing you the idea.
using System.ComponentModel.DataAnnotations;
public class CustomEmailAttribute : ValidationAttribute
{
public CustomEmailAttribute()
{
this.ErrorMessage = "Error Message Here";
}
public override bool IsValid(object value)
{
string email = value as string;
// Put validation logic here.
return valid;
}
}
You would use as such
public class AddUserViewModel
{
[CustomEmail]
public string Email { get; set; }
[CustomEmail]
public string RetypeEmail { get; set; }
}
Is there any better way to initialize validation on new DOM elements that received with ajax call other that I mention?
This is how I rebind validators on dynamic elements (jQuery)
/**
* Rebinds the MVC unobtrusive validation to the newly written
* form inputs. This is especially useful for forms loaded from
* partial views or ajax.
*
* Credits: http://www.mfranc.com/javascript/unobtrusive-validation-in-partial-views/
*
* Usage: Call after pasting the partial view
*
*/
function refreshValidators(formSelector) {
//get the relevant form
var form = $(formSelector);
// delete validator in case someone called form.validate()
$(form).removeData("validator");
$.validator.unobtrusive.parse(form);
};
Usage (JQuery)
// Dynamically load the add-user interface from a partial view.
$('#add-user-div').html(partialView);
// Call refresh validators on the form
refreshValidators('#add-user-div form');
Jquery unobtrusive validation works by applying attributes to INPUT elements that instruct the client library to validate that element using a rule that is mapped to the respective attribute. For instance: the data-val-required html attribute is recognized by the unobtrusive library, and causes it to validate that element against the corresponding rule.
In .NET MVC, you can make this happen automatically for some specific rules by applying attributes to your model properties. Attributes like Required and MaxLength work because the Html helpers know how to read those attributes and add corresponding HTML attributes to their output that the unobtrusive library understands.
If you add validation rules to your models in IValidatableObject or using FluentValidation, the HTML Helper will not see these rules, and therefore not try to translate them to unobtrusive attributes.
In other words the "free" coordination you've seen thus far by applying attributes to your model and getting client validation is limited to validation attributes, and further, is limited (by default) only to those attributes that map directly to unobtrusive rules.
The bright side is, you are free to create your own custom validation attributes, and by implementing IClientValidatable, the Html Helper will add an unobtrusive attribute with the name of your choosing that you can then teach the unobtrusive library to respect.
This is a custom attribute we use that ensures that one date falls after another date:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DateGreaterThanAttribute : ValidationAttribute, IClientValidatable
{
string otherPropertyName;
public DateGreaterThanAttribute(string otherPropertyName, string errorMessage = null)
: base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
// Let's check that otherProperty is of type DateTime as we expect it to be
if (otherPropertyInfo.PropertyType.Equals(new DateTime().GetType()))
{
DateTime toValidate = (DateTime)value;
DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
// if the end date is lower than the start date, than the validationResult will be set to false and return
// a properly formatted error message
if (toValidate.CompareTo(referenceProperty) < 1)
{
validationResult = new ValidationResult(this.GetErrorMessage(validationContext));
}
}
else
{
// do nothing. We're not checking for a valid date here
}
return validationResult;
}
public override string FormatErrorMessage(string name)
{
return "must be greater than " + otherPropertyName;
}
private string GetErrorMessage(ValidationContext validationContext)
{
if (!this.ErrorMessage.IsNullOrEmpty())
return this.ErrorMessage;
else
{
var thisPropName = !validationContext.DisplayName.IsNullOrEmpty() ? validationContext.DisplayName : validationContext.MemberName;
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
var otherPropName = otherPropertyInfo.Name;
// Check to see if there is a Displayname attribute and use that to build the message instead of the property name
var displayNameAttrs = otherPropertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false);
if (displayNameAttrs.Length > 0)
otherPropName = ((DisplayNameAttribute)displayNameAttrs[0]).DisplayName;
return "{0} must be on or after {1}".FormatWith(thisPropName, otherPropName);
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
//string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
string errorMessage = ErrorMessageString;
// The value we set here are needed by the jQuery adapter
ModelClientValidationRule dateGreaterThanRule = new ModelClientValidationRule();
dateGreaterThanRule.ErrorMessage = errorMessage;
dateGreaterThanRule.ValidationType = "dategreaterthan"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return dateGreaterThanRule;
}
}
We may apply the attribute to the model as such:
[DateGreaterThan("Birthdate", "You have to be born before you can die")]
public DateTime DeathDate { get; set; }
This causes the Html helper to render the following two attributes on the INPUT element when calling Html.EditorFor on a model property that has this attribute:
data-val-dategreaterthan="You have to be born before you can die"
data-val-dategreaterthan-otherpropertyname="Birthdate"
So far so good, but now I have to teach unobtrusive validation what to do with those attributes. First, I have to create a named rule for jquery validation:
// Value is the element to be validated, params is the array of name/value pairs of the parameters extracted from the HTML, element is the HTML element that the validator is attached to
jQuery.validator.addMethod("dategreaterthan", function (value, element, params) {
return Date.parse(value) > Date.parse($(params).val());
});
And then add an unobtrusive adaptor for that rule that maps the attribute to the rule:
jQuery.validator.unobtrusive.adapters.add("dategreaterthan", ["otherpropertyname"], function (options) {
options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;
options.messages["dategreaterthan"] = options.message;
});
After Ive done all this, I can get this validation rule for "free" anywhere else in my application just by applying that attribute to the model.
To address your question of how to apply rules conditionally based on whether the model is being used in an add or an edit operation: this can probably be done by adding additional logic to your custom attributes and having both the IsValid method the GetClientValidation rules method attempt to glean some context from the model using reflection. But honestly, that seems like a mess to me. For this, I'd just rely on server validation and whatever rules you choose to apply using IValidatableObject.Validate() method.
There are various ways to have Client Validation, like one that Microsoft uses for MVC, works with ubobtrusive library created by itself for integrating with DataAnnotations. But, after some years of working with this helpful tool, I tired of it which is boring and tedious to employ in cases that we need separate ViewModels (and likely separate ViewModels for create/edit templates).
Another way is using MVVM which works well with MVC since the two paradigms are quite similar. In MVC you have a Model that is bounded just on the server side when the client send content to the server. While MVVM binds a local model with the UI directly on the client. Take a look at the Knockoutjs, the known one which help you to understand how to work with MVVM.
With this in mind, I'll answer your questions in order:
You can't centralize validation rules in the application unless by
creating shared classes and reuse them by calling in separate
Models/ViewModels.
If you wanna use Microsoft Validator, separating the Add/Edit
ViewModels is a best option because of its readability and easier way to
change.
I never said that the Knockoutjs is better, they are different
from each other, just gives you some flexibility to create views
based on model requirements. This also take you away from
centralizing validations :(

ASP.net WebApi Parameter Binding to complex type from URI

I want to create a REST webservice using ASP.net WebApi on .net 4.5
Urls should be of a format like this:
/values?Age=55&Height=176&Race=1&Weight=80&Gender=male&id=800001
The associated controller looks like this:
[HttpGet]
public string Get(int id, [FromUri]Demographics demographics)
{ // etc.}
Where Demographics is a custom object that is basically just a DTO with certain properties.
However, it contains one property that has the type of a custom enum:
enum Gender
{
Female = 0,
Male
}
The default mapping works just fine if the URL is of the above format.
However, of course I also need to check whether the parameters provided by the url are correct.
ASP.net WebApi (afaik) per default tries to map each parameter based on the assumed type (or if it can be converted to that type). If it can't find a matching value in the URI it appears to assume it is just 0.
Now this takes me to a very unfortunate situation where 0 is by definition still a valid value for Gender Demographics.Gender (0 ~ Gender.Female).
The simplest solution would be to change the enum so that 0 would be a "indeterminate" state that I could check for. However, I can not change the enum.
I could create my own overload for the Demographics object but would rather not do this, because I think there must be a nicer way.
Can I explicitely bind the gender parameter to a parameter in the URI and throw an exception if it was not submitted?
I read about type converters but I would have to work on the URI string and think I would have to implement a lot functionality that WebApi apparently already has.
Please keep in mind that I have to work on the URI and can not use the Request Body.
How about making the enum nullable like this?
public class Demographics
{
public Gender? Gender { get; set; }
}
Inside the action method, you can just check if demographics.Gender is null or not.
WebAPI contains a validation framework through data annotations. This allows you to mark the Gender-property in the Demographics object as [Required]:
public class Demographics
{
[Required]
public Gender Gender { get; set; }
}
Then, mark the controller or the controller method with the [ValidateModel] attribute to force validation (read about other ways to force this, e.g. globally: model validation):
[HttpGet]
[ValidateModel]
public string Get(int id, [FromUri]Demographics demographics)
{ // etc.}
After this, if you miss to provide the parameter, you'll get automatic validation and an error 400 Bad Request with a message:
{"Message":"The request is invalid.","ModelState":{"Gender":["The Gender field is required."]}}
Invalid values are handled correctly by default, even without the required attribute, so an incorrect value of Foo provided to the gender is responded also with a 400 Bad Request message:
{"Message":"The request is invalid.","ModelState":{"Gender":["The value 'Foo' is not valid for Gender."]}}

MVC3: How to change the generic [Required] validation message text?

When you decorate a model object's property with the Required attribute and don't specify ErrorMessage or ResourceType/Name you get the validation message in the interpolated form of "The {0} field is required.", where param 0 is the value of the DisplayName attribute of that property.
I want to change that default string to something else but I want to keep the generic nature of it, that is I don't want to specify ErrorMessage or ResourceType/Name for every property of the model object. Where is the default string stored and how can I change it?
Deriving your own attribute is a fair option and probably has the lowest overhead to get started, but you'll need to go back and change all your existing uses of [Required]. You (and any others on your team) will also need to remember to use (and teach newcomers to use) the right one going forward.
An alternative is to replace the ModelMetadataProviders and ModelValidatorProviders to return strings from a resource file. This avoids the drawbacks above. It also lays the groundwork for replacing messages for other attributes (e.g., MaxLengthAttribute) and for supporting additional languages.
protected void Application_Start()
{
var stringProvider = new ResourceStringProvider(Resources.LocalizedStrings.ResourceManager);
ModelMetadataProviders.Current = new LocalizedModelMetadataProvider(stringProvider);
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider(stringProvider));
}
Here is the full source, documentation, and a blog post describing the usage.
Have you tried creating a derived class of RequiredAttribute and overriding the FormatErrorMessage method? This should work:
public class MyRequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(string.Format("This is my error for {0}", name));
}
}

Implementing a class-level validation attribute that writes to a property inside the class

I'm building an ASP.NET MVC site where I want to decorate my ViewModels with validation attributes. One of the things I want to validate is that the address that a user submits through a form is geocodable. For that, I have already created a custom ValidationAttribute and have applied to my StreetAddress property.
This is all good, except that I am actually making two geocoding requests - one in my ViewModel for validation and another in my Controller to input the latitude and longitude into my database. I want to cut down on unnecessary network usage and delays, so I need to pass the
result from the validation geocode into my controller.
To accomplish such a thing, I think I should create a Latitude and Longitude property in my ViewModel. The View itself won't touch these 2 properties, but the validation attribute will either report a failure in geocoding and return the View or write the results into the properties.
For a validation attribute to access 3 properties, it has to be applied to the whole class. I don't know how to do that yet, but that's what this question is for.
UPDATE: Thanks to this answer, I have figured out how to create a class-level validation attribute. The link in the answer also demonstrates how to read the contents of a property inside the class (via Reflection). I still haven't figured out how to write to a property, though.
My Questions
How do I create a ValidationAttribute that can be applied to a whole class? Below, I have posted the code that I want to transfrom into an attribute that can be applied to my ViewModel:
public class GeocodableAttribute : ValidationAttribute
{
public GeocodableAttribute() : base()
{
ErrorMessage = "We weren't able to find that location.";
}
public override bool IsValid(object value)
{
if (value == null) //we don't care if it's required or not.
{
return true;
}
var address = (string)value;
var response = Geocoder.CallGeoWS(address.Trim());
if(response.Status=="ZERO_RESULTS")
{
return false;
}
return true;
}
}
How do I have the attribute write to certain properties in the class that it is applied to? When I apply this attribute to my ViewModel, I want it to write successful geocoding results into two properties and return true. How can I implement that?
UPDATE #2: I think I just figured out how to write to a property. Should the following code work?
private static void WritePropertyValue(object obj, string propertyName, object valueToWrite)
{
if (obj == null) return null;
var type = obj.GetType();
var propertyInfo = type.GetProperty(propertyName);
if (propertyInfo == null) return null;
propertyInfo.SetValue(obj, valueToWrite, null);
}
Will this break client-side validation for other attributes/properties? I have other properties in my ViewModel that I have decorated with built-in ValidationAttributes, such as [Required] and [Range]. I have also enabled client-side validation for them. Will applying my new attribute to the whole ViewModel class completely break client-side validation or will validation for the other properties be performed on the client and then total validation will be performed on the server?
1) You can't access the outer class via a property level ValidationAttribute.
You could use a custom model binder to accomplish this. Simply detect the attributes and validate accordingly.
Creating a custom model binder:
http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
2) No. Did you try?
This is almost a duplicate. I'd check out the question and answers for my dupe submission. It may contain a separate technique, class level validation, that may do what you need.

NHibernate Validator One Value OR Another

Is it possible with NHibernate validators to get a validator that will validate one or other properties? In the example below either FirstName OR Surname is required.
[OneOrOther("Group")]
public string FirstName {get; set; }
[OneOrOther("Group")]
public string Surname {get; set; }
I think this post (http://devlicio.us/blogs/billy_mccafferty/archive/2009/07/30/writing-a-custom-nhibernate-class-validator.aspx) can be applied nicely in this case. I think the only difference is that the sample uses a mutually exclusive condition (one or the other, but not both -- XOR).
Yes you can create a property on the object to test whether the condition holds true and then use one of the nhibernate decorators to check that it is valid i.e. true.
From the documentation:
AssertTrue property check that the method evaluates to true (useful for constraints expressed in code rather than annotations)
This is by far the simplest method to implement the particular problem you have described, you can create a custom class level validator but hey its maybe more work than you need.

Categories

Resources