Posts in  aspnetmvc

2.5.2010

IJoinedFilter Part 3: IFilterPriority

Note: This article is a continuation of the series on IJoinedFilter:

  1. IJoinedFilter
  2. AutoMapFilter meet IJoinedFilter

In-lining mapping and injection aspects – yuck!

After implementing several aspects of controller actions as compos-able filters, it became apparent that controlling the order would be import. One of the latest additions is a filter to build up a view model property with data aspects. An example would be a select list for states on a person edit view. Normally, the states would be fetched and set on the model in the controller action:

  public class PriorityController : Controller
  {
    public ActionResult Injected()
    {
      var person = new Person
                   {
                    Name = "John Doe",
                    BirthDate = new DateTime(1980, 1, 1),
                    State = StatesService.GetStates()[1]
                   };
      var personViewModel = new PersonViewModel(person);
      person.AllStates = StatesService.GetStates();
      return View(person);
    }
  }

  public class StatesService
  {
    public static IList<State> GetStates()
    {
      return new List<State>
             {
              new State {Name = "Nebraska", Id = 1},
              new State {Name = "Iowa", Id = 2},
              new State {Name = "Kansas", Id = 3}
             };
    }
  }

  public class Person
  {
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
    public State State { get; set; }
  }

  public class State
  {
    public int Id { get; set; }
    public string Name { get; set; }
  }

  public class PersonViewModel
  {
    public string Name { get; set; }
    public string BirthDate { get; set; }
    public string StateId { get; set; }
    public IList<State> AllStates { get; set; }
  }

Extracting the injection aspect

This leads to repetitive code every time a view model requires this data. Furthermore, we are forced to create the appropriate view model inside the controller action instead of relying on the mapping filter. To separate concerns we created a filter to find these data aspects and inject the values. Now, we simply add a property to the view model and the filter will take care of the rest!

 
  public class InjectStatesFilter : ViewModelInjectFilter
  {
    protected override Func<PropertyInfo,bool> InjectProperty()
    {
      return p => p.PropertyType == typeof (IList<State>);
    }

    protected override object WithValue()
    {
      return StatesService.GetStates();
    }
  }

  public abstract class ViewModelInjectFilter : IActionFilter
  {
    public virtual void OnActionExecuting(ActionExecutingContext filterContext)
    {
    }

    public virtual void OnActionExecuted(ActionExecutedContext filterContext)
    {
      var viewResult = filterContext.Result as ViewResultBase;
      if (viewResult == null || viewResult.ViewData.Model == null)
      {
        return;
      }
      var model = viewResult.ViewData.Model;
      var property = model.GetType().GetProperties().FirstOrDefault(InjectProperty());
      if (property == null)
      {
        return;
      }
      var value = property.GetValue(model, null);
      if (value == null)
      {
        property.SetValue(model, WithValue(), null);
      }
    }

    /// <summary>
    /// The criteria to use to find the property to inject.0
    /// </summary>
    /// <returns></returns>
    protected abstract Func<PropertyInfo,bool> InjectProperty();

    /// <summary>
    /// The value to inject, only queried if a matching, null property is found.
    /// </summary>
    /// <returns></returns>
    protected abstract object WithValue();
  }

Going back to mapping and injecting filters with this new approach helps remove two aspects of duplication from the controller action:

 public class PriorityController : Controller
  {
    public ActionResult Injected()
    {
      var person = new Person
                   {
                    Name = "John Doe",
                    BirthDate = new DateTime(1980, 1, 1),
                    State = StatesService.GetStates()[1]
                   };

      return View(person);
    }
  }

IFilterPriority - Back to the aspect!

In the example, PersonViewModel can be built up with a list of states, if the mapping filter is executed first. To guarantee this, I have added an interface IFilterPriority with a routine GetOrder() to the IJoinedFilter framework. It returns an integer representing the priority of the filter in the execution chain. I chose an integer to mirror FilterAttribute.Order in the MVC framework, which sadly was not made into a compo-sable extensibility point.

 public interface IFilterPriority
  {
    int GetOrder();
  }

