Autofac con Open Genéricos y tipo especificado en tiempo de ejecución
-
19-09-2019 - |
Pregunta
La documentación indica que Autofac apoya genéricos abiertos y yo soy capaz de registrar y resolver en un caso básico de esta manera:
Registro:
builder.RegisterGeneric(typeof(PassThroughFlattener<>))
.As(typeof(IFlattener<>))
.ContainerScoped();
Resolve:
var flattener = _container.Resolve<IFlattener<Address>>();
El código anterior funciona bien. Sin embargo, en el supuesto que no voy a saber el tipo proporcionado a IFlattener hasta el tiempo de ejecución, lo que quiero hacer algo como esto:
object input = new Address();
var flattener = (IFlattener)_container.Resolve(typeof(IFlattener<>), new TypedParameter(typeof(IFlattener<>), input.GetType()));
¿Es esto posible con autofac? Tengo la idea de la siguiente usando StructureMap:
http://structuremap.sourceforge.net/Generics.htm
Estoy tratando de lograr el mismo objetivo descrito en este artículo.
Solución
Esto es ciertamente posible con Autofac. En "registrar el tiempo", esto es lo que básicamente hace:
- Registrar el tipo genérico abierto (PassThroughFlattener <>)
- registrar ningún tipo específico (AddressFlattener)
- El registro de un método que se puede utilizar para resolver un IFlattener basado en un objeto de entrada
En "tiempo de resolver", que va a hacer:
- Resolver el método
- llama al método con el parámetro (s) de entrada para resolver la aplicación IFlattener
Aquí hay una (esperemos) de la muestra de trabajo:
var openType = typeof(IFlattener<>);
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(PassThroughFlattener<>)).As(openType);
builder.Register<AddressFlattener>().As<IFlattener<Address>>();
builder.Register<Func<object, IFlattener>>(context => theObject =>
{
var concreteType =
openType.MakeGenericType(theObject.GetType());
return (IFlattener) context.Resolve(concreteType,
new PositionalParameter(0, theObject));
});
var c = builder.Build();
var factory = c.Resolve<Func<object, IFlattener>>();
var address = new Address();
var addressService = factory(address);
Assert.That(addressService, Is.InstanceOfType(typeof(AddressFlattener)));
var anything = "any other data";
var anyService = factory(anything);
Assert.That(anyService, Is.InstanceOfType(typeof(PassThroughFlattener<string>)));
Otros consejos
Si usted no sabe el tipo hasta el tiempo de ejecución se puede construir utilizando MakeGenericType:
var addressFlattener = _container.Resolve(typeof(IFlattener<>).MakeGenericType(typeof(Address)));