我有一个使用依赖注入的应用程序,但目前不使用任何IoC容器。我目前在我的应用设置代码中有以下内容:
ISimpleDependency ez = new SimpleDependency();
ISomeOtherDependency otherDep = new SomeOtherDependency();
FooConfig fooConfigA = Settings.Default.FooConfigA;
FooConfig fooConfigB = Settings.Default.FooConfigB;
IFoo fooA = new Foo(fooConfigA, ez);
IFoo fooB = new Foo(fooConfigB, ez);
Bar bar = new Bar(fooA, otherDep);
Baz baz = new Baz(fooB, ez, otherDep);
Qux qux = new Qux(fooA, fooB); //params IFoo[] constructor to which we want to pass every Foo
为了降低应用设置代码的复杂性并提高可维护性,我想介绍一个IoC容器。有没有什么方法可以在这里使用IoC容器来自动连接所有内容而无需将Foo / Bar / Baz / Qux类紧密耦合到IoC容器实现的选择?
答案 0 :(得分:1)
你最终会得到与IOC容器耦合的设置代码,但这没关系。关键是设置始终出现在应用程序的组合根目录中。或者换句话说,在使用类之前,在应用程序启动时指定依赖项。这些类本身将在不了解容器或如何创建依赖项的情况下运行。
假设这些是您的Foo
类和接口:
public interface IFoo { }
public interface IFooConfig { }
public class Foo : IFoo
{
private readonly IFooConfig _config;
public Foo(IFooConfig config)
{
_config = config;
}
}
public class FooConfigA : IFooConfig { }
public class FooConfigB : IFooConfig { }
这是一些容器代码。你可以看到它变得复杂。如果您的依赖关系很小而且简单,那么它可能不值得。
以Windsor为例,您的设置可能如下所示。有多种方法可以做到,所以我会留给你决定它是否更简单或更可取。
container.Register(
Component.For<IFooConfig, FooConfigA>().Named("FooConfigA"),
Component.For<IFooConfig, FooConfigB>().Named("FooConfigB"),
Component.For<IFoo, Foo>()
.DependsOn(Dependency.OnComponent<IFooConfig, FooConfigA>()).Named("FooA")
.IsFallback(),
Component.For<IFoo, Foo>()
.DependsOn(Dependency.OnComponent<IFooConfig, FooConfigB>()).Named("FooB"));
现在您有两个IFoo
注册名称不同的注册。每个对象都返回相同类型的对象(Foo
),但每个实例都有IFooConfiguration
的不同实现。
或者如果您使用的是来自Settings.Default
的IFooConfig实例,您可以这样做:
Component.For<IFoo, Foo>()
.DependsOn(Dependency.OnValue("config", Default.Settings.FooConfigA))
.Named("FooA")
.IsFallback(),
Component.For<IFoo, Foo>()
.DependsOn(Dependency.OnValue("config", Default.Settings.FooConfigB))
.Named("FooB"),
现在,每个依赖IFoo
的课程,您必须按名称指定您获得的版本,否则您将获得“A”,因为它被指定为后备。
正如您所看到的,这很快变得混乱和复杂。如果可能,另一种方法是使用抽象工厂在运行时选择实现,而不是为每个依赖项组合单独注册IFoo
。 Here's some more explanation and an example.
如果您正在使用Windsor(并且我确定其他容器具有相似的行为),您可以使用构造函数来获取IEnumerable<IFoo>
或Foo[]
,然后Windsor将解析 all 实现并将它们传递给构造函数。您可以将其添加到容器设置中:
container.Kernel.Resolver.AddSubResolver(new ListResolver(container.Kernel, true));
答案 1 :(得分:1)
使用Simple Injector时,以下注册相当于您当前的Pure DI方法:
var container = new Container();
container.Register<ISimpleDependency, SimpleDependency>();
container.Register<ISomeOtherDependency, SomeOtherDependency>();
container.Register<Bar>();
container.Register<Baz>();
container.Register<Qux>();
var fooAReg = Lifestyle.Transient.CreateRegistration<IFoo>(
() => new Foo(Settings.Default.FooConfigA, container.GetInstance<ISimpleDependency>()),
container);
var fooBReg = Lifestyle.Transient.CreateRegistration<IFoo>(
() => new Foo(Settings.Default.FooConfigB, container.GetInstance<ISimpleDependency>()),
container);
// The registrations for IFoo are conditional. We use fooA for Bar and fooB for Baz.
container.RegisterConditional(typeof(IFoo), fooAReg,
c => c.Consumer.ImplementationType == typeof(Bar));
container.RegisterConditional(typeof(IFoo), fooBReg,
c => c.Consumer.ImplementationType == typeof(Baz));
container.RegisterCollection<IFoo>(new[] { fooAReg, fooBReg });
请注意,此示例不对所有注册使用自动连线; Foo
注册是手动连接的。这是因为(作为安全措施)Simple Injector不允许通过其直接父级“查找”调用图,因为这可能导致不正确的结果。这就是我们无法使用自动布线将FooConfigB
注入Foo
Bar
的原因。
<强>更新强>:
如果我有两个Bar实例,其中一个依赖Foo和FooConfigA,另一个依赖Foo和FooConfigB
var fooAProd = Lifestyle.Transient.CreateProducer<IFoo>(
() => new Foo(Settings.Default.FooConfigA, container.GetInstance<ISimpleDependency>()),
container);
var bar1Reg = Lifestyle.Transient.CreateRegistration<Bar>(() => new Bar(
fooAProd.GetInstance(),
container.GetInstance<IOtherDep1>()));
var fooBProd = Lifestyle.Transient.CreateProducer<IFoo>(
() => new Foo(Settings.Default.FooConfigB, container.GetInstance<ISimpleDependency>()),
container);
var bar2Reg = Lifestyle.Transient.CreateRegistration<Bar>(() => new Bar(
fooBProd.GetInstance(),
container.GetInstance<IOtherDep1>()));
// The registrations for IFoo are conditional. We use fooA for Bar and fooB for Baz.
container.RegisterConditional(typeof(IFoo), fooBProd.Registration,
c => c.Consumer.ImplementationType == typeof(Baz));
container.RegisterCollection<IFoo>(new[] { fooAReg, fooBReg });