Posts tagged ‘Asp.Net MVC’

Getting started with MVCContrib: Filters

What is MVCContrib?  MVCContrib is a open source project was designed to add functionality and ease-of-use to the out of the box experience of the ASP.NET MVC Framework.  It kicks in where MVC on its own has left off.  MVCContrib is hosted on CodePlex at http://mvccontrib.codeplex.com and it also a CodePlex Foundation Project.

In this post I’m going to cover a couple of the Action Filters that are included with MVCContrib, ModelStateToTempData and PassParametersDuringRedirect.

ModelStateToTempData

When you have this filter decorating your action method (or at the controller level) you will get a behavior in which that when model state gets created post model binding you might have a state in which you want available to another action method.  This happens when you are practicing the Post-Redirect-Get Pattern (PRG).

Let’s say you have a CustomerEditModel that coming from a web form.  You need a Post Action that takes in a CustomerEditModel and out of the box that DataAnnotations will engage and validate your model.  Below is a model with two attributes, Required and DisplayName.  Required may or may not be what you think it means and DisplayName gives you an opportunity to give your labels a different name than from the reflected property type name when using the template helpers (go read Brad’s series now).

public class CustomerEditModel
{
  [Required]
  [DisplayName("First Name")]
  public string FirstName { get; set; }

  [Required]
  [DisplayName("Last Name")]
  public string LastName { get; set; }
}

[HttpPost]
public ActionResult CreateCustomer(CustomerEditModel customerEditModel)
{
  if (ModelState.IsValid)
  {
    //Do Something Worthy of your NEW Customer
    return RedirectToAction("Success");
  }
  return RedirectToAction("FixErrors");
}

[HttpGet]
public ActionResult FixErrors()
{
  return View("Index");
}

Ok back to the story. When you submitted a nice clean valid model and you flow into the ModelState.IsValid block life is good. When you had some validation issues and you flow to the FixErrors RedirectToAction something particular needs to happen. ModelState has some your validation results that you will want to use to display validation hints back to your end users. The problem is that ModelState isn’t carried over, without any intervention to the next Action method being called.

Enter the ModelStateToTempData Attribute

Add a [ModelStateToTempData] to the action method that the modelstate is being generated from, let’s call it the sender, AND the action method that needs to use it, maybe call it the reciever?

[HttpPost]
[ModelStateToTempData]
public ActionResult CreateCustomer(CustomerEditModel customerEditModel)
{
  if (ModelState.IsValid)
  {
    //Do Something Worthy of your NEW Customer
    return RedirectToAction("Success");
  }
  return RedirectToAction("FixErrors");
}

[HttpGet]
[ModelStateToTempData]
public ActionResult FixErrors()
{
  return View("Index");
}

What’s happening on the sending side with these attributes is when a RedirectToRouteResult is returned from an action (in this case we’re using RedirectToAction) any data that is the ModelState dictionary will be copied into TempData – A.U.T.O.M.A.T.I.C.A.L.L.Y. You don’t have to manually do it yourself!

What’s happening on the receiving side with these attributes is when a ViewResultBase is returned from an action (in this case we’re using View) all the data that was previously put into TempData is copied out of TempData and put back into ModelState

In this case since we’re in the FixErrors action the errors that we just got back from the [ModelStateToTempData] attribute will be displayed on the user form. Additionally we’re also getting back the values of the "bad submission" from the end user so that for non-trivial (or really any) forms they don’t have to re-enter their data.

PassParametersDuringRedirect

Since we live in a perfect world and our information management is so well done that our users get all the forms filled out perfectly that that always drop into the ModelState.IsValid block every time. Maybe we can just render the view from there … NO! My post, my rules. Thou shalt not render view from Post Methods. We have to PRG our way out of there. In this case we’d do something interesting with some persistence and then we’ll take the user to a ‘You Are Teh Awesome‘ page.

Let’s take the same controller action methods but add one more attribute to them, [PassParametersDuringRedirect]

[HttpPost]
[ModelStateToTempData]
[PassParametersDuringRedirect]
public ActionResult CreateCustomer(CustomerEditModel customerEditModel)
{
  if (ModelState.IsValid)
  {
    //Do Something Worthy of your NEW Customer
    return this.RedirectToAction(x => x.Success(customerEditModel));
  }
  return RedirectToAction("FixErrors");
}

[HttpGet]
[PassParametersDuringRedirect]
public ActionResult Success(CustomerEditModel customerEditModel)
{
  return View(customerEditModel);
}

I’d like to point out that the difference between the two RedirectToAction call in the CreateCustomer Action Method. The second one, the one that ships with Asp.NET MVC and the first one has the nice expression in there. This is another nugget that comes with MVCContrib. This flavor of the RedirectToAction extension method will take in a an expression and make sure that the destination method will have the correct parameters passed to it.

The new attribute added to the methods, [PassParametersDuringRedirect] is bringing some more pleasant PRG stuff to the controller. After the valid customer has been ‘persisted’ we want to show the user a Sucess page. In this case we want to show the model (or maybe another model with additional information) thus the method signature as so public ActionResult Success(CustomerEditModel customerEditModel). This is very similar to the [ModelStateToTempData] behaviour.

