Convention-based Registration Extension for Unity

Inspired by Jeremy Miller’s presentation of StructureMap’s convention-based type registration at this week’s Austin .Net User’s Group, I set out to create a convention-based type registration extension for the Unity container.

For those unfamiliar with the convention-based approach to type registration for IoC containers, I’ll refer you to Jeremy’s article here which should get you up to speed on the concept.

Unity is fairly easy to extend, so I had the first working prototype after about 15 minutes. I spent several hours afterward, however, trying to tease out an API I felt was suitable. After a few cycles attempting to design a fluent API, I settled upon something similar to Fluent NHibernate’s mapping registration, and one which I think fits decently with Unity’s existing API.

Without further ado, here is an example of its usage:

        [Concern("During setup")]
        public class When_container_configured_with_interface_impl_name_match_convention :
            Behaves_like_context_with_container_with_convention_extension
        {
            protected override void Because()
            {
                _container
                    .Using<IConventionExtension>()
                    .Configure(x =>
                        {
                            x.Conventions.Add<InterfaceImplementionNameMatchConvention>();
                            x.Assemblies.Add(Assembly.GetExecutingAssembly());
                        })
                    .Register();
            }

            [Observation, Test]
            public void Types_matching_convention_should_be_auto_registered()
            {
                _container.Resolve<ITestType>().ShouldBeOfType(typeof (TestType));
            }
        }

After adding the extension to the container, the IConventionExtension configuration is used to configure the conventions and assemblies used during the auto-registration process. The convention used here matches the common .Net naming convention where interface and default implementation pairs share the same name, with the interface carrying an ‘I’ prefix. The test demonstrates resolving an instance of ITestType, though no explicit registration has been provided.

The following test demonstrates using an alternate convention where only implementations of the specified type are auto-registered:

        [Concern("During setup")]
        public class When_container_configured_with_implementation_convention_with_name_replacement :
            Behaves_like_context_with_container_with_convention_extension
        {
            protected override void Because()
            {
                _container
                    .Using<IConventionExtension>()
                    .Configure(x =>
                        {
                            x.Conventions.Add<ImplementationConvention<IController>>();
                            x.Assemblies.Add(Assembly.GetExecutingAssembly());
                        })
                    .Register(x => x.Replace("Controller", ""));
            }

            [Observation, Test]
            public void Controller_types_should_be_resolvable_by_prefix_name()
            {
                _container.Resolve<IController>("MainView").ShouldNotBeNull();
            }

            [Observation, Test]
            public void Types_not_of_type_controller_should_not_be_resolvable()
            {
                try
                {
                    _container.Resolve<ITestType>();
                    throw new Exception("Type ITestType should not be resolvable.");
                }
                catch (ResolutionFailedException)
                {
                }
            }
        }

In this example, the types are registered by name utilizing a delegate to facilitate the ability to modify the default name of the concrete type to be registered in some way. In this case, all implementations of IController are registered under the name of the concrete type, minus the “Controller” suffix.

The full extension source can be downloaded at: http://code.google.com/p/conventionextension/. Enjoy!

About Derek Greer

Derek Greer is a consultant, aspiring software craftsman and agile enthusiast currently specializing in C# development on the .Net platform.
This entry was posted in Uncategorized and tagged , . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Anonymous

    Awesome. Thanks for posting this. Was about to implement something similar (but not nearly as cool). Xml configuration finally drove me nuts this afternoon.

  • http://LeeCampbell.blogspot.com Lee Campbell

    Cheers. This looks good. I have to do a recompile for Silverlight. I was wondering if I/You/We could update the source repository to include a SL build?

    • Derek

      My apologies for dragging my heals on getting this done. I’ll try to update this by this weekend.

  • http://wizardsofsmart.net Ryan Riley

    I love it! Thanks for posting this. I found a small error when using this with multiple closing generics (e.g. StringRepository and TestTypeRepository), but I’ve filed an issue and submitted a patch: http://code.google.com/p/conventionextension/issues/detail?id=2

    • Derek

      Thanks for submitting the patch Ryan. I cherry-picked a bit, but it was instrumental in understanding the issue.

  • http://retrospectivepragmatic.blogspot.com/ Nachiket Mehta

    This is awesome. I was thinking about writing an extension like this and found your article.

  • http://davidkeaveny.blogspot.com David Keaveny

    Thanks for the great article; I love your Unity extension, and it’s really cutting down the amount of configuration I need to do.

    One quick question – how would you set a lifetime manager on an instance that has been auto-registered by the extension?

    • Derek

      It’s been about 1 1/2 years since I’ve used Unity, but I believe you would just re-register the component you want a specific lifetime manager with. It’s basically a “last one registered wins” model. So, everything would be registered as transient by default and then you would re-register the ones you want specific lifetime managers for. You may also be able to associate a lifetime manager with an existing type registered without re-registering it. Some containers work that way, but I’m not sure about Unity.

      • http://davidkeaveny.blogspot.com David Keaveny

        So something along the lines of:

        return new UnityContainer()
        .AddNewExtension()
        .Configure()
        .Configure(x => {
        x.Conventions.Add();
        x.Conventions.Add(new ClosingTypeConvention(typeof(IRepository)));
        x.Assemblies.Add(Assembly.GetExecutingAssembly()));
        })
        .Register()
        .RegisterType(typeof(IRepository), new ContainerControlledLifetimeManager());

        is that right? Too easy, if that’s the case!

        • Derek

          Yeah, I believe it’s something along those lines.