Whenever there is some custom validation to do or any fiddling with ModelState I had to rely on magic strings (that reflect property names) so far. Surely there must be a better way.
say you have a form with 2 submit buttons. You've already set validation rules by adding Required attributes:
public class MyModel
{
[Required]
public string ValueOne { get; set; }
[Required]
public string ValueTwo { get; set; }
}
This will always validate both fields. But say I've added two buttons to the form showing and editor for above model. Button one only requires ValueOne and button two only required ValueTwo.
Typically I would have custom validation code that checks which button was clicked and do something along the lines:
private void ValidateViewModel(MyModel viewModel)
{
foreach (var key in ModelState.Keys)
ModelState[key].Errors.Clear();
if (Request[{ButtonOneName}] != null && string.IsNullOrEmpty(viewModel.ValueOne))
ModelState.AddModelError("ValueOne", "Required");
else if (Request[{ButtonTwoName}] != null && string.IsNullOrEmpty(viewModel.ValueTwo))
ModelState.AddModelError("ValueTwo", "Required");
}
Not very pretty, I know but ... My beef is with magic string "ValueOne" and "ValueTwo". Also with the way errors are cleared out. Is there a way to generate those keys? I'm looking for something like:
ModelState.KeyFor<MyModel>(m => m.ValueOne)
And then a logical extension:
ModelState.Get<MyModel>(m => m.ValueOne)
Before I start reinventing the wheel - is there something like this hidden somewhere already?
Before you ask, I normally define static class SubmitActions, containing constant strings that represent submit button names. And no, I can't split it up into multiple forms because of the was the view is rendered.
Thanks for any suggestions.
There is a Model Validation Improvements in MVC 3, which will make you be able to check validate based on property related to each other
So by using the IValidatableObject interface built-into .NET 4 to implement a custom validation method on a class. This method can apply validation rules across multiple properties and yield back multiple validation errors,
Have you look at it?
Model Validation Improvements part
Related
Has anyone found a trick for handling multiple forms on a Razor Page?
My page has two forms, each with a corresponding model that is decorated with a BindProperty attribute.
[BindProperty]
public TripDetailsUpdateDto UpdateTrip { get; set; }
[BindProperty]
public TripNoteUpdateDto UpdateNote { get; set; }
The problem is that, although either one works fine on its own, having both of them causes ModelState.IsValid to return false. Both models are combined and when one model is submitted, the properties of the other model haven't been set.
Surely I'm not the first to struggle with this. Is there a way to deal with this case without writing manual code to remove the unused items from ModelState?
So, as suggested, the problem is that every property of every model decorated with the [BindProperty] is combined into the ModelState.
To resolve the issue, first remove all [BindProperty] attributes.
Then bind to your values with a parameter:
public async Task<IActionResult> OnPostUpdateNoteAsync(int noteId, TripNoteUpdateDto updateNote)
{
// ...
}
Notes:
You can still have the original model members (in my case, UpdateTrip and UpdateNote). You can still reference them from your markup. This allows your markup to consider validation attributes, and also lets you specify default values.
If your markup references your original model members, your model argument must have the same name in order to match.
I somewhat can't wrap my head around it.
Lets assume simplest model possible:
public class Model
{
[Required]
[MaxLength(128)]
public string Name {get;set;}
}
If you now use it in form and declare validation, it will work. But default messages are not the most pleasant for average user (Field must be an array of length X etc).
And now comes my question, how to create custom validation error? I have seen one useful topic that I can't find anymore, but they were overriding some function and there was no info provided how to call it.
I'm mostly interested in MaxLength, because for Required you can just set Display, which won't work for MaxLength.
If you only want to change the default message try it with
[Required]
[MaxLength(128, ErrorMessage = "YourCustomMessageString")]
public string Name {get;set;}
I have several fields that only need to be displayed depending on selections in other fields. When these fields are displayed, they are also required. First, I tried to just hide the field in the view. But, the field is set to be required in the view model, so the ModelState comes back as invalid since the field is required. For now, I am creating several different view models to handle all the different possibilities, but this is getting cumbersome since there are dozens of variations for the form in the view. Is there some better way to get the results I need? For example, could I use just one model with all the fields there, then hide them in the view, but only make them required when they need to be displayed? Maybe I could add the data annotation in the ActionResult in the controller dynamically?
I found another post that might be the same question here: ASP.NET MVC 3: programmatically add DataAnnotation (RequiredAttribute) to view model
You can implement IValidatableObject. This way you can get conditional validation on your model properties.
public class MyViewModel : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Condition here)
{
yield return new ValidationResult("Validation error");
}
if (Other Condition here)
{
yield return new ValidationResult("Other Validation error");
}
}
}
Another option is MVC Foolproof Validation.
I am using from attribute validation in my project.
[Required(ErrorMessage = "DepartmentCode is Required")]
public string DepartmentCode { get; set; }
In some case DepartmentCode isn't required. How can I dynamically ignore Validation in my case?
Take a look at: Remove C# attribute of a property dynamically
Anyway I think the proper solution is to inherit an attribute from RequiredAttribute and override the Validate() method (so you can check when that field is required or not). You may check CompareAttribute implementation if you want to keep client side validation working.
Instead of dynamically adding and removing validation, you would be better served to create an attribute that better serves this purpose.
The following article demonstrates this (MVC3 with client-side validation too):
http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
I would remove the RequiredAttribute from your model and check it once you've hit your controller and check it against whatever causes it to not be required.
If it falls into a case where it is required and the value is not filled in, add the error to the ModelState manually
ModelState.AddModelError("DepartmantCode", "DepartmantCode is Required");
You would just lose the validation on the client side this way
I've got round this issue in the model, in some cases it's not ideal but it's the cheapest and quickest way.
public string NonMandatoryDepartmentCode
{
get
{
return DepartmentCode;
}
set
{
DepartmentCode = value;
}
}
I used this approach for MVC when a base model I inherited contained attributes I wanted to override.
I have a model class :
public class YearlyChageRate
{
public int Year { get; set; }
public double Rate { get; set; }
}
and I want to check that Yeae is unique or no and in condition Year is not unique application show an error message to users.How can I check the Year filed is repeated or not?
Here is a good example:
http://tugberkugurlu.com/archive/asp-net-mvc-remote-validation-for-multiple-fields-with-additionalfields-property
And here too: MVC validation for unique
You can use Remote attribute in your model to perform check for unique value in database.
This is official example of Remote attribute: http://msdn.microsoft.com/en-us/library/gg508808(v=vs.98).aspx
And one more: http://www.a2zdotnet.com/View.aspx?Id=198
You could use the [Remote] validation attribute on your view model.
Although you can use DataAnnotations attributes for validation and the [Remote] attribute for checks against the DB, it's not a very good design choice.
Let me explain:
data access is a data-layer matter
validation is a business-layer matter
user input and feedback is a ui matter
With DataAnnotations, you're mixin 3 in 1. It can be faster, but surely not well designed.
You could try a more disciplinate approach, like this:
Have a method at business level that will take your object as a parameter, perform validation internally using a validation framework of your choiche;
This method will call the data access to persist the object only if the validation passed;
This method will always return to the UI the validated object, plus a collection of fields/errors if anything didn't validate;
When you read the output of the method in your ui, you can either display a success page if there were no errors, or redisplay the form with the validation errors returned. To do this, the use of the PRG pattern is highly recommended, as you should never display a page on a POST method. Google for the PRG pattern to learn more about it. MvcContrib has a nice ActionFilter called ModelStateToTempData to make the implementation of the PRG pattern something trivial.