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())
{
开发者_C百科 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);
}
}
}
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);
}
}
精彩评论