使用温莎
我将回避关于这在这种情况下是否有意义的问题,并尝试按要求回答问题:
.NET Core 的 IoC 容器并不是特别适合这种场景。 (他们在他们的文档中承认了这一点。)您可以通过添加另一个 IoC 容器(如 Windsor)来解决它。
最终实现看起来比我希望的要复杂得多,但是一旦您通过了设置,这还不错,您可以访问 Windsor 的功能。我将提供另一个不包括温莎的答案。我必须完成所有这些工作才能看到我可能更喜欢另一种方法。
在您的项目中,添加Castle.Windsor.MsDependencyInjection NuGet 包。
用于测试的接口和实现
为了测试,我添加了一些接口和实现:
public interface ICustomService { }
public interface IRegisteredWithServiceCollection { }
public class CustomServiceOne : ICustomService { }
public class CustomServiceTwo : ICustomService { }
public class CustomServiceThree : ICustomService { }
public class RegisteredWithServiceCollection : IRegisteredWithServiceCollection { }
目的是创建一个工厂,该工厂将使用一些运行时输入选择并返回ICustomService 的实现。
这是一个用作工厂的接口。这就是我们可以注入到类中并在运行时调用以获得ICustomService 的实现:
public interface ICustomServiceFactory
{
ICustomService Create(string input);
}
配置温莎容器
接下来是一个类,它将配置一个IWindsorContainer 来解决依赖关系:
public class WindsorConfiguration : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<ICustomService, CustomServiceOne>().Named("TypeOne"),
Component.For<ICustomService, CustomServiceTwo>().Named("TypeTwo"),
Component.For<ICustomService, CustomServiceThree>().Named("TypeThree"),
Component.For<ICustomService, CustomServiceOne>().IsDefault(),
Component.For<ICustomServiceFactory>().AsFactory(new CustomServiceSelector())
);
}
}
public class CustomServiceSelector : DefaultTypedFactoryComponentSelector
{
public CustomServiceSelector()
: base(fallbackToResolveByTypeIfNameNotFound: true) { }
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return (string) arguments[0];
}
}
这里发生了什么:
-
TypedFactoryFacility 将使我们能够使用 Windsor 的类型化工厂。它将为我们创建工厂接口的实现。
- 我们正在注册
ICustomService 的三个实现。因为我们注册了多个实现,所以每个实现都必须有一个名称。当我们解析ICustomService时,我们可以指定一个名字,它会根据那个字符串来解析类型。
- 为了说明,我注册了另一个没有名称的
ICustomService 实现。如果我们尝试使用无法识别的名称进行解析,这将使我们能够解析默认实现。 (一些替代方案只是抛出异常,或返回 ICustomService 的“null”实例或创建类似 UnknownCustomService 的类来抛出异常。)
-
Component.For<ICustomServiceFactory>().AsFactory(new CustomServiceSelector()) 告诉容器创建一个代理类来实现ICustomServiceFactory。 (更多信息请参见their documentation。)
-
CustomServiceSelector 将参数传递给工厂的 Create 方法并返回将用于选择组件的组件名称(TypeOne、TypeTwo 等)。在这种情况下,我们期望传递给工厂的参数与我们使用的注册名称相同。但是我们可以用其他逻辑代替它。我们的工厂甚至可以接受其他类型的参数,我们可以检查并确定返回哪个字符串。
配置您的应用以使用 Windsor 容器
现在,在 StartUp 中,修改 ConfigureServices 以返回 IServiceProvider 而不是 void,并创建一个 IServiceProvider,它将直接使用 IServiceCollection 注册的服务与使用 Windsor 容器注册的服务结合起来:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var container = new WindsorContainer();
container.Install(new WindsorConfiguration());
return WindsorRegistrationHelper.CreateServiceProvider(container, services);
}
container.Install(new WindsorConfiguration()) 允许WindsorConfiguration 配置我们的容器。我们可以用这种方法直接配置容器,但这是保持容器配置井井有条的好方法。我们可以创建大量IWindsorInstaller 实现或我们自己的自定义类来配置Windsor 容器。
WindsorRegistrationHelper.CreateServiceProvider(container, services) 创建使用container 和services 的IServiceProvider。
有效吗?
我不会在没有先发现的情况下发布所有这些。这是一些 NUnit 测试。 (我通常会为 DI 配置写一些基本的测试。)
安装程序会创建一个IServiceProvider,类似于应用程序启动时发生的情况。它创建一个容器并应用WindsorConfiguration。我还直接向ServiceCollection 注册了一项服务,以确保两者能够很好地配合使用。然后我将两者组合成一个IServiceProvider。
然后我从IServiceProvider 解析ICustomerServiceFactory 并验证它是否为每个输入字符串返回ICustomService 的正确实现,包括当字符串不是可识别的依赖项名称时的回退。
我也在验证直接使用ServiceCollection 注册的服务是否已解析。
public class Tests
{
private IServiceProvider _serviceProvider;
[SetUp]
public void Setup()
{
var services = new ServiceCollection();
services.AddSingleton<IRegisteredWithServiceCollection, RegisteredWithServiceCollection>();
var container = new WindsorContainer();
container.Install(new WindsorConfiguration());
_serviceProvider = WindsorRegistrationHelper.CreateServiceProvider(container, services);
}
[TestCase("TypeOne", typeof(CustomServiceOne))]
[TestCase("TypeTwo", typeof(CustomServiceTwo))]
[TestCase("TYPEThree", typeof(CustomServiceThree))]
[TestCase("unknown", typeof(CustomServiceOne))]
public void FactoryReturnsExpectedService(string input, Type expectedType)
{
var factory = _serviceProvider.GetService<ICustomServiceFactory>();
var service = factory.Create(input);
Assert.IsInstanceOf(expectedType, service);
}
[Test]
public void ServiceProviderReturnsServiceRegisteredWithServiceCollection()
{
var service = _serviceProvider.GetService<IRegisteredWithServiceCollection>();
Assert.IsInstanceOf<RegisteredWithServiceCollection>(service);
}
}
这一切值得吗?
现在我已经弄清楚了,如果我真的需要这种功能,我可能会使用它。如果您尝试将 Windsor 与 .NET Core 结合使用,并且第一次看到它是抽象工厂实现,那看起来会更糟。 Here's another article 提供了有关 Windsor 抽象工厂的更多信息,而没有关于 .NET Core 的所有噪音。