Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is currently employed as .NET Technical Consultant at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
View Maarten Balliauw's MVP profile

Search

Latest Twitter

    Follow me on Twitter...

    My projects

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright Maarten Balliauw 2010

    Form validation with ASP.NET MVC release candidate

    Last week, the ASP.NET MVC framework release candidate was released (check ScottGu’s post). Apart from some great new tooling support, form validation has never been easier. Here’s a quick introduction.

    Employee from Northwind database Imagine we have a LINQ to SQL data model, containing an Employee from the Northwind database. As you may know, LINQ to SQL will generate this Employee class as a partial class, which we can use to extend this domain object’s behaviour. Let’s extend this class with an interface implementation for IDataErrorInfo.

    public partial class Employee : IDataErrorInfo
    {
        #region IDataErrorInfo Members

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }

    IDataErrorInfo is an interface definition that is found in System.ComponentModel. It provides the functionality to offer custom error information that a user interface can bind to. Great, let’s do that! Assume we have a view which is used to edit this Employee object. The code for this will be quite easy: some HTML form stuff, Html.ValidationMessage calls, … Here’s a snippet:

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

        <h2>Edit</h2>

        <%= Html.ValidationSummary() %>

        <% using (Html.BeginForm()) {%>
            <%= Html.Hidden("id", Model.EmployeeID) %>

            <fieldset>
                <legend>Fields</legend>
                <p>
                    <label for="LastName">LastName:</label>
                    <%= Html.TextBox("LastName") %>
                    <%= Html.ValidationMessage("LastName", "*") %>
                </p>

                <!-- ... -->

            <fieldset>
        <% } %>

    </asp:Content>

    The controller’s action method for this will look like the following:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection collection)

        Employee employee = repository.RetrieveById(id);

        try
        {
            UpdateModel(employee, collection.ToValueProvider());
            repository.Save(employee);

            return RedirectToAction("Index");
        }
        catch
        {
            return View(employee);
        }
    }

    Nothing fancy here: a call to UpdateModel (to populate the Employee instance with data fom the form) and a try-catch construction. How will this thing know what’s wrong? This is where the IDataErrorInfo interface comes useful. ASP.NET MVC’s UpdateModel method will look for this interface implementation and retrieve information from it. The Error property that is defined on IDataErrorInfo returns a string containing any error that is “global” for the Employee object. The this[string columnName] indexer that is defined on IDataErrorInfo is used to retrieve error messages for a specific property. Now let’s make sure FirstName and LastName are provided:

    public partial class Employee : IDataErrorInfo
    {
        #region IDataErrorInfo Members

        public string Error
        {
            get { return ""; }
        }

        public string this[string columnName]
        {
            get
            {
                switch (columnName.ToUpperInvariant())
                {
                    case "FIRSTNAME":
                        if (string.IsNullOrEmpty(FirstName))
                            return "Please provide a firstname.";
                        break;
                    case "LASTNAME":
                        if (string.IsNullOrEmpty(LastName))
                            return "Please provide a lastname.";
                        break;
                }

                return "";
            }
        }

        #endregion
    }

    Great, let’s try it out. If I omit the firstname or lastname when editing an Employee object, here’s what the view looks like:

    ASP.NET MVC form validation

    How easy was that! More on the new things in the ASP.NET MVC release candidate can be found in ScottGu’s blog post.

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | MVC

    Comments

    DotNetKicks.com | Reply

    Friday, January 30, 2009 1:10 PM

    trackback

    Trackback from DotNetKicks.com

    Form validation with ASP.NET MVC release candidate

    Microsoft RealDolmen blogs | Reply

    Friday, January 30, 2009 1:26 PM

    trackback

    Trackback from Microsoft RealDolmen blogs

    Form validation with ASP.NET MVC release candidate

    DotNetShoutout | Reply

    Friday, January 30, 2009 1:56 PM

    trackback

    Trackback from DotNetShoutout

    Maarten Balliauw {blog} - Form validation with ASP.NET MVC release candidate

    PaulBlamire United Kingdom | Reply

    Friday, January 30, 2009 3:01 PM

    PaulBlamire

    Hi Maarten,

    Good article. Just a thought though, should you not be declaring your employee object outside of the try block so that if the catch clause is invoked you don't need to hit the db again? Suppose it depends on if L2S implements an identity map, and if your repository hold onto the datacontext.

    maartenba Belgium | Reply

    Friday, January 30, 2009 3:17 PM

    maartenba

    Have updated that. Thanks for noticing!

    alvinashcraft.com | Reply

    Friday, January 30, 2009 5:45 PM

    pingback

    Pingback from alvinashcraft.com

    Dew Drop - January 30, 2009 | Alvin Ashcraft's Morning Dew

    Wookie United Kingdom | Reply

    Friday, January 30, 2009 6:54 PM

    Wookie

    Nice work by the MVC team but I think that the solution provided by Steve Sanderson's xVal library is much more elegant; not to mention that it covers both client and server side validation.

    http://xval.codeplex.com/

    maartenba | Reply

    Saturday, January 31, 2009 9:54 AM

    maartenba

    True, but this is still an elegant solution to server side validation.

    trendbender Russia | Reply

    Monday, February 02, 2009 7:41 PM

    trendbender

    thx, good post Smile

    Alberto Ferreira Portugal | Reply

    Friday, February 20, 2009 12:55 PM

    Alberto Ferreira

    Because DefaultBindig check this[string columnName] just after set the property, this approach don't let you validate one property based on another, because it coud be not initialized yet.

    I Think was better to validate all the columnName when Error is called.

    Another think: if DefaultBinding can´t cast to destination type (ex.: unitPrice TextBox with "Hello"), IDataErrorInfo will never be called, so this way we can create our custom message for this kind of error.

    James United Kingdom | Reply

    Friday, February 20, 2009 8:41 PM

    James

    How do you add error messages for the situation where someone enters text in a numeric-only or date field?
    i.e. if you have:
    public ActionResult Add(Employee item)
    {
    }
    and someone puts in "blahblah" into the BirthDate field (which presumably is of type DateTime)...

    The allocation of each field to the employee object happens before the Add method is called, and the ModelState already has an error from the start of the method, but with a blank error message. Is there any way to capture this and add a message in? At the moment i'm identifying the blank error message, removing the error from the dictionary object and adding it back in with a generic message ... all from within the controller... which doesn't seem quite right...

    maartenba Belgium | Reply

    Monday, February 23, 2009 9:07 AM

    maartenba

    James, here's an answer in code format Smile

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(FormCollection collection)
    {
        Note note = new Note();

        try
        {
            UpdateModel(
                note,
                new string[] { "Title", "Body" },
                collection.ToValueProvider()
            );

            noteRepository.Save(note, User.Identity.Name);

            return RedirectToAction("List");
        }
        catch (InvalidOperationException ex)
        {
            // ...................................................
            // Model binding went wrong...
            //
            // You can add some code here for validating tha values
            // provided, or pass the formCollection.ToValueProvider()
            // to the Note class.
            //
            // In there, you can set the ModelState errors.
            //
            // Example:
            //   note.AddModelStateErrors( formCollection.ToValueProvider(), ModelState );
            // ...................................................

            return View(note);
        }
    }

    Jon United Kingdom | Reply

    Saturday, April 04, 2009 11:06 AM

    Jon

    I have the same issue as James but cannot get it to work properly. Any chance you could do a post or show some code exactly how that scenario would work.

    Thanks

    Add comment




      Country flag

    biuquote
    • Comment
    • Preview
    Loading