2009-05-15 11 views
6

Estoy usando el patrón de repositorio genérico para conservar mis datos. En PageLoad, estoy creando un nuevo objeto Repositorio (desde IRepository), y en PageUnload, me deshago de él.Patrón MVP utilizando formularios web e instanciación de objetos DI

¿Debería MasterPage/Page ser el encargado de instanciar los objetos para pasar al presentador o debería el presentador estar a cargo de esto? Estoy más interesado en probar el presentador que en la página (Vista) ya que es más fácil burlarse de las interfaces pasadas al presentador.

Ejemplo Página

public partial class _Default : System.Web.UI.Page 
{ 
    private IRepository _repo; 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     if (_repo == null) 
      _repo = new Repository(); 
     ConnectPresenter(); 
    } 

    private void ConnectPresenter() 
    { 
     _DefaultPresenter presenter = new _DefaultPresenter(_repo); 
    } 

    private void Page_Unload(object sender, EventArgs e) 
    { 
     if (_repo != null) 
      _repo.Dispose(); 
    } 
} 

¿Un marco DI como StructureMap o ayuda Ninject en este caso? ¿Estaría a cargo de deshacerse de objetos como este?

Respuesta

6

Ni la clase Page ni los presentadores deberían tener que encargarse directamente de la gestión de la construcción o el ciclo de vida de cualquiera de sus dependencias; todo esto debe ser manejado por su contenedor. Como la inyección de constructor no funciona con WebForms, deberá exponer las dependencias necesarias como propiedades en la clase. Por ejemplo, usted podría cambiar su clase a:

public partial class _Default : System.Web.UI.Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
    } 

    public _DefaultPresenter Presenter { get; set; } 
} 

La página no debería necesitar ninguna referencia al repositorio, ya que se inyecta en el presentador.

El resto de esta respuesta es específica de StructureMap; los detalles pueden diferir para otros contenedores.

Para habilitar la inyección del colocador, debe decirle a StructureMap qué propiedades debe rellenar. Una forma es aplicar el atributo [SetterProperty] a la propiedad en sí. Sin embargo, esto puede parecer un poco invasivo para tener detalles de StructureMap dentro de sus clases. Otra forma es configurar StructureMap para que sepa qué tipos de propiedades inyectar. Por ejemplo:

protected void Application_Start(object sender, EventArgs e) 
{ 
    ObjectFactory.Initialize(x => 
    { 
     x.Scan(scan => 
     { 
      scan.TheCallingAssembly(); 
      scan.WithDefaultConventions(); 
     }); 
     x.ForRequestedType<IRepository>().TheDefaultIsConcreteType<Repository>().CacheBy(InstanceScope.Hybrid); 
     x.SetAllProperties(set => set.WithAnyTypeFromNamespaceContainingType<IRepository>()); 
    }); 
} 

El método SetAllProperties permite indicar a StructureMap cómo reconocer las propiedades que debe rellenar. En este caso, le estoy diciendo a StructureMap que inyecte a todos los presentadores (suponiendo que todos estén en el mismo espacio de nombres).

Aún necesita realizar la inyección del colocador en cada solicitud.Con StructureMap, utiliza el método BuildUp() para inyectar dependencias en una instancia existente. Podrías hacerlo en los eventos Init o Load de cada página o una clase base de página, pero una vez más, eso parece invasivo. Para mantener el recipiente fuera de sus clases de página por completo, se puede utilizar el evento PreRequestHandlerExecute de la aplicación (en Global.asax o un IHttpModule):

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e) 
{ 
    var application = (HttpApplication)sender; 
    var page = application.Context.CurrentHandler as Page; 
    if (page == null) return; 
    ObjectFactory.BuildUp(page); 
} 

Por último, si quieres explícitamente se deshaga de la IRepository, se pudiera manejar que en el caso EndRequest:

protected void Application_EndRequest(object sender, EventArgs e) 
{ 
    var disposable = ObjectFactory.GetInstance<IRepository>() as IDisposable; 
    if (disposable != null) disposable.Dispose(); 
} 

Tenga en cuenta que esto funciona correctamente porque en la inicialización le dijimos StructureMap para almacenar en caché IRepository por híbrido, que significa "dame la misma instancia para cada solicitud HTTP (o hilo, si no se ejecuta dentro de un sitio web) ". Cuando recupere el IRepository en EndRequest, recibirá el mismo que utilizó durante la solicitud, y puede deshacerse de él.

2

Sí, vale la pena que investigue one of the walkthroughs out there of using DI with ASP.NET.

Sí, la eliminación de los objetos de comportamiento por petición en el punto apropiado generalmente se gestiona mediante la integración del contenedor con ASP.NET.

La disposición típica es que la creación de objetos fluye desde la Página y Application/Module s hacia adentro. Generalmente marcas las propiedades [Inject] en tu clase Page, pero depende de cómo hayas organizado tu tríada. El presentador generalmente puede usar Inyección de Constructo para declarar lo que necesita, independientemente de si es prueba o cotexto de ASP.NET. Luego, en tiempo de ejecución, las dependencias serán satisfechas por el DI. En el momento de la prueba, aún puede usar DI, aunque en otros casos podría ser más natural crear un grupo de Falsificaciones junto con el SUT y pasarlas al Presentador.

En cuanto a los arreglos triead wrt testing, encontré que this MSDN Mag article on using Ninject with xUnit.net by Justin Etheredge es muy útil, aunque está destinado a ASP.NET MVC.

+0

¡Perdón por no mirar el código o las etiquetas en su pregunta! Lo he reelaborado ahora, ¡espero que eso mejore! Eliminará esto si elimina el suyo ... –