这是一个简单的方法。它缺乏某种优雅,但它会满足您的需求:
public static void Register(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<Hello>();
serviceCollection.AddSingleton<Hey>();
serviceCollection.AddSingleton<ClassThatDependsOnIHello1>(serviceProvider =>
new ClassThatDependsOnIHello1(serviceProvider.GetService<Hello>()));
serviceCollection.AddSingleton<ClassThatDependsOnIHello2>(serviceProvider =>
new ClassThatDependsOnIHello2(serviceProvider.GetService<Hey>()));
}
有两个类依赖于IHello。他们每个人的注册都包括一个功能。该函数从服务提供者解析Hello 或Hey,并将其传递给每个相应类的构造函数。这样您就可以控制将哪个实现传递给哪个类。
(题外话,服务提供者还没有建立。你提供的功能将在稍后执行,传递给它的服务提供者将是从服务集合中构建的服务提供者。 )
这样做的一个缺点是,现在您的 DI 注册显式调用您的构造函数。这可能会很麻烦,因为如果构造函数发生变化(也许您注入其他依赖项),那么您将不得不编辑此代码。这不是很好,但并不少见。
B 计划是像Microsoft suggests 一样使用另一个容器。
Autofac
首先,添加Autofac.Extensions.DependencyInjection NuGet 包。这引用了 Autofac 并且还提供了将 Autofac 容器添加到服务集合所需的扩展。
我已经安排这个重点关注依赖项在 Autofac 中注册的方式。它类似于IServiceCollection 和IServiceProvider。您创建一个ContainerBuilder,注册依赖项,然后从中构建一个Container:
static void RegisterDependencies(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<Hello>().Named<IHello>("Hello");
containerBuilder.RegisterType<Hey>().Named<IHello>("Hey");
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.ResolveNamed<IHello>("Hello")
));
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.ResolveNamed<IHello>("Hey")
));
}
这也不是很漂亮,但它回避了调用构造函数的问题。
首先它注册IHello 的两个实现并给它们命名。
然后它注册两个依赖于IHello的类。 WithParameter(new ResolvedParameter()) 使用两个函数:
- 第一个函数确定给定参数是否是我们要解析的参数。因此,在每种情况下,我们都会说,“如果要解析的参数是
IHello,则使用下一个函数解析它。”
- 然后它通过指定要使用的命名注册来解析
IHello。
我对这有多复杂并不感到兴奋,但这确实意味着如果这些类注入了其他依赖项,它们将被正常解析。您无需实际调用其构造函数即可解析ClassThatDependsOnIHello1。
你也可以不用名字:
static void RegisterDependencies(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<Hello>();
containerBuilder.RegisterType<Hey>();
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.Resolve<Hello>()
));
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(IHello),
(parameter, context) => context.Resolve<Hey>()
));
containerBuilder.RegisterType<SomethingElse>().As<ISomethingElse>();
}
我们可以通过一种简化创建 ResolvedParameter 的方法来清理它,因为那太可怕了。
public static ResolvedParameter CreateResolvedParameter<TDependency, TImplementation>()
where TDependency : class
where TImplementation : TDependency
{
return new ResolvedParameter((parameter, context) => parameter.ParameterType == typeof(TDependency),
(parameter, context) => context.Resolve<TImplementation>());
}
现在之前的注册变成了:
containerBuilder.RegisterType<ClassThatDependsOnIHello1>().WithParameter(
CreateResolvedParameter<IHello, Hello>());
containerBuilder.RegisterType<ClassThatDependsOnIHello2>().WithParameter(
CreateResolvedParameter<IHello, Hey>());
更好!
剩下的就是你如何将它与你的应用程序集成的细节,这会因你的应用程序而异。这是Autofac's documentation,它提供了更多详细信息。
出于测试目的,您可以这样做:
public static IServiceProvider CreateServiceProvider()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterDependencies();
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
我喜欢为这类事情编写单元测试。此方法将从 Autofac 容器创建一个 IServiceProvider,然后您可以测试从容器中解析的内容,以确保它们按预期得到解析。
如果您更喜欢其他容器,请查看它是否具有类似的集成以将其与 Microsoft 的容器一起使用。你可能会找到一个你更喜欢的。
温莎
这是一个使用 Castle.Windsor.MsDependencyInjection 的类似示例。
public static class WindsorRegistrations
{
public static IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var container = new WindsorContainer();
container.RegisterDependencies();
return WindsorRegistrationHelper.CreateServiceProvider(container, serviceCollection);
}
public static void RegisterDependencies(this IWindsorContainer container)
{
container.Register(
Component.For<Hello>(),
Component.For<Hey>(),
Component.For<ClassThatDependsOnIHello1>()
.DependsOn(Dependency.OnComponent<IHello, Hello>()),
Component.For<ClassThatDependsOnIHello2>()
.DependsOn(Dependency.OnComponent<IHello, Hey>())
);
}
}
我喜欢这个有两点:
- 阅读起来容易多了!解析
ClassThatDependsOnIHello2 时,使用Hey 实现对IHello 的依赖。简单的。
- 这 -
WindsorRegistrationHelper.CreateServiceProvider(container, serviceCollection) - 允许您使用 IServiceCollection 注册依赖项并将它们包含在服务提供程序中。因此,如果您的现有代码使用IServiceCollection 注册了大量依赖项,您仍然可以使用它。您可以向 Windsor 注册其他依赖项。然后CreateServiceProvider 将它们混合在一起。 (也许 Autofac 也有办法做到这一点。我不知道。)