Thursday, December 13, 2007

How To: Validation using ASP.NET MVC

Thanks to Scott Guthrie's series of posts on the ASP.NET MVC Framework, I was able to pretty quickly get started once the first CTP dropped.  However, I was still having trouble getting my head around how scenarios like validation may work in MVC, so I decided to try coding a simple validated form.  It goes without saying that this is subject to change and provided as-is, but here's my first stab at it.

Overview

In many cases, we're used to validation being part of the User Interface.  We drag a control that tells ASP.NET "this text box is required.  Set a few properties, and it's done.  This is convenient and may work in many cases. However this may not be the best place for this sort of logic.  If you have multiple UIs, then you have to code this logic in each one.  If rules change, then you have to find the pages that have the rules and change them. If your code is of any size, then you may want to consider putting these rules in a business layer. In MVC, they belong on the model - those classes that represent data in your application.  Then it becomes the View's role to display any errors to the user and except input to correct the broken rules.  The Controller's job is to mediate between the View and the Model and decide what to do with invalid or valid Model instances.

Setting Up The View

The MVC Web Application project gets you started with a simple company website.  For this example, we'll create a "Contact Us" form that includes validation to prevent incorrect email addresses or empty messages.  So, the first thing to do is to create the view: rt click Views\Home -> New Item -> MVC View Content Page -> (ContactUs.aspx) -> Add

In MVC, the View is a User Interface that interacts with a Model to display data and get input.  For this form, we want the View to work against a ContactForm class, which will be our model.  So, we specify in the page's code behind that this is a "ViewPage of type ContactForm":

public partial class ContactUs : ViewPage<ContactForm>

This will make the page's ViewData property be of type ContactForm.  We'll create the ContactForm class in a minute.  To really know what needs to go on the ContactForm class, we need to finish up the view.  Reference MvcToolkit and add this to bring in the HtmlHelper extension methods:

using System.Web.Mvc.BindingHelpers;

Next, add the following to the page's markup:

<%using(Html.Form("SendContactForm","Home", FormExtensions.FormMethod.post)){ %>
<%if (!ViewData.IsValid){ %>
<ul><%=ViewData.ValidationErrors.ToFormattedList("<li>{0}</li>")%></ul>
<%} %>
<label for="FromEmailAddress">Your Email Address</label><br />
<%=Html.TextBox("FromEmailAddress", ViewData.FromEmailAddress) %><br />
<label for="Subject">Subject</label><br />
<%=Html.TextBox("Subject", ViewData.Subject) %><br />
<label for="MessageBody">Message</label><br />
<%=Html.TextArea("MessageBody", ViewData.MessageBody) %><br />
<%=Html.SubmitButton() %>
<%} %>

This will render a form based on the ContactForm instance passed to the view.  If the form is not valid, then any errors will be displayed on the view.  Based on this, we now know we need a model with IsValid, ValidationErrors, FromEmailAddress, Subject, and MessageBody properties.

Adding a Self-Validating Model

The View will render a ContactForm instance, which contains information about whether or not it is currently valid.  There are a number of ways to do this.  Microsoft ships IDataErrorInfo in the framework, and Enterprise Library comes with a validation framework.  I have my suspicions that MVC will ship with something to address this in the future.  While it seems simple, things like globalization and dependency injection come into play and it can get complex quickly.  So for now, we're not going to confuse things with an elaborate validation framework.  We just want a model class that knows when it's valid and allows the view to get a list of error messages.

Here's a simple ContactForm class that does just that:

public class ContactForm
{
    private IList<string> _ValidationErrors = new List<string>();
   
    public IEnumerable<string> ValidationErrors
    {
        get
        {
            return _ValidationErrors;
        }           
    }
    public bool IsValid { get; set; }
    public string FromEmailAddress { get; set; }
    public string Subject { get; set; }
    public string MessageBody { get; set; }

    public void Validate()
    {
        _ValidationErrors.Clear();
        if (String.IsNullOrEmpty(MessageBody)) _ValidationErrors.Add("Message body is required.");
        if (String.IsNullOrEmpty(Subject)) _ValidationErrors.Add("Subject is required.");
        if (String.IsNullOrEmpty(FromEmailAddress))
        {
            _ValidationErrors.Add("Email address is required.");
        }
        else if (!System.Text.RegularExpressions.Regex.IsMatch(FromEmailAddress, @"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b",System.Text.RegularExpressions.RegexOptions.IgnoreCase))
        {
            _ValidationErrors.Add("Email addresses must be in the format 'name@company.com'.");
        }
        this.IsValid = _ValidationErrors.Count == 0;
    }        
}

The only job this class has is to contain ContactForm data, and to know when the data is or isn't valid.  One problem with the above is that it requires the developer to call "Validate()" explicitly, instead of validating when a property value changes. We'll refactor that out later, but for now, this gives us what we need in the model.

