4

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);
            }
        }
    }
Peter T. LaComb Jr.
  • 2,935
  • 2
  • 29
  • 44
  • OK - So I finished this example and got it running. property injection looks like the way to go, I just have to register a child container as the instance I want to use (registering the top level container results in a stack overflow). I'd still appreciate any answers since I'm new to this DI stuff. – Peter T. LaComb Jr. Jan 26 '11 at 16:33
  • Ok, now I see here http://stackoverflow.com/questions/827756/can-a-unity-container-pass-a-reference-of-itself-as-a-constructor-parameter that I don't even have to create a child - I just have to mark the property as a dependency and unity auto-injects the current container. I think that's even cleaner. – Peter T. LaComb Jr. Jan 26 '11 at 17:12
  • Related: http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it-through-t – Mark Seemann Jan 26 '11 at 18:21

1 Answers1

9

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);
  }
}
Paul Hiles
  • 9,558
  • 7
  • 51
  • 76
  • But doesn't this tie my ApplicationA to the IFooBuilder interface more closely than using a child container? (with a child container, the types returned and accepted by IFooBuilder can change without any change to ApplicationA). – Peter T. LaComb Jr. Jan 26 '11 at 16:56
  • On the flip side, what downside is there to passing in a child container? – Peter T. LaComb Jr. Jan 26 '11 at 16:56
  • 8
    @Peter LaComb Jr.: The point of inversion of control is to make dependencies explicit. If you're passing in a container to serve as a service locator, you are not revealing the dependencies (the consumers could pull anything out of the container, and you don't know it without reading the code). Make dependencies explicit, so don't use a service locator, and thus don't pass in a container. – jason Jan 26 '11 at 17:10
  • Oh, and +1 because this is a valid option that I had no idea even existed. – Peter T. LaComb Jr. Jan 26 '11 at 17:13
  • OK - I think I see at least part of your point @Jason. doing it this way limits what ClassC has to a function accepting a string and returning an IFooBuilder. I like that. I'm not certain how much more this 'reveals' about the dependency in ApplicationA though. It might seem to say more about the fact that a string is required to resolve an IFooBuilder, but I would think that is what most would see when they see the type registered with a name. – Peter T. LaComb Jr. Jan 26 '11 at 17:46
  • +1 as well to Jason; once you've grasped this fundamental point about DI, everything starts to make sense. It's a hard habit to break though! – David Keaveny Feb 01 '11 at 22:22