I am using server side validation like
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty(Name))
yield return new RuleViolation("Name is Required", "Name");
if (Price == 0)
yield return new RuleViolation("Price is Required", "Price");
yield break;
}
When I left Price as blank, Then It takes 0 as a value.
So I check it with 0.
In my Database Price cannot be null; and I am using LINQ-to-SQL class.
Now my problem is when I left Price blank it gives me two messages.e.g.
A value is required.
Price is Required.
So How do I put custom validation without showing first error message?
Relpy to comment
I am reffering book code of Professional Asp.net MVC 1.0 here.
HTML pages of Book are Here.
usefull page.
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation(string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage= errorMessage;
PropertyName = propertyName;
}
}
I think you get the first message "A value is required" automatically from the framework because your Price property is a value type, which can never be null.
So when you post a blank field, the framework will usually try to assign null to this property, which is not possible in this case.
If you change the type to nullable:
public double? Price { get; set; }
That particular message should disappear. Then you could change your validation to:
if (Price == null)
yield return new RuleViolation("Price is required", "Price");
The fact that the database field does not allow nulls should not interfere with your viewmodels.
To make what Thomas Eyde wrote above work (without messing with the code), you can...
Open the corresponding .dbml file
Click the "Price" property
In the Visual Studio Properties window, change the Nullable value from False to True
Save the file!
You can now go into your class and add the if statement and VS should not complain.
That's because the Default Model Binder adds that error. You can write your own model binder for that particular object and work directly with the form collection to get more control over validation.
Related
I have this field that for some reason when I click on submit, gets a validation message that the field is required.
[DisplayName("Total Budget:")]
public double Budget { get; set; }
#Html.EditorFor(model => model.account.Budget)
#Html.ValidationMessageFor(model => model.account.Budget)
public class Account
{
[DisplayName("Total Budget:")]
public double Budget { get; set; } //dropdown
}
The built-in DefaultModelBinder in MVC will perform required and data type validation on value types like int, DateTime, decimal, etc. This will happen even if you don't explicitly specify validation using someting like [Required].
In order to make this optional, you will have to define it as nullable:
public double? Budget { get; set; }
You have to add the following line in the application_start (global.asax)
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
Source: Unrequired property keeps getting data-val-required attribute
double is a value type. Value types always contain a value, even if you did not set one. That value is the default value for it's type (in this case 0.0). All value types are treated as required by the framework. The only way around this is to create a custom model binder, but that will not prevent the model from containing the default value (because there is no way to say that it wasn't entered).
So even if you create a custom binder, when you process your model, you won't be able to tell if someone entered 0 or whether that was just the default value.
Thus, the only real solution is to change your view model to use a nullable type, such as Nullable<double> (shorthand is double?).
You probably change Budget from a double to double?
You probably can try adding this attribute to the controller
BindExclude([Bind(Exclude="Budget")]) as well
Use Nullable or ? after attribute name.
Use [NotMapped] annotation , this removes the required validation in the flow you also use own display attributes
I have just encountered the problem with a project migrated to .Net 6.0, suddenly some fields started to be required even if there's no data annotation for it.
For example, in
public class LoginModel
{
[BindProperty]
public InputModel Input { get; set; }
[TempData]
public string ErrorMessage { get; set; }
...
}
I got the error : "The ErrorMessage field is required", which have of course no sense at all.
The project has <Nullable>enable</Nullable> feature enabled, it seem to cause this.
Simply rewrite property to
public string? ErrorMessage { get; set; }
To fix this (note the question mark)
You may also use:
services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
I would like to use the built-in validation features as far as possible. I would also like to use the same model for CRUD methods.
However, as a drop down list cannot be done using the standard pattern, I have to validate it manually. In the post back method, I would like to just validate the drop down list and add this result to ModelState so that I don't have to validate all the other parameters which are done with Data Annotation. Is it possible to achieve this?
I may be mistaken about the drop down list, but from what I read, the Html object name for a drop down list cannot be the same as the property in the Model in order for the selected value to be set correctly. Is it still possible to use Data Annotation with this workaround?
Thanks.
You can use the addModelError
ModelState.AddModelError(key,message)
when you use that, it will invalidate the ModelState so isValid will return false.
Update
after seeing the comment to #Pieter's answer
If you want to exclude an element from affecting the isValid() result, you can use the ModelState.Remove(field) method before calling isValid().
Another option is to inherit IValidatableObject in your model. Implement its Validate method and you can leave all other validation in place and write whatever code you want in this method. Note: you return an empty IEnumerable<ValidationResult> to indicate there were no errors.
public class Class1 : IValidatableObject
{
public int val1 { get; set; }
public int val2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var errors = new List<ValidationResult>();
if (val1 < 0)
{
errors.Add(new ValidationResult("val1 can't be negative", new List<string> { "val2" }));
}
if (val2 < 0)
{
errors.Add(new ValidationResult("val2 can't be negative", new List<string> { "val2" }));
}
return errors;
}
}
EDIT: After re-reading the question I don't think this applicable to this case, but I'm leaving the answer here in case it helps someone else.
You cannot manually set the ModelState.IsValid property but you can add messages to the ModelState that will ensure that the IsValid is false.
ModelState.AddModelError();
yes, you can achieve this (also you will use the same model for CRUD methods) :
Example MODEL
public class User
{
public virtual int Id{ get; set; }
public virtual Role Role { get; set; }
}
public class Role
{
[Required(ErrorMessage = "Id Required.")]
public virtual int Id { get; set; }
[Required(ErrorMessage = "Name Required.")]
public virtual string Name { get; set; }
}
Example VIEW with validation on the dropdownlist
#Html.DropDownListFor(m => m.Role.Id, (SelectList)ViewBag.gRoles, "-- Select --")
#Html.ValidationMessageFor(m => m.Role.Id)
CONTROLLER: clearing the required (but not needed here) fields
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Creedit(User x)
{
x.Role = db.RoseSet.Find(x.Role.Id);
if (x.Role != null)
{
ModelState["Role.Name"].Errors.Clear();
}
if (ModelState.IsValid)
{
// proceed
}
else
{
// return validation error
}
}
Might be more recent methods, since this is an old post, but this might help future readers.
One can set a field to valid with this two methods:
ModelState.ClearValidationState("Password");
ModelState.MarkFieldValid("Password");
Need to use both because the second one without the first one it gives an error stating that the state is already marked.
To set a field to invalid, just use ModelState.AddModelError() method as already referred.
This is my first question here on stack overflow.
i need help on a problem i encountered during an ASP.NET MVC2 project i am currently working on.
I should note that I'm relatively new to MVC design, so pls bear my ignorance.
Here goes :
I have a regular form on which various details about a person are shown. One of them is "Date of Birth". My view is like this
<div class="form-items">
<%: Html.Label("DateOfBirth", "Date of Birth:") %>
<%: Html.EditorFor(m => m.DateOfBirth) %>
<%: Html.ValidationMessageFor(m => m.DateOfBirth) %>
</div>
I'm using an editor template i found, to show only the date correctly :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>"%>
<%= Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty))%>
I used LinqToSql designer to create my model from an sql database. In order to do some validation i made a partial class Person to extend the one created by the designer (under the same namespace) :
[MetadataType(typeof(IPerson))]
public partial class Person : IPerson
{ //To create buddy class }
public interface IPerson
{
[Required(ErrorMessage="Please enter a name")]
string Name { get; set; }
[Required(ErrorMessage="Please enter a surname")]
string Surname { get; set; }
[Birthday]
DateTime? DateOfBirth { get; set; }
[Email(ErrorMessage="Please enter a valid email")]
string Email { get; set; }
}
I want to make sure that a correct date is entered. So i created a custom DataAnnotation attribute in order to validate the date :
public class BirthdayAttribute : ValidationAttribute
{
private const string _errorMessage = "Please enter a valid date";
public BirthdayAttribute() : base(_errorMessage) { }
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
DateTime temp;
bool result = DateTime.TryParse(value.ToString(), out temp);
return result;
}
}
Well, my problem is this. Once i enter an incorrect date in the DateOfBirth field then no custom message is displayed even if use the attribute like [Birthday(ErrorMessage=".....")]. The message displayed is the one returned from the db ie "The value '32/4/1967' is not valid for DateOfBirth.". I tried to enter some break points around the code, and found out that the "value" in attribute is always null when the date is incorrect, but always gets a value if the date is in correct format. The same ( value == null) is passed also in the code generated by the designer.
This thing is driving me nuts. Please can anyone help me deal with this?
Also if someone can tell me where exactly is the point of entry from the view to the database. Is it related to the model binder? because i wanted to check exactly what value is passed once i press the "submit" button.
Thank you.
Generally speaking all validation stuff is work after binder binded values. As you can understand it's not possible to bind dateTime value from string like "asdvfvk". So, when binder encounters with such an error it adds it to the ModelState (take a look at ModelState["DateOfBirth"].Errors[0].ErrorMessage), and binds default value. Default value for DateTime? is null, so that's why you always get it in IsValid method. It's normal.
So as you can see validation for date has sence if you whant to check for example if it's bigger then some other date. If input string is incorrect no further verification have sence.
What can you do?
First straightforward way - you can correct your action like this
[HttpPost]
public ActionResult About(Person person, string dateOfBirth) {
var birthdayAttribute = new BirthdayAttribute();
if( !birthdayAttribute.IsValid(dateOfBirth)) {
ModelState["DateOfBirth"].Errors.Clear();
ModelState.AddModelError("DateOfBirth", birthdayAttribute.ErrorMessage);
}
.......
}
As you can see there is string dateOfBirth, so binder have no problems with binding string value. But this will not make your users happy.
The better way - ensure that string will be in correct format with client Javascript. Actualy people use date picker controls for dates and feel themselves good.
In addition take a look here http://forums.asp.net/t/1512140.aspx
Especialy Brad Wilson's answer.
I have a Model that is using DataAnnotations. Something like
public class Appointment {
[Required(ErrorMessage="Please enter your name")]
public string Name { get; set; }
[Required(ErrorMessage="Please enter your appointment date?")]
[DataType(DataType.Date, ErrorMessage="Appointment date is not a date")]
public DateTime AppointmentDate { get; set; }
}
The "Required" attributes respect the value in ErrorMessage; that is, if I don't enter a value, I am getting my "please enter" message. However, if I enter a string in the DateTime field, I am getting a standard system error message "The value 'blah' is not valid for AppointmentDate".
I debugged through ASP.NET MVC code, and it seems that in the case of FormatException it doesn't pick the right display name from propertyMetadata. Either that, or I am missing something blatantly obvious :/
Did anybody run into this problem? Is it me, or is it just beta (I am using ASP.NET MVC 2 Beta)?
In MVC1 DataType attribute does not do what you'd expect, it looks like it does not in MVC2 either. Your best call is to have a string property representing the date, check it's validity there.
Here's a small excerpt from a project (using DataAnnotations and xVal):
private List<ErrorInfo> _errors;
private List<ErrorInfo> Errors
{
get
{
if (_errors == null)
_errors = new List<ErrorInfo>();
return _errors;
}
//set { _errors = value; }
}
private string _startDateTimeString;
/// <summary>
/// A string reprsentation of the StartDateTime, used for validation purposes in the views.
/// </summary>
/// <remarks>
/// If the user passes something like 45/45/80 it would be a valid mm/dd/yy format, but an invalid date,
/// which would cause an InvalidOperationException to be thrown by UpdateModel(). By assigning dates to string properties
/// we can check not only the format, but the actual validity of dates.
/// </remarks>
public string StartDateTimeString
{
get
{
return _startDateTimeString;
}
set
{
// Check whether the start date passed from the controller is a valid date.
DateTime startDateTime;
bool validStartDate = DateTime.TryParse(value, out startDateTime);
if (validStartDate)
{
StartDateTime = startDateTime;
}
else
{
Errors.Add(new ErrorInfo("StartDateTime", "Provide a valid date for the start time."));
}
_startDateTimeString = value;
}
}
partial void OnValidate(ChangeAction action)
{
if (action != ChangeAction.Delete)
{
Errors.AddRange(DataAnnotationsValidationRunner.GetErrors(this));
if (StartDateTimeString != null)
{
DateTime startDateTime;
if (!DateTime.TryParse(StartDateTimeString, out startDateTime))
{
Errors.Add(new ErrorInfo("StartDateTime", "Provide a valid date for the start time."));
}
}
if (Errors.Any())
throw new RulesException(Errors);
}
}
}
It makes sense to have the check in both places in our project, but I just want to show you a concept.
I ran into this problem today and I wanted to share another workaround...
[Required(ErrorMessage="Please enter your appointment date?")]
//[DataType(DataType.Date, ErrorMessage="Appointment date is not a date")]
[Range(typeof(DateTime), "1/1/1880", "1/1/2200", ErrorMessage = "...")]
public string AppointmentDate { get; set; }
You will need to adjust your code to work with a string instead of a datetime (presumably easy if this is part of your view model), but the error message works as expected and the string is guaranteed to be a valid date (possibly with a time).
There appears to be something of a hole in the way DataAnnotations works in that a user entering in some text into a field that will go into an int will never reach the DataAnnotations code. It kicks off a model binding error and displays the error to the user "The value 'a' is not valid for the XXXX field."
Anyway, it's all very nice that it automatically handles this situation, but I actually want to display an error message indicating the problem eg. "The value 'a' is not numeric. Please enter in a numeric value for the XXXX field".
I have tried the solutions set out How to replace the default ModelState error message in Asp.net MVC 2? and ASP.NET MVC - Custom validation message for value types, but I can't get them to work.
It appears that my resource file is not being read at all, since here (http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder.resourceclasskey.aspx) it states "If the property is set to an invalid class key (such as a resource file that does not exist), MVC throws an exception." and even if I change the line to DefaultModelBinder.ResourceClassKey = "asdfasdhfk" there is no exception.
Anyone have any ideas?
EDIT: Here is some code. All of it is working minus my Messages.resx file's messages are not being used. The code for Messages.resx is auto generated so I won't include it.
So entering "a" into ProcessOrder results in a generic message rather than what I have entered into Messages.resx for PropertyValueInvalid (and InvalidPropertyValue for good measure).
Application_Start method
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder(); //set dataanooations to be used
DefaultModelBinder.ResourceClassKey = "Messages"; //set data annotations to look in messages.resx for the default messages
ValidationExtensions.ResourceClassKey = "Messages";
}
Entity Class
[MetadataType(typeof(GLMetaData))]
public partial class GL
{
}
public class GLMetaData
{
public int TransRefId { get; set; }
[DisplayName("Process Order")]
public int? ProcessOrder { get; set; }
[DisplayName("Trans Type")]
[StringLength(50)]
public string TransType { get; set; }
[StringLength(100)]
public string Description { get; set; }
[DisplayName("GL Code")]
[StringLength(20)]
public string GLCode { get; set; }
[DisplayName("Agents Credit No")]
[StringLength(50)]
public string AgentsCreditNo { get; set; }
[Required]
public bool Active { get; set; }
}
Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(GL glToBeUpdated)
{
try
{
if (!ModelState.IsValid)
return View(glToBeUpdated);
//set auto properties
glToBeUpdated.UpdateDate = DateTime.Now;
glToBeUpdated.UpdateUser = this.CurrentUser;
glDataLayer.update(glToBeUpdated);
glDataLayer.submitChanges();
return RedirectToAction("Index");
}
catch
{
glDataLayer.abortChanges();
throw;
}
}
What I did to combat a similar issue was to clear the model state, validate against ModelState["XXXX"].Value.AttemptedValue instead of against the nulled value caused by an trying to put an invalid value into the Model's property, populating the error messages and resetting the Model values.
That way I can have the error messages I want and if necessary offer more than one ("a value is required" or "the value must be numeric").
I have battled this for most of the day on MVC4 RC. No matter what i set
DefaultModelBinder.ResourceClassKey
to it never seemed to work. It also never threw an exception when I assigned junk.
This is what I was using to assign the value (to no avail):
DefaultModelBinder.ResourceClassKey = typeof(App_GlobalResources.ValidationMessages).Name;
In the end I decided to tackle this error message on the client side and override the data attribute that jQuery uses to display the message.
#Html.TextBoxFor(m => m.Amount, new Dictionary<string,object>(){{"data-val-number","Invalid Number"}})
this is working how I need it to.
Ironically this works too:
#Html.TextBoxFor(m => m.Amount, new Dictionary<string, object>() {{ "data-val-number", HttpContext.GetGlobalResourceObject("ValidationMessages", "PropertyValueInvalid") } })
Here I have taken Contact number field as string but with Range Attribute so can provide numeric validatioin to use if from your Resource file .
[Required(ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ContactNumberRequired")]
[Range(0, int.MaxValue, ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ValidContactNumber")]
[Display(Name = "Contact Number")]
public string ContactNumber { get; set; }
So now here provided ErrorMessageResourceName as key . You can customize and use it also in Multi Language