Gluing it Together with a Controller

We now have a view to display our model, but the controller isn't yet aware of the views.  To make the HomeController handle requests to /ContactUs and /SendContactForm, add the following methods:

        [ControllerAction]
        public void ContactUs()
        {            
            RenderView("ContactUs", new ContactForm());
        }

        [ControllerAction]
        public void SendContactForm()
        {
            var contactForm = new ContactForm();
            contactForm.UpdateFrom(Request.Form);
contactForm.Validate(); if (!contactForm.IsValid) { RenderView("ContactUs", contactForm); } else { //TODO: Send email RenderView("ContactFormThanks"); } }

The first method simply creates a new instance of ContactForm and passes it to the view.  The second creates a ContactForm instance and updates it from the form input.  If it is not valid, then it returns to the view, passing in the invalid instance.  If it is valid, then it sends the email and renders a "thank you" view.

Now, if we run and browse to /Home/ContactUs, our email form is displayed.  Click 'Submit' with invalid data, and the errors are displayed.  Click 'Submit' with valid data, and we are redirected to the confirmation page.  Add a little CSS, and viola, an email form with validation!

Where to go from Here

This is a simple example of validation using the new MVC framework.  There's plenty of work left, though.  Here are a few things we'll try to hit in a future posts:

  • Refactoring the validation logic to use interfaces and associate error messages with properties.
  • Examine other validation frameworks for use with MVC.
  • Adding Extension Methods to simplify creating forms that display validation errors.
  • Allowing for localization.
kick it on DotNetKicks.com

Testing Out Windows Live Writer

The new version seems to be working with Blogger!image

Friday, November 30, 2007

PLINQ

A CTP of “Parallel Extensions to the .NET Framework”  (aka PLINQ) just dropped today.  It’s stuff like this and SyncLinq that really show off the power of these new language features coming from Redmond.  Using one line of code (or less), you can convert code to take advantage of multiple processors:

 

var filteredPeople = (from person in people.AsParallel()

                              where person.Name.StartsWith(“Bob”)

                              select person).ToArray()

 

or convert it to play nicely with databinding UIs:

 

 var bindablePeople = from person in people.ToSyncLinq()

                              where person.Name.StartsWith(“Bob”)

                              select person

 

I haven’t had a chance to do anything with either besides looking at the docs, but clearly this is exciting stuff, and something to keep on your radar.

 

Wednesday, November 28, 2007

GhostDoc for VS2008

One of my favorite (free) VS add-ins is now available for 2008:

http://www.roland-weigelt.de/ghostdoc/

 

If you’re not familiar with it, GhostDoc adds a “Document This” feature that divines the documentation based on your method or property name.  For example, if the method is named “StartService()”, GhostDoc will add XML documentation summary “Starts the service”.  While some of the generated summaries will need tweaking, this helps you both document faster and get your naming right. 

Sunday, October 28, 2007

Give LINQ a REST

We recently upgraded to FogBugz 6, which sports a new REST styled API.  I decided to play with it by writing a simple .NET client using VS 2008, and take LINQ-to-XML for a spin.  The center piece of LINQ-to-XML is the new XDocument class, which is yet another way to work with XML data in .NET:

 

return XDocument.Load(urlForXmlData);

 

What makes this differ from XmlDocument, XmlReader, etc., is that a LINQ provider has been written around it:

 

var cases = from c in doc.Elements("cases").Elements("cases")

            where c.Element("sTitle").Value.StartsWith(searchText)

            select c;

 

Beyond that, it’s plain easier to use, in my opinion:

 

if (responseElement.Element("error").Attribute("code").Value == "2")

 

With that, it’s fairly easy to come up with a method to send commands and read XML:

 

private XDocument SendCommand(string commandFormatString, params object[] parameterValues)

{           

    var commandUri = this.FogBugzApiUrl.ToString();

    commandUri += String.Format(commandFormatString, parameterValues);

    return XDocument.Load(commandUri);

}

 

This method can then be used to work against the REST API:

 

public bool LogOn(string email, string password)

{

    var responseElement = SendCommand("cmd=logon&email={0}&password={1}", email, password).Element("response");

    if (responseElement.Element("error") == null)

    {

        this._token = responseElement.Element("token").Value;

        return true;

    }

    else

    {

        return false;              

    }

}

 

Where it gets interesting, though, is using LINQ and inline initialization to new up strongly-typed wrappers around the responses:

 

public IEnumerable<Case> GetCases(string search)

{

    var response = SendCommand("cmd=search&q={0}&cols=ixBug,sTitle", search);

    var cases = from c in response.Elements("cases").Elements("case")

                select new Case() { Id = int.Parse(c.Element("ixBug").Value), Title = c.Element("sTitle").Value };

    return cases;

}

 

Hopefully, you can see how powerful LINQ and these other new features in .NET 3.5 are.  In a few lines of code, we can query a REST API and return strongly-typed wrappers around the content.

Tuesday, October 16, 2007

Sandcastle Tip

Sandcastle has come a long way from the early CTPs. It still lacks the slick GUI of good old NDoc, though 3rd party apps provide similar functionality. But really, Sandcastle is designed to be integrated into your build process. The latest CTP ships with a batch file, CompileHelp.bat, that will help you do just that, but there are a few quirks. If yours isn’t outputting any help, or if you’re getting path-related errors, check the following:
  • When supplying the assembly path, leave off “.dll”
  • Call CompileHelp with the current path set to the same folder as your assembly
So, a simple example looks like this: cd c:\projectdir\bin\release CompileHelp.bat vs2005 assemblynamewithoutdotdll

Friday, October 12, 2007

Open Letter to Visual Studio: Please, Please, Fix This Window

Dear Sara, Scott, Scott, and Redmond,

VS2005 made great strides in UI usability for developers. VS2008 makes even more. Microsoft clearly gets this stuff now, and is cranking out don’t-make-me-think UIs like Steve Jobs’ mama. But, somewhere deep in the halls of Redmond sits a developer on the Visual Studio project who answers to no UI Guru, no Usability Cop, no Design Guideline. He wires up his beloved window to SqlDataSource -> Select Query -> Query Builder…, ensures no pane is left usable, turns off ‘maximize’ and ‘persist size’, then clocks out and goes home.

Sara, as a fellow Mississippian, and .NET dev, you owe this to the Magnolia State. Scott and Scott, you’ve thrown yourselves under the train in the community as champions of us little morts banging away code the best we know how. Microsoft, you invented the GUI and Ribbon Interface. Please, Please, Fix This Window:

Sincerely,

Daniel

kick it on DotNetKicks.com

Tuesday, October 2, 2007

Apple iPhone Review - .NET Developer Edition

After the price drop, I got an iPhone “for my wife”.  My 8525 has most of the features of the iPhone and then some.  It has 3G and can stream internet radio.  I have a choice of 3 browsers for it, thousands of other 3rd party apps, and I can write code using the same language I use for my day job.  But I can still do more with less effort on the [wife’s] iPhone.

 

It all comes down to different philosophies on software and hardware.  Apple dictates with iron fist the entire platform from hardware to software, providing developers a limited API for customization.  Microsoft sells the OS and guidelines for hardware, and tools to let developers do just about anything possible with the devices.  Each approach has its merits.  I can sense frustrated Apple developers guiltily sneaking peeks at the .NET Compact Framework after hours, and what .NET dev doesn’t seem a little depressed with their now bulky, unpretty clunker of a phone?

 

Even so, the Windows Mobile platform isn’t dead.  I suspect Microsoft has more than a few developers busy coming up with an answer to the iPhone.  In the meantime, my recommendation to .NET developers considering an iPhone?  Read up:

http://developer.apple.com/documentation/iPhone/Conceptual/iPhoneHIG/

http://developer.apple.com/documentation/iPhone/Conceptual/iPhoneHIG/iPhoneHIG.pdf

Monday, October 1, 2007

Surface

More on Microsoft Surface, including some close up vids of the platform in action.

http://arstechnica.com/articles/culture/surface.ars/1

 

I was a little disappointed that the initial stuff is all in XNA, which is Microsoft’s gaming platform, as opposed to WPF.   Also disappointing is what looks like a pretty closed environment in marketing and developing these.  They’re still saying $5 – 10k, but the initial rollout seems very limited.

 

 

 

Tuesday, September 18, 2007

Reporting Services Build Issues

We’ve been getting more into SharePoint and Reporting Services lately.  In their 3rd versions, these products have matured a good deal and are great solutions for many of our clients.

 

I recently ran across an odd issue with Reporting Services, though, where a site containing a .rdlc report would compile fine on my laptop, but on our build server, it failed with the error:

 

error MSB4062: The "Microsoft.Reporting.RdlCompile" task could not be loaded from the assembly Microsoft.ReportViewer.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Could not load file or assembly 'Microsoft.ReportViewer.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. Confirm that the <UsingTask> declaration is correct, and that the assembly and all its dependencies are available

 

At first I assumed the problem was that Reporting Services wasn’t installed, but even after installing that, the error persisted.  Googling “Microsoft.Reporting.RdlCompile” came up empty.  After a little digging, though, I came across Microsoft’s guidance on deploying reports and viewer controls, which reminded me that the report viewer has a separate installer.  I ran the bootstrap package in C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\ReportViewer\ReportViewer.exe on our build server, and the build cleared up.

 

Tuesday, September 4, 2007

IRootProps

I always enjoy it when the VS IDE gets confused and does this, in the same hip-hoptastic way I enjoy Beasty Boys’ “Root Down”.  I imagine Visual Studio holding up a power fist as it says “I give Mad Props to dat code, Root”.  I got no love for the Linux administrator console prompt though…

 

 

Wednesday, August 8, 2007

VS 2008 Beta 2 Rocks

If you haven't tried VS 2008 Beta 2 yet, you really should. I'm sure they'll find improvements, but compared to where VS 2005 was in Beta 2, this is much, much better. Mine hasn't crashed, and it is as fast, if not faster that VS 2005. In fact, I would say it's stable enough to take advantage of the Go-Live license and begin migration to now, if your environment will allow it.

Friday, July 13, 2007

Windows Live Mobile v2

I started up Windows Live Mobile on my phone, and bing, a new update! Seems even faster with smoother scrolling, and even more user friendly interface. If you haven't tried this- you must. Somebody is dropping the ball advertising this app, because it is clearly 10 times better than the similar Google Java-based offering in terms of speed and usability. A great example of what a good .NET CF app looks like. http://mobile.search.live.com/client/download.aspx

Friday, June 29, 2007

Be Positive

No, I don't mean in a cheesy Disney way. I mean in a nerdy boolean logic way. I'm not sure where I picked up this rule of thumb, but one particularly subtle bit of .NET developer guidance I've started to try to follow is to keep booleans positive. For example: if (myPerson.IsActive) ... //"if the person is active". is easier to think through than: if (! myPerson.IsInactive) ... //"if the person is not inactive". At first pass, this seems nitpicky and maybe even arbitrary. Doesn't this rule just make it longer to test for a negative? if (! myPerson.IsActive) ... But the point isn't so much the amount of code, as it is the the double-negative "not inactive", which burns a few more brain cells to get. An extension of this is when branching: if (myPerson.IsActive) doSomething(); else doSomethingElse(); is better than if (!myPerson.IsActive) doSomethingElse; else doSomething(); Again, the reason isn't so much for the one if..then..else, as it is for the consistency and readability of code as a whole. Anyway, it's helped me =)

Wednesday, June 20, 2007

Spell Check for VS2005

I have horrible spelling, and I know it's crept into some of my applications. FxCop is good for catching it in code, but doesn't help you when it comes to markup inside web applications. I guess I'm a little late finding it, but Mikhail has a pretty good stab at providing this functionality via his Html Spell Check Add In for VS 2005. It does have a couple minor drawbacks. You must run the tool manually each time you want to spell check a file- none of this fancy-pants spell check in the background. (He does have a macro to check all files at once). You also can't add words via the add in- you have to go into Office and add it from there, since it uses Office's dictionary. Those aside, it works as advertised and is something I'll be using frequently. Thanks Mikhail!

Wednesday, June 13, 2007

Windows Presentation Foundation Unleashed

I promised myself I wouldn't buy a Windows Presentation Foundation book until it was fully baked into Visual Studio - with a real designer and freedom from angle-brackets. I know how I am - I was mutzing around with DataGrams before they became DataSets, and ObjectSpaces before they became, well, nothing. I'm an early adopter, and sometimes that's a Bad Thing when it comes to real-world projects and timelines. But then Surface came out, and I just couldn't wait. I downloaded Expression Blend May 2 Preview and on Scott Guthrie's recommendation, bought Windows Presentation Foundation Unleashed and Essential Windows Presentation Foundation. The short review: WPF Unleashed rocks, get it. Essential WPF is decent, but not as good as Unleashed. The longer version: Unleashed is full color- including the code samples- and this makes it much more readable. It does a good job balancing samples and content, and explaining why things in WPF work the way they do. I read Essential WPF second, and so maybe had higher expectations. I do like this Microsoft Development series (Framework Design Guidelines is a must-read for all .NET developers), but this just paled next to Unleashed, and didn't really add or explain much more. The one criticism I have for both books is that I'd like to see more of the whiz-bang examples that makes WPF shine. Unleashed includes a sample Photo Browser app that comes close, but still lacks the 'Wow' that Surface and Silverlight demos are bringing. Some of that is a matter of design principals that fall out of the scope of a technical overview, but this raises my main fear with WPF. Putting this sort of thing in the hands of developers will often lead to trouble. I still cringe when I have to install a video card CD, since I know they've come up with their own goofy UI that will be slower and uglier than a plain WinForms UI. Some effort in these early books to urge restraint and introduce usability and design concerns could help some of us early adopters.

Thursday, June 7, 2007

DanielRoot.com - Take 3

This is about the 3rd or 4th makeover for this site, so I've done it enough to know it's probably not the last. Things are a little different this time around- this will be primarily a .NET development blog (translation: nerd stuff), though I'll no doubt sneak in a few fishing pictures and such from time to time.