实现这一点的推荐方法是使用Options模式——注意,这适用于任何。net Core/5应用程序,而不仅仅是ASP。净的核心。但也有一些用例是不切实际的(例如,当参数只在运行时知道,而不是在启动/编译时知道),或者你需要动态替换依赖项。
当您需要替换单个依赖项(字符串、整数或其他类型的依赖项)或使用仅接受字符串/整数参数且需要运行时参数的第三方库时,这非常有用。
你可以试试ActivatorUtilities。CreateInstance<T>(IServiceProvider, Object[])作为快捷方式,而不是手动解决每个依赖项:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "");
);
传递给服务构造函数的参数(object[]参数给CreateInstance<T>/CreateInstance)允许你指定应该直接注入的参数,而不是从服务提供者解析。它们在出现时从左到右应用(即第一个字符串将被替换为要实例化的类型的第一个字符串类型参数)。
ActivatorUtilities。CreateInstance<Service>在许多地方用于解析服务并替换此单个激活的默认注册之一。
例如,如果你有一个名为MyService的类,它有IOtherService, ILogger<MyService>作为依赖项,你想解析这个服务,但是用OtherServiceB替换IOtherService的默认服务(说它是OtherServiceA),你可以这样做:
myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider,
new OtherServiceB());
然后IOtherService的第一个参数将注入OtherServiceB,而不是OtherServiceA——但其余参数将来自服务提供者。
当你有很多依赖关系,并且只想特别处理其中一个依赖关系时,这是很有帮助的(例如,用在请求期间或为特定用户配置的值替换特定于数据库的提供程序,这是你只在运行时和/或请求期间知道的,而不是在应用程序构建/启动时知道的)。
如果性能是一个问题,您可以使用ActivatorUtilities。CreateFactory(Type, Type[])来创建工厂方法。GitHub参考和基准。
当类型解析非常频繁时(例如在SignalR和其他高请求场景中),这非常有用。基本上,您可以通过
var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new Type[] { typeof(IOtherService), });
然后缓存它(作为变量等),并在需要的地方调用它:
MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);
这一切都适用于原始类型-这是我测试的一个例子:
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddTransient<HelloWorldService>();
services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow"));
var provider = services.BuildServiceProvider();
var demoService = provider.GetRequiredService<DemoService>();
Console.WriteLine($"Output: {demoService.HelloWorld()}");
Console.ReadKey();
}
}
public class DemoService
{
private readonly HelloWorldService helloWorldService;
private readonly string firstname;
private readonly string lastname;
public DemoService(HelloWorldService helloWorldService, string firstname, string lastname)
{
this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService));
this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname));
this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname));
}
public string HelloWorld()
{
return this.helloWorldService.Hello(firstname, lastname);
}
}
public class HelloWorldService
{
public string Hello(string name) => $"Hello {name}";
public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}";
}
// Just a helper method to shorten code registration code
static class ServiceProviderExtensions
{
public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class =>
ActivatorUtilities.CreateInstance<T>(provider, parameters);
}
打印
输出:Hello Tseng Stackoverflow