推荐实现这一点的方法是使用
选项模式 - 请注意,这适用于任何.NET Core/5应用程序,而不仅仅是ASP.NET Core。但有些情况下这样做是不切实际的(例如当参数仅在运行时而不是在启动/编译时已知),或者您需要动态替换依赖项。
当您需要替换单个依赖项(无论是字符串、整数还是其他类型的依赖项)或者使用一个只接受字符串/整数参数并且需要运行时参数的第三方库时,它非常有用。
您可以尝试
ActivatorUtilities.CreateInstance<T>(IServiceProvider, Object[])
作为快捷方式,而不是手动解析每个依赖项:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "")
)
传递给服务构造函数的参数(
CreateInstance<T>
/
CreateInstance
的
object[]
参数)允许您直接指定要注入的参数,而不是从服务提供程序中解析。它们按照它们出现的顺序从左到右应用(例如,第一个字符串将被替换为要实例化的类型的第一个字符串类型的参数)。
ActivatorUtilities.CreateInstance<Service>
在许多地方用于解析服务并替换此单个激活的默认注册之一。例如,如果您有一个名为
MyService
的类,它具有
IOtherService
、
ILogger<MyService>
作为依赖项,并且您想要解析服务但替换
IOtherService
的默认服务(假设是
OtherServiceA
),则可以像这样做:
myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider,
new OtherServiceB())
然后,IOtherService
的第一个参数将获得注入OtherServiceB
,而不是OtherServiceA
- 但其余参数将来自服务提供者。
当您有许多依赖项并且只想特别处理单个依赖项时,这很有帮助(即使用在请求期间或特定用户为其配置的值替换特定于数据库的提供程序,仅在运行时和/或请求期间才知道 - 而不是应用程序构建/启动时)。
如果性能是问题,您可以使用ActivatorUtilities.CreateFactory(Type, Type[])
创建工厂方法。 GitHub reference 和 benchmark。
当该类型频繁解析时非常有用(例如在SignalR和其他高请求场景中)。基本上,您将通过创建一个ObjectFactory
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}";
}
static class ServiceProviderExtensions
{
public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class =>
ActivatorUtilities.CreateInstance<T>(provider, parameters);
}
{{打印}}
Output: Hello Tseng Stackoverflow