JoinedFilterLocator has been modified to add filters based on this order to the FilterInfo result. The actual execution is dependent on the MVC pipeline and follows the following rules when using JoinedFilterLocator:

  • Action Filters
    • High->Low for OnActionExecuting
    • Low->High for OnActionExecuted
  • Authorization Filters - High->Low
  • Exception Filters - High->Low
  • Result Filters
    • High->Low for OnResultExecuting
    • High->Low for OnResultExecuted
 public virtual FilterInfo FindFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
  {
    var filters = new FilterInfo();
    var joinedFilters = JoinedFilters
      .Where(i => i.JoinsTo(controllerContext, actionDescriptor)).ToList();

    if (joinedFilters != null)
    {
      AddFilters(joinedFilters, filters.ActionFilters);
      AddFilters(joinedFilters, filters.ExceptionFilters);
      AddFilters(joinedFilters, filters.AuthorizationFilters);
      AddFilters(joinedFilters, filters.ResultFilters);
    }

    return filters;
  }

  private void AddFilters<T>(IEnumerable<IJoinedFilter> joinedFilters, IList<T> filters)
  {
    var orderedFilters = joinedFilters.OfType<T>()
      .OrderByDescending(f => f is IFilterPriority ? (f as IFilterPriority).GetOrder() : int.MaxValue)
      .ToList();

    orderedFilters.ForEach(filters.Add);
  }

Now, we can set the priority of our mapping filter to 1, ensuring it’s OnActionExecuted is executed first. All other filters in the JoinedFilter project are given an order of Int32.Max by default. This might not be the best way to sort, so watch out for updates in the future. This seems to work well with action filters and exception filters.

 public class OnViewResult_IfModelDoesNotMatchViewModelThenMapWithAutoMapper :
    OnViewResult_ExecuteActionFilter<ReflectedAutoMapFilter>
  {
    public override int GetOrder()
    {
      return 1;
    }
  }

Prioritizing exception aspects

Exception filters are another good example where priority becomes important. I have created a sample set of exceptions NestedException inheriting from ExceptionBase. I added two methods to the PriorityController sample:

 public class PriorityController : Controller
  {
    ...
    public void NestedException()
    {
      throw new NestedException();
    }

    public void ExceptionBase()
    {
      throw new ExceptionBase();
    }
  }

  public class ExceptionBase : Exception
  {
  }

  public class NestedException : ExceptionBase
  {
  }

What if we want one handler to catch specific exceptions of type NestedException and another to handle the more general exception ExceptionBase? Without priority, we are at the mercy of JoinedFilterLocator and whatever mechanism it relies on to acquire JoinedFilters. The sample below implements two handlers to send users to an error view with a message reporting what exception handler dealt with the error:

 public class NestedExceptionHandler : ExceptionHandler<NestedException>
  {
    public override int GetOrder()
    {
      return 1;
    }
  }

  public class ExceptionBaseHandler : ExceptionHandler<ExceptionBase>
  {
    public override int GetOrder()
    {
      return 0;
    }
  }

  public abstract class ExceptionHandler<T> : IExceptionFilter, IJoinedFilter, IFilterPriority
    where T : Exception
  {
    public void OnException(ExceptionContext filterContext)
    {
      var exception = filterContext.Exception as T;
      if (exception == null || filterContext.ExceptionHandled)
      {
        return;
      }

      filterContext.Result = ErrorView();
      filterContext.ExceptionHandled = true;
    }

    private ViewResult ErrorView()
    {
      var result = new ViewResult
                   {
                    ViewName = "Error"
                   };
      result.ViewData["Message"] = string.Format("{0} exception handler caught this", typeof (T));
      return result;
    }

    public bool JoinsTo(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
      return true;
    }

    public abstract int GetOrder();
  }

Run the sample yourself and try changing the priority to see how the ExceptionBaseHandler and NestedExceptionHandler work. Notice how this allows control over exception handlers from most specific to most general, just like with try/catch statements.

As usual, all code is available at my Google code repository for IJoinedFilter, this set of changes was wrapped up with commit 56. Please leave me some feedback about any enhancements, specifically I am looking for better ideas to deal with priority of filters, the integer thing kind of bothers me :).

Update: I am thinking that another nice way to do priority would be to use an explicit configuration mechanism, a lot like FubuMvc has so the filters being used are explicit and the order listed is the order applied. Thoughts?

kick it on DotNetKicks.com Shout it

-Wes

12.11.2009

IJoinedFilter

We have been using ASP.Net MVC for a few projects at work and the standard set of cross cutting concerns are popping up, as usual. A lot of samples exist to create filters for the scenarios (logging, exception handling, mapping, output transformations etc). We have been using many of these and they are adding a lot of excitement to the development process.

However, I smell a bit of a problem with all these attributed filters. The smell of configuration over convention. Configuration isn’t always a bad thing, it’s actually pretty useful when you have niche situations, but when you have repetitive, cross cutting concerns that need the application of the same filters, it gets to be a burden to remember what to apply. The MVC framework is designed with just the right amount of convention over configuration in several other areas, I felt like it was time to do the same for applying filters.

