More flexible dependencies

Nov 14, 2008 at 5:33 PM
I am not seeing how I can set up Unity to allow me to designate on the fly which dependency instance to inject.  For example, let's say I have a class that takes an IPersistenceStrategy dependency to determine how/where to save its data.  How would I accomplish the following:

MyClass mySqlClass = _container.Resolve(typeof(IMyClass));  //in this case, I want to inject SqlPersistenceStrategy
MyClass myFileClass = _container.Resolve(typeof(IMyClass));  //in this case, I want to inject FilePersistenceStrategy
MyClass myXmlClass = _container.Resolve(typeof(IMyClass));  //in this case, I want to inject XmlPersistenceStrategy

I don't  see a simple way to do this (other than proliferating a gazillion child containers).  It looks to me like, if I want to use a config file or even want to set up my container programmatically at startup, I only get a one-to-one interface-implementation mapping.  I could just new up my class directly (MyClass x = new MyClass(new SqlPersistenceStrategy()), but then I can't swap that out at unit test time if MyClass is, say, inside a method I'm testing.

I'm sure I'm missing something. 

Thanks
Nov 14, 2008 at 5:46 PM
Okay, I think I am getting closer to the answer:

_container.RegisterType(IPersistenceStrategy, SqlPersistenceStrategy)("Sql");
_container.RegisterType(IPersistenceStrategy, FilePersistenceStrategy)("File");
_container.RegisterType(IPersistenceStrategy, XmlPersistenceStrategy)("Xml");

MyClass mySqlClass =_container.Resolve<IMyClass>("Sql");

I think I may be missing a step in here - is this correct?  This doesn't quite make sense as "Sql" applies to the dependency, not the class I'm instantiating, so what if I had multiple dependencies. 
Nov 14, 2008 at 11:02 PM
You're almost there. You're right that named registration are the way to go. But the extra step is you need to tell the container what to pass to the objects as they're building up. Assuming you've got a class like this:

   class MyClass
    {
        private IPersistenceStrategy ps;

        public MyClass(IPersistenceStrategy ps)
        {
            this.ps = ps;
        }

        public string WhatAmIUsing { get { return ps.WhatAmI; } }
    }

You want to inject a SqlPersistenceStrategy, XmlPersistenceStrategy, or FilePersistenceStrategy depending on which name you resolve. So configure the container like this:

            IUnityContainer container = new UnityContainer()
                .RegisterType<MyClass>("Sql",
                    new InjectionConstructor(typeof (SqlPersistenceStrategy)))
                .RegisterType<MyClass>("File",
                    new InjectionConstructor(typeof (FilePersistenceStrategy)))
                .RegisterType<MyClass>("Xml",
                    new InjectionConstructor(typeof (XmlPersistenceStrategy)));

This configures which values get resolved for which named registration. So if you do container.Resolve<MyClass>("Xml") you'll get the xml provider.

You can take it another step and also do named registrations on the implementation of IPersistence strategy. You'll need to configure the constructor slightly differently:

            IUnityContainer container = new UnityContainer()
                // Configure persistence strategies
                .RegisterType<IPersistenceStrategy, SqlPersistenceStrategy>("Sql")
                .RegisterType<IPersistenceStrategy, FilePersistenceStrategy>("File")
                .RegisterType<IPersistenceStrategy, XmlPersistenceStrategy>("Xml")

                // And the various combinations of those strategies
                .RegisterType<MyClass>("Sql",
                    new InjectionConstructor(new ResolvedParameter<IPersistenceStrategy>("Sql")))
                .RegisterType<MyClass>("File",
                    new InjectionConstructor(new ResolvedParameter<IPersistenceStrategy>("File")))
                .RegisterType<MyClass>("Xml",
                    new InjectionConstructor(new ResolvedParameter<IPersistenceStrategy>("Xml")));

The main difference is the use of explicit ResolvedParameter objects. This is how you specify "Please resolve through the container with this name".

Hope this helps,

-Chris