Monday, April 29, 2013

Getting Started with Castle Windsor Interceptors in ASP.NET MVC

Don’t you hate when writing code you have to think about generic exception handling, logging of method calls, caching of methods, authorization, etc...  Well suppose there was a tool/technique you could use that you could write the above items once and make sure they get implemented on existing code along with any new code added in the future.  Castle Windsor and its Interceptors support just that.

In the example below we will create a interceptor and do the necessary wiring up to make it work.  We will be using an ASP.NET MVC4 application based on the initial Windsor setup done in my last blog post.
http://blog.devdave.com/2013/04/castle-windsor-in-aspnet-mvc-4.html

Create a manager class to do some work.  We will call methods of this class in our controller class. later in this blog post  In the Infrastructure folder create a new folder called Managers.  Inside the Managers folder create a new folder called Interfaces.  Create an interface called IExampleManager in the interface folder.

public interface IExampleManager
{
    void ExampleMethod();
}

Creating the concrete implementation of the manager, create a class called ExampleManager in the Managers folder.

public class ExampleManager : IExampleManager
{
    public void ExampleMethod()
    {
        Debug.WriteLine("Doing Work...");
    }
}

Now we need to register our new manager in Windsor.  In the Infrastructure\Installers folder add a new class called ManagerInstaller.

public class ManagerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
            .Where(type => type.Name.EndsWith("Manager"))
            .WithServiceDefaultInterfaces()
            .Configure(c => c.LifestyleTransient()));
    }
}

Now that Windsor knows how to handle the manager we can modify our controllers constructor like so

private IExampleManager exampleManager;

public HomeController(IExampleManager exampleManager)
{
    this.exampleManager = exampleManager;
}

Since Windsor knows how to turn an IExampleManager into ExampleManager it will handle this for us when Windsor instantiates the controller.

Modify the controller action method to call a method on the example manager.

public ActionResult Index()
{
    this.exampleManager.ExampleMethod();
    return new ContentResult(){Content = "test"};
}

Running the site and calling the Index action, note the output window.
image

Now lets say we wanted to intercept the call to any public method of the ExampleManager class.  We can do this by adding in an interceptor. To do this we will need to create the interceptor.  Create a new folder inside the infrastructure folder called Interceptors.  Create a new class in this folder called ExampleInterceptor.

public class ExampleInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Debug.WriteLine(string.Format("Before method: {0}", invocation.Method.Name));
        invocation.Proceed();
        Debug.WriteLine(string.Format("After method: {0}", invocation.Method.Name));
    }
}

Now we need to tell Windsor about the interceptor.  In the Infrastructure\Installers folder lets add a new class called InterceptorInstaller.

public class InterceptorInstaller : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register(Component.For<ExampleInterceptor>()
                               .LifestyleTransient());
    }
}

Now lets go back to our manager installer and add in the interceptor to all managers.

public class ManagerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
            .Where(type => type.Name.EndsWith("Manager"))
            .WithServiceDefaultInterfaces()
            .Configure(c => c.LifestyleTransient().Interceptors<ExampleInterceptor>()));
    }
}

Running the site and calling the Index action, note the output window now.
image

As you can see the interceptor intercepted the call to the method on our ExampleManager.   Adding new public methods to our manager call will automatically get intercepted also.  This means that adding code in the future required no thought about the interceptor.  This opens up all kinds of ideas for interceptors like:

  • Exception Handling
  • Logging
  • Performance Counters
  • Authorization
  • Caching
  • Timing
  • etc...

Note there is a different way to implement interceptors.  You can use an attribute on a class that is already under the control of Windsor.  More information about this can be found here: http://docs.castleproject.org/Windsor.Interceptors.ashx.

No comments:

Post a Comment