Stealing a page out of the playbook of interception, I felt it was time to design filters to join to actions/controllers when certain criteria are met. Filters themselves are interceptors, so naturally, a filter could define what it joins to, it’s join point (from the interceptor world). This way we have filters apply themselves to actions instead of vice versa! This would be great, for example, to create standard sets of exception filters and have them attached to all actions, no need for someone to decorate every controller and no chance they would forget! The same is true for any type of filter (action, result, authorization or exception).

Note: I also see a need for some SRP with join points and the interceptors and will look into taking existing filters and applying a join point to attach them, instead of the filter needing to define it’s join points.

IJoinedFilter

So enough with the motivation, and on to how this works. IJoinedFilter is an interface to define how a filter should apply to an action and the context:

public interface IJoinedFilter
{
  bool JoinsTo(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}

The method JoinsTo determines if the filter should apply to the current action being executed. Both the controller context and action descriptor are passed to allow for coarse to very fine grained application. Before I explain how to wire up the infrastructure, let's look at an example of how this can be used. Let's say I have a simple controller with two actions, About and World:

public class HomeController : Controller
{
  public ActionResult About()
  {
    var data = new
               {
                message = "about"
               };

return Json(data);

}

public ActionResult World() { var data = new { message = "world" };

return Json(data);

} }

To keep this simple, they both just return json results. The output right now looks like the following:

image image


Now, let’s say I want to use a filter to change my output on the World action to return “Hello world”. Normally I would create a new ActionFilterAttribute and manually apply this attribute:

public class HelloWorldFilter : ActionFilterAttribute
{
  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
    var result = new JsonResult
    {
      Data = new
      {
        message = "Hello World!"
      }
    };

filterContext.Result = result;

} }

public class HomeController : Controller { public ActionResult About() { var data = new { message = "about" };

return Json(data);

}

[HelloWorldFilter] public ActionResult World() { var data = new { message = "world" };

return Json(data);

} }

HelloWorldFilter

But now, with joined filters I can avoid the step of applying the attribute! Instead I can simply implement IActionFilter with IJoinedFilter to get the same result:

public class HelloWorldFilter : IActionFilter, IJoinedFilter
{
  public bool JoinsTo(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
  {
    return actionDescriptor.ActionName == "World";
  }

public void OnActionExecuting(ActionExecutingContext filterContext) { }

public void OnActionExecuted(ActionExecutedContext filterContext) { var result = new JsonResult { Data = new { message = "Hello World!" } };

filterContext.Result = result;

} }

imageJoinsTo is set to only apply to actions with a name of "World". Now, I can replace my World action with a result of "Hello World!" by simply joining to the specific action I want (convention) instead of attributing it (configuration). This is a rather “cheesy” example but I am working on a subsequent blog post to show a few really cool, practical examples!

Infrastructure

Now for how the magic happens. I am going to need to find a spot to discover the joined filters and apply them to an action. The best spot to do this is to create a custom action invoker. I started with the WindsorControllerFactory in MVCContrib, so I also get IoC while I am at it :). This requires overriding the GetControllerInstance. If the container has an IActionInvoker registered, then resolve it and use it instead of the default action invoker.

public class ExtendedWindsorControllerFactory : WindsorControllerFactory
{
  public ExtendedWindsorControllerFactory(IWindsorContainer container) : base(container)
  {
    Container = container;
  }

protected IWindsorContainer Container { get; set; }

protected override IController GetControllerInstance(Type controllerType) { var controller = base.GetControllerInstance(controllerType) as Controller;

if (Container.Kernel.HasComponent(typeof (IActionInvoker)))
{
  controller.ActionInvoker = Container.Resolve&lt;IActionInvoker&gt;();
}

return controller;

} }

Now that I can inject an IActionInvoker, it is time to make one that can find my dynamic filters! I am calling this a LocatorActionInvoker as it resolves a list of IFilterLocator. Each one of these could find filters in it’s own way, this is just for SRP. For each locator the invoker will give it the controller context and the action descriptor and ask it to return filters (FilterInfo). It merges the results of each IFilterLocator into the set of filters to use!

public interface IFilterLocator
{
  FilterInfo FindFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}

public class LocatorActionInvoker : ControllerActionInvoker { protected IWindsorContainer Container;

public LocatorActionInvoker(IWindsorContainer container) { Container = container; }

protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var filters = base.GetFilters(controllerContext, actionDescriptor);

var filterFinders = Container.ResolveAll&lt;IFilterLocator&gt;();

var foundFilters = filterFinders.Select(f =&gt; f.FindFilters(controllerContext, actionDescriptor));

foundFilters.ForEach(f =&gt; AddFilters(filters, f));

return filters;

}

