Servicio de registro que implementa varias interfaces como singleton usando tipo genérico abierto
Hay un genérico MyClass<T>que implementa 2 interfaces genéricos IMyInterface1<T>y IMyInterface2<T>. Quiero registrarlo como singleton. Entonces, para un determinado, Tambas interfaces se resolverán en la misma instancia de MyClass.
Estoy tratando de registrarlo como tipo genérico abierto con reenvío a un método de fábrica:
services.AddSingleton(typeof(MyClass<>));
services.AddSingleton(typeof(IMyInterface1<>), sp => sp.GetRequiredService(typeof(MyClass<>)));
services.AddSingleton(typeof(IMyInterface2<>), sp => sp.GetRequiredService(typeof(MyClass<>)));
pero luego, en tiempo de ejecución, recibo una excepción con el mensaje " El tipo de servicio genérico abierto 'IMyInterface1`1 [T]' requiere registrar un tipo de implementación genérico abierto " .
Cuando estoy registrando mi singleton para cada tipo concreto T, es decir, sin un tipo genérico abierto, todo funciona bien:
services.AddSingleton(typeof(MyClass<A>));
services.AddSingleton(typeof(IMyInterface1<A>), sp => sp.GetRequiredService(typeof(MyClass<A>)));
services.AddSingleton(typeof(IMyInterface2<A>), sp => sp.GetRequiredService(typeof(MyClass<A>)));
services.AddSingleton(typeof(MyClass<B>));
services.AddSingleton(typeof(IMyInterface1<B>), sp => sp.GetRequiredService(typeof(MyClass<B>)));
services.AddSingleton(typeof(IMyInterface2<B>), sp => sp.GetRequiredService(typeof(MyClass<B>)));
Sé que hay otra forma de registrar un servicio singleton con dos interfaces: proporcionando una instancia de implementación en lugar de una fábrica de implementación. Pero no puedo usar esto en el "mundo real" porque mi clase real también tiene muchas dependencias y probablemente es imposible resolverlas todas en la etapa de registro.
¿Hay alguna forma de resolver este problema utilizando tipos genéricos abiertos?
Excepción detallada:
System.ArgumentException
HResult=0x80070057
Message=Open generic service type 'IMyInterface1`1[T]' requires registering an open generic implementation type. (Parameter 'descriptors')
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.Populate()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory..ctor(IEnumerable`1 descriptors)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine..ctor(IEnumerable`1 serviceDescriptors, IServiceProviderEngineCallback callback)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CompiledServiceProviderEngine..ctor(IEnumerable`1 serviceDescriptors, IServiceProviderEngineCallback callback)
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services)
at WebApplication1.Startup.ConfigureServices(IServiceCollection services) in ...\WebApplication1\WebApplication1\Startup.cs:line 39
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at WebApplication1.Program.Main(String[] args) in ...\WebApplication1\WebApplication1\Program.cs:line 16
Respuestas
Parece que no puede hacer esto, no es compatible.
Los siguientes problemas parecen haber solicitado esto:
- deploymentFactory para genéricos abiertos
- Inyección de dependencia de genéricos abiertos a través de fábrica
Si reemplaza sp => sp.GetRequiredService(typeof(MyClass<A>))los registros de la interfaz con typeof(MyClass<A>), terminará con una instancia singleton para cada interfaz que haya registrado, lo que no suena como lo que desea.
Lo mejor que se me ocurre, y no es particularmente bonito, es que te conviertes MyClass<T>en una fachada que gestiona su propia instancia única de la implementación subyacente. Puede hacer esto haciendo que la fachada llame a una clase no genérica con un diccionario estático (los miembros estáticos son por tipo genérico, por lo que debe ser una clase no genérica) para recuperar una instancia verdaderamente única por T.
Ejemplo
Registre sus servicios:
services.AddSingleton(typeof(MyFacadeClass<>));
services.AddSingleton(typeof(IMyInterface1<>), typeof(MyFacadeClass<>));
services.AddSingleton(typeof(IMyInterface2<>), typeof(MyFacadeClass<>));
Implementación de fachada:
public class MyImplementationClass<T> : IMyInterface1<T>, IMyInterface2<T>
{
public void Interface1Method()
{
}
public void Interface2Method()
{
}
}
public class MyClassSingletonRegister
{
public static Dictionary<Type, object> MyImplementationClassInstances = new Dictionary<Type, object>();
}
public class MyFacadeClass<T> : IMyInterface1<T>, IMyInterface2<T>
{
private readonly MyImplementationClass<T> _instance;
public MyFacadeClass()
{
_instance = GetInstance();
}
private MyImplementationClass<T> GetInstance()
{
if (!MyClassSingletonRegister.MyImplementationClassInstances.TryGetValue(typeof(T), out var instance))
{
Debug.WriteLine("Creating new instance for " + typeof(T).Name);
var myImplementationClass = new MyImplementationClass<T>();
MyClassSingletonRegister.MyImplementationClassInstances.Add(typeof(T), myImplementationClass);
instance = myImplementationClass;
}
return (MyImplementationClass<T>) instance;
}
public void Interface1Method()
{
_instance.Interface1Method();
}
public void Interface2Method()
{
_instance.Interface2Method();
}
}
public interface IMyInterface1<T>
{
void Interface1Method();
}
public interface IMyInterface2<T>
{
void Interface2Method();
}