Sunday, June 3, 2012

Thoughts on ActionFilters

Source: Ayende/Rob Conery

I have a very basic UnitOfWork ActionFilter which runs after every action, when no exceptions are thrown it calls SaveChanges on my DbContext (which is set to be  1 per request in ninject).  Mostly because we occasionally forgot to call this method.


Because I use AutoMapper to create my ViewModels from my entities I ended up with a bunch of code in my views that basically looked like

In my crusade to remove repetitive code in my actions, I created an extra ActionFilter that can perform the mapping after the action has finished executing.

The main benefit of this turned out to not be less code, as it’s still the same number of lines of code. It was that my tests no longer needed to setup AutoMapper in order to test an Action (except for the create/update actions).

Jimmy has actually moved on from using an ActionFilter and started using an ActionResult that decorates another ActionResult and performs the mapping.  I don’t use that approach as I find it hard/impossible to add code between the mapping and the final view being displayed, which I needed to do to populate dropdowns (see ViewModelEnricher).  Jimmy’s new approach looks like the following:


Another piece of repetitive code was loading the contents dropdowns and adding them to the ViewModels. I created yet another ActionFilter (run after the AutoMap one) which looks at all the properties on a ViewModel for an attribute telling it where to load dropdowns from.

This was added globally to all actions and meant I could no longer accidentally forget to add the code to set the dropdowns on Validation failure of an update/create action (most common place to forget to do it).

I’m still not 100% convinced this is the best approach; I’ve been playing with the concept that dropdowns are dependencies of the ViewModel and should be created by the DI container.  This would either require the AutoMap ActionFilter and ModelBinder to create all ViewModels using the DependencyResolver or a small modification to the ViewModelEnricher ActionFilter to use property injection into an existing object on the way through.


It’s fairly common to check ModelState.IsValid on all postbacks and return a view if validation fails.  I added another global action filter which performs this check on any post request.  Again this is so it isn’t accidentally forgotten and to reduce the amount of repetitive code in Actions.