How can i run all DataAnnotation validations on model?
I'm building a model instance from code and i don't have no modelstate binding or anything. I just want to run all my validations against it... I am using EF CodeFirst.
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
cat = new Category();
if (cat.IsValid()) { /* blah */ } // i want something like this
I know it's probably a stupid question but i can't seem to find an answer anywhere..
This is similar to this question about unit testing data annotations. You could add an extension method similar to this:
public static class ValidationExtension {
public static bool IsValid<T>(this T model) where T: class {
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, validationContext, validationResults, true);
return validationResults.Count == 0;
}
}
The title of this question includes ASP.net MVC.
Please be aware that the Validator class and MVCs validation has subtle differences.
For example:
DataAnnotations.Validator doesn't support buddy class out of box.
MVC can be configured to use another validation framework for example FluentValidation.
If you want to run MVC's validation and populate the ModelState, you can call the TryValidateModel or ValidateModel.
if you don't want to populate the ModelState, use this code snippet in your controller.
var metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
ModelValidator.GetModelValidator(metadata, ControllerContext).Validate(null);
Related
From what I can tell, ASP.Net Core performs model state validation before calling the relevant controller action method. This means that code in the action method isn't given an opportunity to add data to the model before it is validated.
What is the ASP.Net Core way of giving a view model access to additional, non-user-submitted data prior to validation?
Example
What I'm trying to do (doesn't work).
The view model's Validate method expects data to be in ValidOptions. However, since validation occurs before the controller can set this property, validation causes the view model to throw an ArgumentNullException.
// From the Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Process([Bind("SelectedId")]ViewModels.Import details)
{
// data needed for validation
details.ValidOptions = await service.ImportTypes.ToListAsync();
if (ModelState.ValidationState != ModelValidationState.Valid) {
// ...
}
}
// From ViewModels.Import
public IEnumerable<Option> ValidOptions { get; set; }
public int SelectdId {get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// throws ArgumentNullException because ValidOptions hasn't been set when this is executed
var option = ValidOptions.Single(t => t.Id == SelectdId);
//...
}
Probably many ways to skin a cat here. But the easiest for you is probably custom model binders. It's a way to "supplement" or change the binding of your model before it hits the controller. I will say that some see it as extremely bad practice to call an external service/repository at the point of model binding, but it does work and can come in handy.
You need to implement a class that inherits from IModelBinder.
public class MyViewModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
//Bind here. Including calling external services if you want.
}
}
Then you need to implement a provider, this essentially says "when" to bind.
public class MyViewModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(MyViewModel))
return new MyViewModelBinder();
return null;
}
}
In your configure method of your startup.cs, you need to add the provider to the ModelBinderProviders list.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(config =>
config.ModelBinderProviders.Add(new MyViewModelBinderProvider())
);
}
Further Documentation :
http://dotnetcoretutorials.com/2016/12/28/custom-model-binders-asp-net-core/
http://intellitect.com/custom-model-binding-in-asp-net-core-1-0/
I don't think the official documentation has an article on custom model binders yet unfortunately.
Hi I would want to have validations something of this sort
[RequiredCustom(ActionType=(int)Action.Update, ActionType=(int)Action.Delete)]
public string NotesID { get; set; }
[Required]
[RegularExpression("1|2|3|4")]
public int ActionType { get; set; }
I would want to validate this NotesID only when Updation and Deletion is taking place. I don't need any javascript code for unobtrusive and all. I just want server side validation.
Please don't suggest use of separate models I can't do that. Something similar solution will also do.
Let me know if this requires more clarification, Any help would be greatly appreciated.
If you are ok with placing the validation data annotations on the class itself, you could do something like the answer to this post (by Gary.S) suggests.
Here
In essense, just create a new custom validation attribute and since its an attribute at class level, you will have access to the object itself and hence access to other properties.
You custom attribute should look something like this (Assuming your class name is 'Notes')
public class RequiredCustom: ValidationAttribute
{
List<int> actions;
public RequiredCustom(int[] actions)
{
this.actions = new List<int>();
this.actions.AddRange(actions);
}
public override bool IsValid(object value)
{
bool isValid = true;
Notes testVal = value as Notes;
if(this.actions.Contains((int)testVal.Action))
{
if(string.IsNullOrWhiteSpace(testVal.NotesID))
{
isValid = false;
}
}
return isValid;
}
}
Then, add the validation attribute on the class and send the array of enums you want to test for. This way it will be dynamic enough to send other later. In this case, I am sending an ARRAY of Update and Delete enums since you have a special case for these two.
[RequiredCustom(new int[] {(int)Action.Update, (int)Action.Delete)}]
public class Notes
{
public string NotesID { get; set; }
[Required]
[RegularExpression("1|2|3|4")]
public int ActionType { get; set; }
}
Remember, this will work only if you set the attribute on the class, not on the NotesId property.
I'm attempting to create my own model validation attributes for an ASP.NET MVC project. I've followed the advice from this question but cannot see how to get #Html.EditorFor() to recognise my custom attributes. Do I need to register my custom attribute classes in the web.config somewhere? A comment on this this answer seems to be asking the same thing.
FYI the reason I'm creating my own attributes is because I want to retrieve the field display names and validation messages from Sitecore and don't really want to go down the route of creating a class with a ton of static methods to represent each text property, which is what I'd have to do if I were to use
public class MyModel
{
[DisplayName("Some Property")]
[Required(ErrorMessageResourceName="SomeProperty_Required", ErrorMessageResourceType=typeof(MyResourceClass))]
public string SomeProperty{ get; set; }
}
public class MyResourceClass
{
public static string SomeProperty_Required
{
get { // extract field from sitecore item }
}
//for each new field validator, I would need to add an additional
//property to retrieve the corresponding validation message
}
This question has been answered here:
How to create custom validation attribute for MVC
In order to get your custom validator attribute to work, you need to register it. This can be done in Global.asax with the following code:
public void Application_Start()
{
System.Web.Mvc.DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof (MyNamespace.RequiredAttribute),
typeof (System.Web.Mvc.RequiredAttributeAdapter));
}
(If you're using WebActivator you can put the above code into a startup class in your App_Start folder.)
My custom attribute class looks like this:
public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
private string _propertyName;
public RequiredAttribute([CallerMemberName] string propertyName = null)
{
_propertyName = propertyName;
}
public string PropertyName
{
get { return _propertyName; }
}
private string GetErrorMessage()
{
// Get appropriate error message from Sitecore here.
// This could be of the form "Please specify the {0} field"
// where '{0}' gets replaced with the display name for the model field.
}
public override string FormatErrorMessage(string name)
{
//note that the display name for the field is passed to the 'name' argument
return string.Format(GetErrorMessage(), name);
}
}
Using MVC3 and EF4.1 how do I validate on client and server more than one field in my view model?
I have a start date text box (that can be modified) and I have the original start date in a hidden field. When the user submits the form I want to check that the modied start date is no more than one month either side of the original start date.
I can't figure out how this can be done with DataAnnotation and CustomValidation (or maybe I'm going down the wrong road)? This is an example of whay I've been working with:
[MetadataType(typeof(Metadata.MyUserMetaData))]
public partial class MyUser
{
public System.DateTime DateOfBirth { get; set; }
}
Partial Class
public class MyUserMetaData
{
[CustomValidation(typeof(AmendedStartDate), "amendedstartdate", ErrorMessage = "Invalid date."]
public DateTime StartDate { get; set; };
public DateTime OriginalStartDate { get; set; };
}
Custom Validator
public class AmendedStartDate : ValidationAttribute, IClientValidatable
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// How do I get multiple field values from object value?
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(Modelmetadata metadate, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "amendedstartdate"
};
yield return rule;
}
}
I know I've still to add jQuery to the view for this validator.
Instead of using data annotations implement IValidatableObject on your model class - it is simpler and much more clear in scenarios with cross validation.
If you still want to use ValidationAttribute you have two parameters in the IsValid method:
value represents validated value of the property where the attribute is assigned
context is context in which the property is validated. It also contains ObjectInstance and ObjectType properties to access the whole model and its type so you can cast the instance and access other properties.
The question asked in MVC custom validation: compare two dates has an example of a validator which compares to a second value in the model. That should get you started.
Since now I've used the excellent FluentValidation
library to validate my model classes. In web applications I use it in conjunction with the jquery.validate plugin to perform client side validation as well.
One drawback is that much of the validation logic is repeated on the client side and is no longer centralized at a single place.
For this reason I'm looking for an alternative. There are many examples out there showing the usage of data annotations to perform model validation. It looks very promising.
One thing I couldn't find out is how to validate a property that depends on another property value.
Let's take for example the following model:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
public DateTime? EndDate { get; set; }
}
I would like to ensure that EndDate is greater than StartDate. I could write a custom
validation attribute extending ValidationAttribute in order to perform custom validation logic. Unfortunately I couldn't find a way to obtain the
model instance:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// value represents the property value on which this attribute is applied
// but how to obtain the object instance to which this property belongs?
return true;
}
}
I found that the CustomValidationAttribute seems to do the job because it has this ValidationContext property that contains the object instance being validated. Unfortunately this attribute has been added only in .NET 4.0. So my question is: can I achieve the same functionality in .NET 3.5 SP1?
UPDATE:
It seems that FluentValidation already supports clientside validation and metadata in ASP.NET MVC 2.
Still it would be good to know though if data annotations could be used to validate dependent properties.
MVC2 comes with a sample "PropertiesMustMatchAttribute" that shows how to get DataAnnotations to work for you and it should work in both .NET 3.5 and .NET 4.0. That sample code looks like this:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
private readonly object _typeId = new object();
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
: base(_defaultErrorMessage)
{
OriginalProperty = originalProperty;
ConfirmProperty = confirmProperty;
}
public string ConfirmProperty
{
get;
private set;
}
public string OriginalProperty
{
get;
private set;
}
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmProperty);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
return Object.Equals(originalValue, confirmValue);
}
}
When you use that attribute, rather than put it on a property of your model class, you put it on the class itself:
[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public class ChangePasswordModel
{
public string NewPassword { get; set; }
public string ConfirmPassword { get; set; }
}
When "IsValid" gets called on your custom attribute, the whole model instance is passed to it so you can get the dependent property values that way. You could easily follow this pattern to create a date comparison attribute, or even a more general comparison attribute.
Brad Wilson has a good example on his blog showing how to add the client-side portion of the validation as well, though I'm not sure if that example will work in both .NET 3.5 and .NET 4.0.
I had this very problem and recently open sourced my solution:
http://foolproof.codeplex.com/
Foolproof's solution to the example above would be:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
[GreaterThan("StartDate")]
public DateTime? EndDate { get; set; }
}
Instead of the PropertiesMustMatch the CompareAttribute that can be used in MVC3. According to this link http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1:
public class RegisterModel
{
// skipped
[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
public string ConfirmPassword { get; set; }
}
CompareAttribute is a new, very useful validator that is not actually
part of
System.ComponentModel.DataAnnotations,
but has been added to the
System.Web.Mvc DLL by the team. Whilst
not particularly well named (the only
comparison it makes is to check for
equality, so perhaps EqualTo would be
more obvious), it is easy to see from
the usage that this validator checks
that the value of one property equals
the value of another property. You can
see from the code, that the attribute
takes in a string property which is
the name of the other property that
you are comparing. The classic usage
of this type of validator is what we
are using it for here: password
confirmation.
It took a little while since your question was asked, but if you still like metadata (at least sometimes), below there is yet another alternative solution, which allows you provide various logical expressions to the attributes:
[Required]
public DateTime? StartDate { get; set; }
[Required]
[AssertThat("StartDate != null && EndDate > StartDate")]
public DateTime? EndDate { get; set; }
It works for server as well as for client side. More details can be found here.
Because the methods of the DataAnnotations of .NET 3.5 don't allow you to supply the actual object validated or a validation context, you will have to do a bit of trickery to accomplish this. I must admit I'm not familiar with ASP.NET MVC, so I can't say how to do this exactly in conjunction with MCV, but you can try using a thread-static value to pass the argument itself. Here is an example with something that might work.
First create some sort of 'object scope' that allows you to pass objects around without having to pass them through the call stack:
public sealed class ContextScope : IDisposable
{
[ThreadStatic]
private static object currentContext;
public ContextScope(object context)
{
currentContext = context;
}
public static object CurrentContext
{
get { return context; }
}
public void Dispose()
{
currentContext = null;
}
}
Next, create your validator to use the ContextScope:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Event e = (Event)ObjectContext.CurrentContext;
// validate event here.
}
}
And last but not least, ensure that the object is past around through the ContextScope:
Event eventToValidate = [....];
using (var scope new ContextScope(eventToValidate))
{
DataAnnotations.Validator.Validate(eventToValidate);
}
Is this useful?