private void AddFilters(FilterInfo filters, FilterInfo mergeFilters) { mergeFilters.ActionFilters.ForEach(filters.ActionFilters.Add); mergeFilters.ExceptionFilters.ForEach(filters.ExceptionFilters.Add); mergeFilters.AuthorizationFilters.ForEach(filters.AuthorizationFilters.Add); mergeFilters.ResultFilters.ForEach(filters.ResultFilters.Add); } }

Now I need to implement IFilterLocator to find my IJoinedFilters:

public class JoinedFilterLocator : IFilterLocator
{
  private IWindsorContainer Container;

public JoinedFilterLocator(IWindsorContainer container) { Container = container; }

public FilterInfo FindFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var filters = new FilterInfo();

var joinedFilters = Container.ResolveAll&lt;IJoinedFilter&gt;()
  .Where(i =&gt; i.JoinsTo(controllerContext, actionDescriptor)).ToList();

if (joinedFilters != null)
{
  joinedFilters.OfType&lt;IActionFilter&gt;().ForEach(filters.ActionFilters.Add);
  joinedFilters.OfType&lt;IExceptionFilter&gt;().ForEach(filters.ExceptionFilters.Add);
  joinedFilters.OfType&lt;IAuthorizationFilter&gt;().ForEach(filters.AuthorizationFilters.Add);
  joinedFilters.OfType&lt;IResultfilter&gt;().ForEach(filters.ResultFilters.Add);
}

return filters;

} }

JoinedFilterLocator uses the container to resolve a list of IJoinedFilter. It filters this list for only filters that apply to the given ActionDescriptor and ControllerContext using the IJoinedFilter.JoinsTo method. If any filters match, it returns them in a new FilterInfo instance, which will be merged in LocatorActionInvoker with the rest of the filters.

Registration

That is it for custom components for the infrastructure of joined filters. All that is left is to configure the application to use the components and to register my components. First I add a container to my application:

public class MvcApplication : HttpApplication
{
  public static IWindsorContainer Container;

Then, in the startup of the application I initialize my container and register my components:

protected void Application_Start()
{
  RegisterRoutes(RouteTable.Routes);
  if (InitializeContainer())
  {
    RegisterControllers();
    SetControllerFactory();
    SetActionInvoker();
    RegisterJoinedActionFilters();
  }
}

Initialize container sets up a Windsor container for the application:

private bool InitializeContainer()
{
  lock (_lock)
  {
    if (Container != null)
    {
      return false;
    }
    Container = new WindsorContainer();
    Container.Register(
      Component.For<IWindsorContainer>()
        .Instance(Container)
        .LifeStyle.Singleton
      );
  }
  return true;
}

If the container is being initialized for the first time, then I register components. RegisterControllers just scans for controllers. SetControllerFactory registers ExtendedWindsorControllerFactory, resolves and sets it as the controller factory for MVC to use.

private void SetControllerFactory()
{
  Container.Register(Component
                      .For<IControllerFactory>()
                      .ImplementedBy<ExtendedWindsorControllerFactory>()
                      .LifeStyle.Transient);

var factory = Container.Resolve<IControllerFactory>();

ControllerBuilder.Current.SetControllerFactory(factory); }

SetActionInvoker registers my LocatorActionInvoker:

private void SetActionInvoker()
{
  Container.Register(Component.For<IActionInvoker>().ImplementedBy<LocatorActionInvoker>()
                      .LifeStyle.Transient);
}

Finally, RegisterJoinedActionFilters registers my JoinedFilterLocator and scans for IJoinedFilter types. You may want to change how this scans based on your project structure, in my simple example I have filters in my MVC app (bad practice but great for samples :)).

private void RegisterJoinedActionFilters()
{
  Container.Register(
    Component.For<IFilterLocator>().ImplementedBy<JoinedFilterLocator>().LifeStyle.Transient
    );

Container.Register( AllTypes.Of<IJoinedFilter>() .FromAssembly(Assembly.GetExecutingAssembly()) .ConfigureFor<IJoinedFilter>(c => c.LifeStyle.Transient) ); }

That is all for now, pretty easy way to setup joined filters and start creating convention based, joined filters instead of manually configuring them via attributes!

Download Sample

If you want to download the sample and try it out, feel free. You probably want an in browser json viewer with this sample, I like using JSONView with FireFox.

Note: due to issues building the latest MVCContrib against MVC 1.0 and/or getting castle version mismatches to work and me being lazy, I simply copied the WindsorControllerFactory into my project for now, cheap yes, but hey my time isn’t unlimited :)

-Wes