Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I be passing a Unity Container in to my dependencies?

So I have:

Application A: Requires Class B (different assembly)

Class B: Requires Class C (again, different assembly)

Class C: Uses a container to resolve various objects, but the lifetime of the container (and the objects it resolves) should be controlled by the composition root.

I think I understand how this would work under most circumstances, but in Class C, I need to resolve based on a property of an object that is passed in.

I think what I'm asking is, has the container become a dependency, and as such, how best to get it where it's needed (not sure that I'd really like to pass it through a bunch of constructors - would property injection be the way to go?)

I believe this source is as clean and simple as I can get:

namespace InjectionTest
{
    using System;
    using Microsoft.Practices.Unity;

    public class ApplicationA
    {
        static void Main(string[] args)
        {
            using (IUnityContainer container = new UnityContainer())
            {
                // Normally I'd use this, but for clarity in the example, I'm doing it in code.
                //container.LoadConfiguration(); 
                container.RegisterType<IClassB, ClassB>();
                container.RegisterType<IClassC, ClassC>();
                container.RegisterType<IFooBuilder, FrobBuilder>("frob");
                container.RegisterType<IFooBuilder, WidgetBuilder>("widget");
                IClassB machine = container.Resolve<IClassB>();
                InitialObject bar = new InitialObject() { Name = "widget" };
                machine.doSomethingWithBar(bar);
                bar = new InitialObject() { Name = "frob" };
                machine.doSomethingWithBar(bar);
            }
        }
    }

    public class ClassB : IClassB
    {
        IClassC classC { get; private set; }

        public ClassB(IClassC classc)
        {
            this.classC = classc;
        }

        public void doSomethingWithBar(InitialObject bar)
        {
            var foo = this.classC.BuildMyFoo(bar);
            /*
             * Do something else with foo & bar
             * */
        }

    }

    public interface IClassB
    {
        void doSomethingWithBar(InitialObject bar);
    }

    public class ClassC : IClassC
    {
        public ResultObject BuildMyFoo(InitialObject bar)
        {
            IFooBuilder builder = null;
            //How best do I get my container here?
            //IFooBuilder builder = container.Resolve<IFooBuilder>(bar.Name);
            return builder.build(bar);
        }
    }

    public interface IClassC
    {
        ResultObject BuildMyFoo(InitialObject bar);
    }

    public class InitialObject
    {
        public string Name { get; set; }
    }

    public class ResultObject
    {
        public string SomeOtherData { get; set; }
    }

    public interface IFooBuilder
    {
        ResultObject build(InitialObject bar);
    }

    public class FrobBuilder : IFooBuilder
    {
        public ResultObject build(InitialObject bar)
        {
            throw new NotImplementedException();
        }
    }

    public class WidgetBuilder : IFooBuilder
    {
        public ResultObject build(InitialObject bar)
        {
            throw new NotImplementedException();
        }
    }
}

Edit: This is how I made it work with property injection:

I changed ClassC:

public class ClassC : IClassC
{
    [Dependency]
    public IUnityContainer Container { get; set; }

    public ResultObject BuildMyFoo(InitialObject bar)
    {
        IFooBuilder builder = null;
        //How best do I get my container here?
        builder = Container.Resolve<IFooBuilder>(bar.Name);
        return builder.build(bar);
    }
}

and updated my Main method in ApplicationA:

    public void Main()
    {
        using (IUnityContainer container = new UnityContainer())
        {
            // Normally I'd use this, but for clarity in the example, I'm doing it in code.
            //container.LoadConfiguration(); 
            container.RegisterType<IClassB, ClassB>();
            container.RegisterType<IClassC, ClassC>();
            container.RegisterType<IFooBuilder, FrobBuilder>("frob");
            container.RegisterType<IFooBuilder, WidgetBuilder>("widget");
            using (IUnityContainer child = container.CreateChildContainer())
            {
                container.RegisterInstance<IUnityContainer>(child);
                IClassB machine = container.Resolve<IClassB>();
                InitialObject bar = new InitialObject() { Name = "widget" };
                machine.doSomethingWithBar(bar);
                bar = new InitialObject() { Name = "frob" };
                machine.doSomethingWithBar(bar);
            }
        }
    }
like image 793
Peter T. LaComb Jr. Avatar asked Jan 20 '23 15:01

Peter T. LaComb Jr.


1 Answers

You definitely do not want to be passing around containers. You should look into the Unity factory support which will work in this situation. Something like this:

container.RegisterType<IFooBuilder, FrobBuilder>("Frob")
         .RegisterType<IFooBuilder, WidgetBuilder>("Widget")
         .RegisterType<Func<string, IFooBuilder>>(new InjectionFactory(c => new Func<string, IFooBuilder>(barName => c.Resolve<IFooBuilder>(barName))))

and then ClassC would have a constructor parameter of Func:

public class ClassC : IClassC
{
  private readonly Func<string, IFooBuilder> _builderFactory;

  public ClassC(Func<string, IFooBuilder> builderFactory)
  {
    _builderFactory = builderFactory;
  }

  public ResultObject BuildMyFoo(InitialObject bar)
  {
    IFooBuilder builder = _builderFactory(bar.Name);
    return builder.build(bar);
  }
}
like image 129
Paul Hiles Avatar answered Jan 30 '23 11:01

Paul Hiles