Passing IUnityContainer as ctor argument of container resolved type

Mar 30, 2012 at 7:13 AM
Edited Mar 30, 2012 at 7:20 AM

We are planning to use Unity in our project to dynamically load objects/types residing in different assemblies. To register the types we are using the .config file. I have written a small sample app to test this out and all works well. Following is the sample code:

    /* App.config
    ...
    <container name="xyz">
          <register type="IOperations" mapTo="COperations">
            <lifetime type="external" />
          </register>
      
          <register type="ITest" mapTo="CTest">
            <lifetime type="external" />
          </register>
    </container>
    ...
    */
    public class CA
    { }
    public class CB
    { }
 
    public interface IMyOperations : IDisposable
    {
        void DoSomething(string str);
    }
    public interface ITest : IDisposable
    {
        void DoSomethingElse(string str);
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            string containerName = "xyz";
            using ( IUnityContainer container = new UnityContainer() )
            {
                UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
                section.Configure(container, containerName);
 
                IMyOperations myOps = container.Resolve<IMyOperations>();
                myOps.DoSomething("Hello");
                myOps.Dispose();
        
                ITest test = container.Resolve<ITest>();
                test.DoSomethingElse("World!");
                test.Dispose();
        
            }
        }
    }
 
    public class COperations
    {
        private IUnityContainer container;
        public COperations(IUnityContainer container)
        {
            this.container = container;
        }
 
        public void DoSomethingElse(string str)
        {
            // based on runtime values create an instance of class CA or CB and register them with the container
            if (str.Length > 10)
                container.RegisterInstance<CA>("InstanceCA"new CA());
            else
                container.RegisterInstance<CB>("InstanceCB"new CB());
        }
        public void Dispose()
        { }
    }
 
 
    public class CTest
    {
        private IUnityContainer container;
        public CTest(IUnityContainer container)
        {
            this.container = container;
        }
 
        public void DoSomethingElse(string str)
        {
            // based on runtime values get the registered instance of class CA or CB
            if (str.Length > 100)
                container.Resolve<CA>("InstanceCA");
            else
                container.Resolve<CB>("InstanceCB");
        }
        public void Dispose()
        { }
    }

I am also creating new objects (of class CA and CB in the code above) in the container-resolved-type and registering those with the container using ResisterInstance(). To do this I am changing the the signature of ctor of my container-resolved-type looks like the following:

 public COperations(IUnityContainer container)

I am not sure if this is the right approach. Is there a better way to use the container to register instance from the container-resolved-type object? I am trying to share a few objects among each other, which I can know only at run time.

Mar 31, 2012 at 4:32 AM

Many people dislike passing the container into the constructor.  This is using Unity as a Service Locator and some people consider this to be an anti-pattern.

Actually, it's a bit worse than that since there is also a hidden dependency (Temporal Coupling) between CTest and COperation.  I.e. if CTest.DoSomethingElse() is invoked before COperation.DoSomething an exception could be thrown because CA or CB is not registered.

A good solution to this is to use an abstract factory.  Inject the factories and at runtime have the factories construct the correct objects based on the runtime criteria.  See Is there a pattern for initializing objects created via a DI container for a good discussion.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com