Factory Pattern using Windsor and MVC

I ran into a situation where I wanted to return a dynamically drawn image based on the file name . For example, if someone requested somedomain.com/image/legacypage42.foo the “legacypage42.foo” would be the name of the Windsor component used to draw the image. The first thing I needed to do was change that page into a parameter which is very simple with MVC .net routing:

  
routes.MapRoute("MyImageRoute",
                "image/{filename}",
                new { controller = "Images", action = "Index" });

So now filename becomes the parameter in the controller. Now comes the tricky part. How to resolve a component in Windsor by name. Windsor does allow you to set the name using this syntax:

  
kernel.Register(
    Component.For<IMyImageComponent>()
        .ImplementedBy<MyImageComponent>()
        .Named("SomeImage"));

Although, using that syntax is going to make for a nightmare if I have many different image components so I’ll use the built in Windsor Type Factory. The first step is to cleanly register all my image components setting the name. To do that I created an interface all my images will use so I can easily set the named value.

  
Interface example:

    public interface IMyImageComponent
    {
        Bitmap BuildImage();

        string GetFileName();
    }

Here is how I use the interface to setup my Windsor components and named values.

  
var type = typeof(IMyImageComponent);
var styles = AppDomain.CurrentDomain.GetAssemblies().ToList()
        .SelectMany(s => s.GetTypes())
        .Where(type.IsAssignableFrom);

    foreach (var style in styles)
    {
        if (!style.IsClass || style.IsNotPublic || style.IsAbstract) 
            continue;

        object o = Activator.CreateInstance(style);
        var propertyInfo = style.GetMethod("GetFileName");
        var name = (string)propertyInfo.Invoke(o, null);
        container.Register(Component.For<IMyImageComponent>()
                    .ImplementedBy(o.GetType()).Named(name));

If you want to use the power of Windsor inside your classes that implement IMyImageComponent. You could use the following instead. The only downside is you have to register the components twice in Windsor but only on the initial start up.

  

container.Register(AllTypes.FromAssembly(typeof(YourClassNameHere).Assembly)
               .BasedOn<IMyImageComponent>());

var myImageStyles = container.ResolveAll<IMyImageComponent>();

var myImageTypes = myImageStyles .Select(style => style.GetType()).ToList();

foreach (var type in myImageTypes )
{
    object o = container.Resolve(type);
    var propertyInfo = type.GetMethod("GetFileName");
    var name = (string)propertyInfo.Invoke(o, null);
    container.Register(Component.For<IMyImageComponent>()
        .ImplementedBy(o.GetType()).Named(name));
}

Here is how the factory was initialized:

  
container.Register(Component.For<ITypedFactoryComponentSelector>()
         .ImplementedBy<ImageStyleFactorySelector>().Named("ImageStyleFactorySelector"));

container.Register(Component.For<IMyImageTemplateFactory>()
         .AsFactory(c => c.SelectedWith("ImageStyleFactorySelector")));

Notice I used a custom selector in the factory. This is used when the factory is looking up the components. Here is what the FactorySelector looks like.

  
public class ImageStyleFactorySelector : DefaultTypedFactoryComponentSelector
{
   protected override string GetComponentName(MethodInfo method, object[] arguments)
   {
       if (method.Name == "GetByFileName" && arguments.Length == 1 && arguments[0] is string)
       {
           return arguments[0].ToString();
       }
       return base.GetComponentName(method, arguments);
   }
}

Now inside the controller I use the factory with the function “GetByFileName” which uses the custom selector to find the component.

   
var imageComponent = _ImageTemplateFactory.GetByFileName(fileName);

Leave a Reply

Your email address will not be published. Required fields are marked *