Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling required fields in ASP.NET MVC when using Entity Framework

Let me start out by describing my app in a step by step format.

First I have my Entity Framework generated model. Within it I have the following entities:

http://www.codetunnel.com/EFEntities.jpg

Now, to implement validation on an entity, I use partial classes and handle the OnChanging hooks that EF provides. Here is a sample of the PostComment partial class:

namespace CodeTunnel.Domain.Models
{
    public partial class PostComment : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnAuthorChanging(string value)
        {
            //Validating anything but required value works fine.
            //Like this pseudo-validator.
            if (value.ToLower() != "fred")
                _errors.Add("Author", "Sorry, your name must be Fred.");
        }

        partial void OnBodyChanging(string value)
        {
            //Required value validation does not work, I will tell you why below.
            if (string.IsNullOrEmpty(value))
                _errors.Add("Body", "Body is required.");
        }

        #region -= IDataErrorInfo methods =-

        public string Error
        {
            get { return string.Empty; }
        }

        public string this[string columnName]
        {
            get
            {
                if (_errors.ContainsKey(columnName))
                    return _errors[columnName];
                return string.Empty;
            }
        }

        #endregion
    }
}

Notice that my class inherits from IDataErrorInfo. MVC checks this for validation errors by default. If it finds any then it invalidates the model and uses the stored errors as the messages for the validation helpers in the view. I'm pretty sure when EF throws an exception, this dictionary never even gets populated with a value for that property. In fact, I'm not even sure that MVC goes so far as to look for a value after detecting and invalidating the model upon the EF generated exception.

Here is a screen shot of the problem, including the sample pseudo-validator so you can see it is working:

http://www.codetunnel.com/ValidationError.jpg

As you can see, the error message didn't show up. However, it did correctly validate the field as invalid. If I put a value in there then it validates just fine. I've done A LOT of research on this issue and here is what I know:

The reason you cannot customize the error message that is displayed for required fields is because the model property is not nullable. Because it is not nullable, trying to bind a null value to it throws an exception. MVC swallows this excepion and invalidates the model as it should. The message is generated wherever this exception occurs. I don't know if the message is generated by EF or MVC (I'm guessing MVC since it was responsible for swallowing the exception in the first place). I don't know how to customize this message. As you can see it always says "The value '' is invalid." which I suppose isn't totally horrible but it's really not very user-friendly.

It gets to my name validator and checks if your name is "fred" just fine because no exception is thrown. As far as EF is concerned the value passed in is fine. It isn't until it gets to my OnAuthorChanging method that it realizes it's not okay. This is the correct behavior and works just fine.

If I throw an exception in my OnChanging events instead of adding to my IDataErrorInfo dictionary I can generate the same validation message, only it puts the entered value between the single quotes. Example:

    partial void OnAuthorChanging(string value)
    {
        if (value.ToLower() != "fred")
            //_errors.Add("Author", "Sorry, your name must be Fred.");
            throw new Exception("Sorry, your name must be Fred.");
    }

That code shows this message when "test" is entered as the value for Author: "The value 'test' is invalid.". With the required fields it's just that the value is null so there is nothing to put between the single quotes.

Several people (like this guy) suggest that you simply set the property to nullable in your model. This is NOT an acceptable solution. One reason why not is this:

Error 3031: Problem in mapping fragments starting at line 313:Non-nullable column PostComments.Body in table PostComments is mapped to a nullable entity property.

Also, setting nullable simply will not work on non-nullable types such as int, and switching int to a nullable int is a yucky mess that I won't even get into here.

The bottom line is, I want to handle those exceptions from EF myself or at the very least customize the message it generates but I don't know how. I'm completely lost. All I want to do is customize a message on what I thought was very simple model validation!

like image 703
Chev Avatar asked Mar 18 '11 21:03

Chev


People also ask

How can we make field mandatory in MVC view?

Add the "[Required]" attribute to the field that you want to make mandatory for insertion. The required attribute requires the "System. ComponentModel. DataAnnotations" namespace.

Can we use Entity Framework in MVC?

The latest version is Entity Framework 6.0. We are using it in our ASP.NET MVC application. First, we will create a project then adding models to it.

How do you handle validation in MVC?

In code we need to check the IsValid property of the ModelState object. If there is a validation error in any of the input fields then the IsValid property is set to false. If all the fields are satisfied then the IsValid property is set to true. Depending upon the value of the property, we need to write the code.


1 Answers

MVC is validating your model based on the type being non nullible, as you have discovered. This is adding errors to ModelState before your custom validation runs.

I had this before, and got round it by looping through Modelstate at the start of an action and removing everything, then doing my custom validation (bad!!)

Then found even if you are not using data annotations as your main form of validation, you can customise the message that is thrown by adding [Required to the non nullible type in the buddy class, and specify the message.

It's something like this:

[MetadataType(typeof(YourClassMetadata))]
public partial class YourClass
{       
  //buddyclass to entity class
  class YourClassMetadata 
  {
    [Required(ErrorMessage="Your custom overriding error message")]
    public int NonNullablePropertyThatIsGivingYouProblems {get;set;}
  }
}

I've started to look at fluent validation (http://fluentvalidation.codeplex.com) for mvc, and they seem to turn off the problem in global.asax, in on application_start() by adding the line

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

but I may be wrong about that.

like image 166
marcemarc Avatar answered Sep 21 '22 08:09

marcemarc