2011-08-26 18 views
21

Tengo un controlador base abstracto que tiene un constructor que esperaba que fuera poblado por autofac cuando se construyeron los controladores.Inyección de constructor en una clase base usando autofac

public abstract class BaseController : Controller 
{ 
    protected ILogger { get; private set; } 

    protected BaseController() 
    { 
    } 

    protected BaseController(ILogger logger) 
    { 
     Logger = logger; 
    } 
} 

Esto no parece funcionar cuando obtengo un controlador de él.

Solo puedo hacer que esto funcione cuando llamo explícitamente al constructor desde el controlador. ¿Es esta la forma correcta de hacer esto?

public class PublicController : BaseController 
{ 
    public PublicController() 
    { 
    } 

    public PublicController(ILogger logger) : base(logger) 
    { 

    } 
} 

Además, utilizando el conjunto Intergration MVC, tampoco parece ser una manera de compartir el contenedor para otras clases para hacer su propio resolver. Leí en algún lado que esto no se fomenta, ¿por qué no? ¿Esto es solo para desacoplar la dependencia de cualquier marco de trabajo individual? Es la inyección del constructor el única forma para poblar las dependencias en la heiraquía.

Gracias

Respuesta

48

Llamar al constructor de la clase base explícitamente es la única forma de hacer esto usando la inyección de constructor en C#. Parece que debería eliminar los constructores sin parámetros de BaseController y PublicController, ya que nunca deberían llamarse cuando hay un registrador disponible.

El problema de inyectar dependencias en un controlador base es común con ASP.NET MVC e IoC. Hay varias opciones/escuelas de pensamiento.

1.) Utilice los servicios agregados. Para mantener simples a los constructores de clase derivados, cree un servicio único que exponga o delegue a todos los diferentes servicios necesarios para el controlador base (por ejemplo, IBaseControllerDependencies o similar). Pase este servicio al BaseController tal como lo hace con ILogger aquí.

Existen varias ventajas y desventajas según su aplicación y la cantidad de clases base que esté utilizando.Google para 'servicios agregados Autofac' para ver más sobre esto.

2.) Utilice la inyección de propiedad. Hacer que la propiedad pública ILogger en su clase base, y configurar el contenedor usando:

builder.RegisterControllers().PropertiesAutowired(); 

inyección de la propiedad no es realmente una técnica preferida en Autofac. El rol del constructor es para aceptar dependencias, mientras que las propiedades escribibles a menudo se ven como un olor a código, por lo que Autofac realmente no se optimiza para este caso. Uno de los inconvenientes es que las propiedades de escritura que no deberían inyectarse a menudo son erróneas, con consecuencias extrañas.

3.) Refactorizar la funcionalidad del controlador base en varios filtros de acción. Autofac puede inyectar filtros de acción en la canalización de invocación de acciones de MVC. Por lo tanto, los filtros pueden tomar las dependencias que estaban en la clase base, y las mismas preocupaciones podrían aplicarse de forma transversal. Más información al respecto en la web, ExtensibleActionInvoker y .InjectActionInvoker() apuntan a la información que necesitaría. No siempre es posible con todas las preocupaciones.

4, también la respuesta a su segunda pregunta.) Resuelva las dependencias del controlador base utilizando la ubicación del servicio desde DependencyResolver.Current.

var logger = DependencyResolver.Current.GetService<ILogger>(); 

La razón de que esto no se anima es que hace que la aplicación resultante más difícil de entender, porque ya no es posible ver qué servicios un componente depende mirando en un solo lugar (el constructor). Para determinar lo que debe configurarse en el contenedor antes de que se pueda usar un componente en particular, hay que mirar toda la base de código del componente para encontrar las llamadas GetService(). Un impedimento notable cuando pruebas unitarias.

Espero que esto ayude, un poco de un vertedero de cerebros que sé :) Otros probablemente puedan agregar algunas ideas más a estos.

2

no tengo privilegios de comentario para pedir más detalles, pero que necesitaría para ver el código de registro para entender lo que se espera y por qué el contenedor no está cumpliendo con las expectativas.

En cuanto a sus preguntas adicionales: no hay necesariamente una forma para que una clase resuelva por su cuenta, pero una clase puede depender de una fábrica de objetos que se puede resolver automáticamente para un tipo conocido (que con frecuencia es suficiente). Si ha registrado A, puede tomar una dependencia en Func<A>. Hay muchos tipos de relaciones como esta en Autofac; ver http://code.google.com/p/autofac/wiki/RelationshipTypes.

"Compartir el contenedor" no se recomienda porque tiende a ocultar las dependencias de una clase. Si se toma una dependencia del contenedor, o se hace referencia a él como un objeto "IoC" global, se pierde la expresividad de las dependencias específicas.

La resolución de dependencias en la jerarquía no debería ser un problema, ya que estás usando inyección de c'tor, no estoy seguro de cuál es tu problema. Hay preocupaciones secundarias, como modificar todas las subclases si cambian las dependencias de la clase base. Afortunadamente, Autofac tiene una respuesta para eso en Servicios agregados (http://code.google.com/p/autofac/wiki/AggregateService).

Autofac es una herramienta genial y profunda. Una sugerencia más, si recién está empezando, tómese un tiempo para analizar la vida útil de los objetos y los ámbitos de duración (especialmente http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/). ¡Buena suerte!

0

Autofac para .NET Core

Instalar

PM> Install-Package Autofac.Core.NonPublicProperty 

Uso

builder.RegisterType<AppService>() 
    .AsImplementedInterfaces() 
    .AutoWireNonPublicProperties(); 
Cuestiones relacionadas