Cabe señalar que la forma recomendada es utilizar el patrón de opciones . Pero hay casos de uso en los que no es práctico (cuando los parámetros solo se conocen en tiempo de ejecución, no en el momento de inicio / compilación) o es necesario reemplazar dinámicamente una dependencia.
Es muy útil cuando necesita reemplazar una sola dependencia (ya sea una cadena, un entero u otro tipo de dependencia) o cuando usa una biblioteca de terceros que acepta solo parámetros de cadena / entero y necesita un parámetro de tiempo de ejecución.
Puede probar CreateInstance (IServiceProvider, Object []) como una mano de acceso directo (no estoy seguro de que funcione con parámetros de cadena / tipos de valor / primitivas (int, float, string), sin probar) (Solo probé y confirmó que funciona, incluso con múltiples parámetros de cadena) en lugar de resolver cada dependencia a mano:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "");
);
Los parámetros (último parámetro de CreateInstance<T>
/ CreateInstance
) definen los parámetros que deben ser reemplazados (no resueltos por el proveedor). Se aplican de izquierda a derecha a medida que aparecen (es decir, la primera cadena se reemplazará con el primer parámetro de tipo cadena del tipo que se va a instanciar).
ActivatorUtilities.CreateInstance<Service>
se utiliza en muchos lugares para resolver un servicio y reemplazar uno de los registros predeterminados para esta única activación.
Por ejemplo, si tiene una clase nombrada MyService
, y tiene IOtherService
, ILogger<MyService>
como dependencias y desea resolver el servicio pero reemplazar el servicio predeterminado de IOtherService
(diga su OtherServiceA
) con OtherServiceB
, podría hacer algo como:
myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())
Luego IOtherService
, se OtherServiceB
inyectará el primer parámetro de , en lugar de que OtherServiceA
los parámetros restantes provengan del contenedor.
Esto es útil cuando tiene muchas dependencias y solo desea tratar una sola de manera especial (es decir, reemplazar un proveedor específico de la base de datos con un valor configurado durante la solicitud o para un usuario específico, algo que solo conoce en tiempo de ejecución y durante una solicitud y no cuando la aplicación está construida / iniciada).
También puede usar el método ActivatorUtilities.CreateFactory (Type, Type []) para crear un método de fábrica en su lugar, ya que ofrece un mejor rendimiento GitHub Reference y Benchmark .
Posteriormente, uno es útil cuando el tipo se resuelve con mucha frecuencia (como en SignalR y otros escenarios de alta solicitud). Básicamente, crearías una ObjectFactory
vía
var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });
luego almacenarlo en caché (como una variable, etc.) y llamarlo donde sea necesario
MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);
## Actualización: Lo intenté yo mismo para confirmar que también funciona con cadenas y números enteros, y de hecho funciona. Aquí el ejemplo concreto con el que probé:
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);
}
Huellas dactilares
Output: Hello Tseng Stackoverflow