I hope this information has helped. Just until very recently I didn’t know these things were in the MVCContrib project and now I have some code to delete from our current code base.

Extension Methods: Improving the quality of Life


Extension methods, such a simple idea that can totally make the small things in your application or at least the un-DRY portions of it cleaner.

AddModelError and Multiple ValidationResults

Here is a simple example (taken from a custom model binder):

foreach( var validationResult in validationResults )
{
  bindingContext.ModelState
    .AddModelError( validationResult.MemberNames.FirstOrDefault(), validationResult.ErrorMessage );
}

not too bad, but when working in a block of code where you might have a few instances of it you tend to see the noise a little bit.

How about a little reduction and also centralize some logic so your entire code base can leverage it?

bindingContext.ModelState.AddModelErrors(validationResults);

Now that everything you thought was upright in the world is all in question, ok, not really – not even close. In fact we’ve only saved three lines of code where such things are being used but I would argue if you try to keep small things like this in mind that over time your code base will start to reap the benefits.

Here is how I implemented the extension method. Your mileage may vary.

public static class ModelStateExtensions
{
    public static void AddModelErrors(this ModelStateDictionary values, IEnumerable<ValidationResult> validationResults)
    {
        foreach (var validationResult in validationResults)
            values.AddModelError(validationResult.MemberNames.FirstOrDefault(), validationResult.ErrorMessage);
    }
}
DataAnnotations and the Validator class

Another area that might not be Asp.net MVC specific is if you are using the DataAnnotaions directly on your models and you use the static Validator class to validation the model. In this case the TryValidateObject is being used for the bool that is returned from the operation.

ValidationContext validationContext = new ValidationContext( someEditModel, null, null );
List<ValidationResult> validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject( someEditModel, validationContext, validationResults );
if( !isValid )
{
  //do something maybe ...
  bindingContext.ModelState.AddModelErrors(validationResults);
}

I think I prefer this a little more

List<ValidationResult> validationResults = someEditModel.ValidateAnnotations();
if( validationResults.Count() > 0 )
  bindingContext.ModelState.AddModelErrors(validationResults);

Where ValidateAnnotations is implemented as follows. Also you if you prefer to change the where this can be used you can change the accessibility or change what this method extends to limit it’s exposure.

public static class ValidationExtensions
{
    public static List<ValidationResult> ValidateAnnotations<T>(this T value) where T : class
    {
        if(value == null) return new List<ValidationResult>();

        var validationContext = new ValidationContext(value, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(value, validationContext, validationResults, true);
        return validationResults;
    }
}

Hopefully these two little nuggets will help someone out. Both of these are just a means to end, where I think that end is a transformation to a code base even beyond simple changes like this. After all it is all about the journey not the destination.

C4MVC: Reason #42 to Join

What is C4MVC?  Well per the about us page it is:

Our Vision is to share knowledge and best practices around using the framework, as well as encourage community contributions that serve the greater good.

Through interactive sessions, projects, and aggregated blog feeds we hope to provide:

  • A way for new developers to use the framework the right way.
  • Techniques and tools to allow experienced developers to develop with less friction.
  • A self supporting community.

Per my definition:

A virtual user group on Wednesday mornings where I can listen in on the Asp.Net MVC big guns discuss the topic of the framework that I need to learn better.


Besides the great free content such as last weeks Advanced MVC2 topic sometimes, just sometimes, you can win some stuff.  In my case, I loves me some 30 day subscription to TekPub which also happens to have a lengthy Asp.NET MVC series. Now the only problem I have is finding the correct window to pull the pin this content grenade so that I can put together 30 days of face melting guitar solo mind melding content assimilation.  Parent of 3 (4 and under) makes that a GTD challenge.

The C4MVC.NET (google) group is open to anyone to join so come and join the group to get some good nuggets of Asp.net MVC knowledge on!  No really, join now!

Asp.net MVC Session State Extension Method

There are a few implementations of the conical wizard out there using Asp.net MVC.  SessionsI’m working on a project where we are needing one such wizard and we’re trying out using session state, not session skate, to keep the models that make up the larger data set persisted for the user until the final submission.

This will allow the user to go back to any of the stages and edit the information if needed.  There are are options but for now we’re testing driving this approach.

Inspired (dang near a complete duplicate) by Donn Felker’s ASP.NET MVC TempData Extension Methods I created one that uses HttpSessionStateBase.

    public static class SessionStateExtensions
    {
        public static void Put<T>(this HttpSessionStateBase httpSession, T value) where T : class
        {
            httpSession[typeof (T).FullName] = value;
        }

        public static void Put<T>(this HttpSessionStateBase httpSession, string key, T value) where T : class
        {
            httpSession[typeof (T).FullName + key] = value;
        }

        public static T Get<T>(this HttpSessionStateBase httpSession) where T : class
        {
            var o = (T) httpSession[typeof (T).FullName];
            return o == null ? null : o;
        }

        public static T Get<T>(this HttpSessionStateBase httpSession, string key) where T : class
        {
            var o = (T) httpSession[typeof (T).FullName + key];
            return o == null ? null : o;
        }
    }