MVC2: Using DataAnnotations to validate DataType - asp.net-mvc

I am using Entity Framework + SQL Server DB and am using partial classes with DataAnnotations to validate data. For things like Required and Range, this works fine, but I am unable to get the DataType validators to work.
Here is an example of the (custom) annotation:
[DataTypeWholeNumberAttribute(ErrorMessage = "Zip must be a whole number")]
public object Zip{ get; set; }
...and the Controller Code...
[HttpPost]
public ActionResult Edit(NamedInsuredViewModel viewModel)
{
try
{ //breakpoint here (opening squiggly bracket) shows .Zip is already null
if (ModelState.IsValid)
...save, etc...
}
}
And I know what's happening: The DataType of Zip in the database is int, so the default validation is catching that and applying the generic error message "the value [x] is not valid for [FieldName]" before my validator can get to it (to prove this, I also added the same validator to a string field, and it works just fine). What I don't know is, how can I get around that (and no, I can't change the DB to use strings for everything)?
Some suggestions have been offered in this post (http://forums.asp.net/p/1608322/4162819.aspx#4162819), but so far nothing has helped.
Thanks in advance.
PS - is there really no way to validate a primitive DataType without creating a custom Attribute?

I think the error is to pass something called "viewModel" to a Edit Action.
ViewModel is intended for pass data to a view to render it.
When you submit a form the data have to be mapped to a entity not to a viewModel.
[HttpPost]
public ActionResult Edit(YourEntity entity)
{
try
{ //breakpoint here (opening squiggly bracket) shows .Zip is already null
if (ModelState.IsValid)
...save, etc...
}
}

Apply your custom validator to the class. Then pass in your class instance as the parameter for your validator instead of as a string. Then you can perform the validation on the appropriate property regardless of type.

Related

Can an ASP.NET MVC model class have a property called Model?

I've just spent quite some time tracking down a defect where a JSON model parameter to an MVC action method was always null. The cause is different to those raised in other related questions, hence the new question.
My action looks something like this:
[HttpPost]
public ActionResult SendDeviceDetails(DeviceModel model)
{
model.DoStuffHere();
}
And my model class looks like this:
public class DeviceModel
{
public string Manufacturer { get; set; } // e.g. "Asus"
public string Model { get; set; } // e.g. "Nexus 7"
// etc.
}
The model object is posted as a JSON string from a mobile device.
However, the controller action always throws a NullReferenceException because model is always null. I spent some time verifying that the data is sent correctly from the client end before starting to strip down my model class and build it back up again. (In reality it has many more properties than I've shown here.)
What I found was this: if the model class has a property called Model, you will always get a null reference passed to your action method. If I rename that property (e.g. to ModelName), everything works perfectly.
Is this really as insane as it seems to me? Am I missing some good reason for this restriction or is it just a flat-out defect in MVC? And is there any way to work around it if I really want to have a property named Model?
The problem was caused by the property name (DeviceModel.Model) matching the action parameter name (model). Renaming either of them solves the problem. So actually it's OK to have a Model property in your model class, as long as the parameter you pass it as is not called model.
Crazy!

ASP.NET MVC does not deserialize JSON correctly when Conten-Type is missing

Using ASP.NET MVC 4, I have a controller that accepts a JSON structure:
public class SomeDto {
...
public Boolean IsUnicornAlive { get; set; }
...
}
[HttpPost]
public ActionResult DoSomething(SomeDto dto) {
...
}
Now if this POST request comes in without Content-Type specified:
{
...
"IsUnicornAlive":true
...
}
the action still gets called but IsUnicornAlive property would be false. Essentially this is a problem on the side that created the request, but still I would expect ASP to not treat it silently and not call my action with defaulted values. The most appropriate handling in this case would be a 4xx error, 415 ideally. What would be the easiest way to implement it? Or is there a way to know that default values were used inside the controller action?
Note: this question is not about ASP.NET WebApi which does not have this issue. This is specifically about MVC.
How about having a custom ModelBinder for SomeDTO and parsing the incoming data yourself and deciding whether to deduce the values, add a modelstate error, etc...
Look at the ModelState property of the Controller. For example ModelState.Keys.Count == 0 when the default value was used but you might find something even nicer in that object.

asp.net mvc 3 validation on data type

I am trying to realize valition on data type. I have used DataAnnotations, but for data type it's not showing customized message
for example when I' am trying enter string data into int typed field. How I can customize messages in this case?
If I had to guess, you sound like you want a custom message to display when validating one or more fields in your model. You can subclass the DataAnnotations.ValidationAttribute class and override the IsValid(object) method and finally setting a custom ErrorMessage value (where ErrorMessage already belongs to the ValidationAttribute class)
public class SuperDuperValidator : ValidationAttribute
{
public override bool IsValid(object value)
{
bool valid = false;
// do your validation logic here
return valid;
}
}
Finally, decorate your model property with the attribute
public class MyClass
{
[SuperDuperValidator(ErrorMessage="Something is wrong with MyInt")]
public int MyInt { get; set; }
}
If you're using out-of-the-box MVC3, this should be all you need to propertly validate a model (though your model will probably differ/have more properties, etc) So, in your [HttpPost] controller action, MVC will automagically bind MyClass and you will be able to use ModelState.IsValid to determine whether or not the posted data is, in fact, valid.
Pavel,
The DataAnnotations DataType attribute does not affect validation. It's used to decide how your input is rendered. In such a case, David's solution above works.
However, if you want to use only the built-in validation attributes, you probably need to use the Range attribute like this:
[Range(0, 10, ErrorMessage="Please enter a number between 0 and 10")]
public int MyInt { get ; set ;}
(Of course, you should really be using the ErrorMessageResourceName/Type parameters and extract out hard-coded error message strings into resx files.)
Make sure to let MVC know where to render your error message:
<%= Html.ValidationMessageFor(m => m.MyInt) %>
Or you can just use EditorForModel and it will set it up correctly.
I don't think this has been answered because I have the same issue.
If you have a Model with a property of type int and the user types in a string of "asd" then the MVC3 framework binding/validation steps in and results in your view displaying "The value 'asd' is not valid for <model property name or DisplayName here>".
To me the poster is asking can this message that the MVC3 framework is outputting be customized?
I'd like to know too. Whilst the message is not too bad if you label your field something that easily indicates an number is expected you might still want to include additional reasons so it says something like:
"The value 'asd' is not valid for <fieldname>; must be a positive whole number."
So that the user is not entering value after value and getting different error messages each time.

Server-side validation of a REQUIRED String Property in MVC2 Entity Framework 4 does not work

I'm trying to get server-side validation of an Entity Framework String Property to work. Other server-side validation such as data type validation and required dateTime and numeric EF properties are working.
This in VS 2010, .Net 4.0, MVC2 + Cloud, ADO.Net Entity Framework.
The String Property I am having issues with is mapped to a SQL 2008, Varchar(50) non-nullable column.
When I try to post to my Create action with an empty string for this Property, I get the follwing error.
Exception Details: System.Data.ConstraintException: This property cannot be set to a null value.
When I post to the action with a blank space, I successfully get a required field validation message.
I have tried using Data Annotations and ClientSideValidation but there seems to be issues with ClientSideValidation working on partial views and jquery dialogs.
Here is the orginal autogenerated code from the entity framework.
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String GradeTypeName
{
get
{
return GradeTypeName;
}
set
{
OnGradeTypeNameChanging(value);
ReportPropertyChanging("GradeTypeName");
_GradeTypeName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("GradeTypeName");
OnGradeTypeNameChanged();
}
}
Depending on the signature of the Action method (CREATE or EDIT), the exception can occur before stepping into the method or within the method when UpdateModel() is called. The inner exception is at the line below from the model.designer.cs file.
_GradeTypeName = StructuralObject.SetValidValue(value, false);
I have been able to reproduce this on a simple mvc2 web application.
i was having the same problem for a while. I have found a piece of explanation here: http://mvcmusicstore.codeplex.com/workitem/6604 . To put it in a nutshell, the exception "System.Data.ConstraintException: This property cannot be set to a null value" is thrown by Entity's Property Validation. This validation is performed when your mvc application tries to bind the form field to the corresponding entity property( it's called PreBinding Validation, and it occurs when submitting the form). As the field is empty( therefore convert to null), the binder tries to bind a null value to the property, which violates the Non-Null constraint on your entity's property.
But if you post with a blank field ( that is different from empty, therefore null) Entity validation passes( as the property is not set to a null value anymore), and then your see the message from the "Required" annotation validation, that is performed after the prebinding (it's PostBinding Validation).
A workaround is to use the annotation [DisplayFormat(ConvertEmptyStringToNull = false)] that tells to the binder not to convert an empty string to null.
[Required]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string YourStringProperty { get; set;}
Hope, this helps!
This was very helpful. I'm using MVC3 and entity framework. I was passing my entities directly into the controller, but got the same error when the form was blank. With entity framework you can do data validation by editing the auto-generated code, but creating a separate partial class of the entity worked better for me.
[MetadataType(typeof(TestEntityValidation))]
public partial class TestEntity{
}
public class TestEntityValidation{
[Required]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public String name { get;set}
}
Sometimes in database first approach in EF, may you update your column from not null to can be null using SQL query and use 'Update Model From Database...' (in EDMX right click) then maybe property of that entity not updated properly and so if you have some null data in that column ,in mapping ,violation occurs and this error shown.
To fix this; You can check the Nullable in Properties of that property of entity that you updated it.

using IDataErrorInfo in asp.net mvc

I've got a simple address entry app that I'm trying to use the IDataErrorInfo interface as explained on the asp.net site.
It works great for items that can be validated independently, but not so well when some items depend on others. For example, validating the postal code depends on the country:
private string _PostalCode;
public string PostalCode
{
get
{
return _PostalCode;
}
set
{
switch (_Country)
{
case Countries.USA:
if (!Regex.IsMatch(value, #"^[0-9]{5}$"))
_errors.Add("PostalCode", "Invalid Zip Code");
break;
case Countries.Canada:
if (!Regex.IsMatch(value, #"^([a-z][0-9][a-z]) ?([0-9][a-z][0-9])$", RegexOptions.IgnoreCase))
_errors.Add("PostalCode", "Invalid postal Code");
break;
default:
throw new ArgumentException("Unknown Country");
}
_PostalCode = value;
}
}
So you can only validate the postal code after the country has been set, but there seems to be no way of controlling that order.
I could use the Error string from IDataErrorInfo, but that doesn't show up in the Html.ValidationMessage next to the field.
For more complex business rule validation, rather than type validation it is maybe better to implement design patterns such as a service layer. You can check the ModelState and add errors based on your logic.
You can view Rob Conroys example of patterns here
http://www.asp.net/learn/mvc/tutorial-29-cs.aspx
This article on Data Annotations ay also be useful.
http://www.asp.net/learn/mvc/tutorial-39-cs.aspx
Hope this helps.
Here's the best solution I've found for more complex validation beyond the simple data annotations model.
I'm sure I'm not alone in trying to implement IDataErrorInfo and seeing that it has only created for me two methods to implement. I'm thinking wait a minute - do i have to go in there and write my own custom routines for everything now from scratch? And also - what if I have model level things to validate. It seems like you're on your own when you decide to use it unless you want to do something like this or this from within your IDataErrorInfo implementation.
I happened to have the exact same problem as the questioner. I wanted to validate US Zip but only if country was selected as US. Obviously a model-level data annotation wouldn't be any good because that wouldn't cause zipcode to be highlighted in red as an error. [good example of a class level data annotation can be found in the MVC 2 sample project in the PropertiesMustMatchAttribute class].
The solution is quite simple :
First you need to register a modelbinder in global.asax. You can do this as an class level [attribute] if you want but I find registering in global.asax to be more flexible.
private void RegisterModelBinders()
{
ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
}
Then create the modelbinder class, and write your complex validation. You have full access to all properties on the object. This will run after any data annotations have run so you can always clear model state if you want to reverse the default behavior of any validation attributes.
public class AddressModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
// get the address to validate
var address = (Address)bindingContext.Model;
// validate US zipcode
if (address.CountryCode == "US")
{
if (new Regex(#"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).
Match(address.ZipOrPostal ?? "").Success == false)
{
// not a valid zipcode so highlight the zipcode field
var ms = bindingContext.ModelState;
ms.AddModelError(bindingContext.ModelName + ".ZipOrPostal",
"The value " + address.ZipOrPostal + " is not a valid zipcode");
}
}
else {
// we don't care about the rest of the world right now
// so just rely on a [Required] attribute on ZipOrPostal
}
// all other modelbinding attributes such as [Required]
// will be processed as normal
}
}
The beauty of this is that all your existing validation attributes will still work - [Required], [EmailValidator], [MyCustomValidator] - whatever you have.
You can just add in any extra code into the model binder and set field, or model level ModelState errors as you wish.
Please note that for me an Address is a child of the main model - in this case CheckoutModel which looks like this :
public class CheckoutModel
{
// uses AddressModelBinder
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
// etc.
}
That's why I have to do bindingContext.ModelName+ ".ZipOrPostal" so that the model error will be set for 'BillingAddress.ZipOrPostal' and 'ShippingAddress.ZipOrPostal'.
PS. Any comments from 'unit testing types' appreciated. I'm not sure about the impact of this for unit testing.
Regarding the comment on Error string, IDataErrorInfo and the Html.ValidationMessage, you can display object level vs. field level error messages using:
Html.ValidationMessage("address", "Error")
Html.ValidationMessage("address.PostalCode", "Error")
In your controller decorate the post method handler parameter for the object with [Bind(Prefix = "address")]. In the HTML, name the input fields as such...
<input id="address_PostalCode" name="address.PostalCode" ... />
I don't generally use the Html helpers. Note the naming convention between id and name.

Resources