Windsor style convention over configuration in StructureMap

So I was trying to get my  BDD project SpecMaker working on Mono only to find out  my usual IoC container of choice Castle Windsor does not play nice.  StructureMap I’ve heard plays nice in Mono, and I know a bunch of people that use it, so I gave it a whirl.

My original Windsor configuration looked something like this:

 var kernel = new DefaultKernel();
 kernel.Register(Component.For<IReportFactory>().ImplementedBy(factory)); //load implementation
 kernel.Register(
                AllTypes.Pick().
                FromAssembly(GetType().Assembly).
                WithService.FirstInterface().  //regardless of naming first interface is listed as service
                Configure(c=>c.LifeStyle.Transient));

I changed this to the what I thought was the StructureMap equivalent:

 var container = new Container(registry =>
            {
                registry.Scan(x =>
                {
                    x.TheCallingAssembly();
                    x.With<DefaultConventionScanner>(); //does NOT configure the first service interface
                });
            });
container.SetDefault(typeof(IReportFactory), new ConfiguredInstance(factory)); //specific implemenation

Ok so I ran my code and nearly none of my classes were registered only interfaces and classes that were in a pattern like ISpecFinder/SpecFinder.  Looking at SM’s site this is actually expected behavior, and I was not able to find anything that would work the same way as I was used to. I’m not personally a fan of naming interfaces exactly like class names, so after looking around a bit at my options with SM and a brief perusal of Windsor’s source, I used StructureMap’s extensibility to come up with the following:

class FirstInterfaceConvention : TypeRules,ITypeScanner
    {
        public void Process(Type type, PluginGraph graph)
        {
            if (!IsConcrete(type)) return;  //only works on concrete types
            var firstinterface = type.GetInterfaces().FirstOrDefault(); //grabs first interface
            if(firstinterface!=null)
                graph.AddType(firstinterface, type); //registers type
            else
            {
                graph.AddType(type); //adds concrete types with no interfaces
            }
        }
    }
    public class IoCConfig
    {
        
        public virtual ISpecRunner InitializeRunner(Type factory)
        {
            var container = new Container(x=> x.Scan(
                                                       
                                                   y=>
                                                       {
                                                           y.TheCallingAssembly();
                                                           
                                                           y.With<FirstInterfaceConvention>();
                                                               
                                                       }
                                                   ));
            container.SetDefault(typeof(IReportFactory), new ConfiguredInstance(factory));
            

            return container.GetInstance<ISpecRunner>();
        }
    }

and perfecto!  Everything now works as it did before and I was happy that something that wasn’t supported out of the box was so very simple to add myself. Finally, SpecMaker now appears to be working in mono and is nearing a release for me which I will share shortly.

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    About Ryan Svihla

    I consider myself a full stack polyglot, and I have been writing a lot of JS and Ruby as of late. Currently, I'm a solutions architect at DataStax
    This entry was posted in Castle, Spec, SpecMaker, StructureMap. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • Eyston

      Thanks, shamelessly cut/pasted.

      I had always meant to look into doing conventions, but this made it easier than procrastinating.

    • http://www.lostechies.com/members/jflanagan/default.aspx Joshua Flanagan

      Just an FYI, you can use:
      container.Inject(factory);
      instead of:
      container.SetDefault(typeof(IReportFactory), new ConfiguredInstance(factory));

    • http://www.lostechies.com/members/jflanagan/default.aspx Joshua Flanagan

      Another tip for someone moving from Windsor to StructureMap: you do not need to configure concrete types.
      In your convention scanner, you can simply have:
      if(firstinterface!=null) graph.AddType(firstinterface, type);
      with no “else”.