2010-12-17 833 views
10

He estado jugando con el soporte DI en ASP.NET MVC RC2.Acción Filtro Dependencia Inyección en ASP.NET MVC 3 RC2 con StructureMap

He implementado sesión por solicitud para NHibernate y necesito inyectar ISession en mi filtro de acción "Unidad de trabajo".

Si me refiero al contenedor StructureMap directamente (ObjectFactory.GetInstance) o utilizar DependencyResolver para conseguir mi instancia de sesión, todo funciona bien:

ISession Session { 
     get { return DependencyResolver.Current.GetService<ISession>(); } 
    } 

Sin embargo, si intento utilizar el profesional de filtro de StructureMap (hereda FilterAttributeFilterProvider) Tengo problemas para comprometer la transacción de NHibernate al final de la solicitud.

Es como si los objetos ISession se están compartiendo entre las solicitudes. Estoy viendo esto con frecuencia, ya que todas mis imágenes se cargan a través de un controlador MVC, así que obtengo unas 20 sesiones de NHibernate creadas en una carga de página normal.

que añade lo siguiente a mi filtro de acción:

ISession Session { 
     get { return DependencyResolver.Current.GetService<ISession>(); } 
    } 

    public ISession SessionTest { get; set; } 

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) { 

     bool sessionsMatch = (this.Session == this.SessionTest); 

SessionTest se inyecta mediante el proveedor StructureMap filtro.

Encontré que en una página con 20 imágenes, "sessionsMatch" era falso para 2-3 de las solicitudes.

Mi configuración StructureMap para la gestión de sesiones es el siguiente:

 For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory()); 
     For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession()); 

En Global.asax que llamar al siguiente al final de cada solicitud:

public Global() { 
     EndRequest += (sender, e) => { 
      ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); 
     }; 
    } 

¿Este hilo configuración segura? Previamente estaba inyectando dependencias en el mismo filtro usando un IActionInvoker personalizado. Esto funcionó bien hasta MVC 3 RC2 cuando comencé a experimentar el problema anterior, por lo que pensé que trataría de usar un proveedor de filtros.

Cualquier ayuda sería apreciada.

estoy usando NHibernate 3 RC y la última versión de StructureMap

Actualización:

continuación son mis implementaciones de DependencyResolver y FilterAttributeFilterProvider:

public class StructureMapDependencyResolver : IDependencyResolver { 
    private readonly IContainer container; 

    public StructureMapDependencyResolver(IContainer container) { 
     this.container = container; 
    } 

    public object GetService(Type serviceType) { 
     var instance = container.TryGetInstance(serviceType); 
     if (instance==null && !serviceType.IsAbstract){ 
      instance = AddTypeAndTryGetInstance(serviceType); 
     } 
     return instance; 
    } 

    private object AddTypeAndTryGetInstance(Type serviceType) { 
     container.Configure(c=>c.AddType(serviceType,serviceType)); 
     return container.TryGetInstance(serviceType); 
    } 

    public IEnumerable<object> GetServices(Type serviceType) { 
     return container.GetAllInstances(serviceType).Cast<object>(); 
    } 
} 
public class StructureMapFilterAttributeFilterProvider : FilterAttributeFilterProvider 
{ 
    private readonly IContainer container; 

    public StructureMapFilterAttributeFilterProvider(IContainer container) { 
     this.container = container; 
    } 

    protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { 
     return BuildUp(base.GetControllerAttributes(controllerContext, actionDescriptor)); 
    } 

    protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { 
     return BuildUp(base.GetActionAttributes(controllerContext, actionDescriptor)); 
    } 

    private IEnumerable<FilterAttribute> BuildUp(IEnumerable<FilterAttribute> attributes) { 
     foreach (var attr in attributes) 
      container.BuildUp(attr); 
     return attributes; 
    } 
} 
+1

Estoy buscando un problema similar. – Craig

Respuesta

6

Pensé que volvería y proporcionaría la solución.

Como @Thomas se ha señalado anteriormente, los filtros son de acción ahora en caché en MVC 3. Esto significa que si se inyecta un objeto con una previsto tiempo de vida corto (por ejemplo, solicitud HTTP), que va a ser almacenado en caché.

Para solucionarlo, en lugar de inyectar un ISession inyectamos un Func<ISession>. Luego, cada vez que necesitamos acceso a ISession, invocamos la función. Esto garantiza que incluso si el ActionFilter se almacena en caché, el ISession tiene un alcance correcto.

tuve que configurar StructureMap al igual que para inyectar la instancia "perezosa" (por desgracia no inyecta una instancia perezoso automáticamente como lo hace con la inyección Ctor):

  x.SetAllProperties(p => { 
       p.OfType<Func<ISession>>(); 
      }); 

Mi ActionFilter actualizado es el siguiente:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 
public class UnitOfWorkAttribute : ActionFilterAttribute { 

    public Func<ISession> SessionFinder { get; set; } 

    public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) { 
     var session = SessionFinder(); 
     session.BeginTransaction(); 
    } 

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {   
     var session = SessionFinder(); 

     var txn = session.Transaction; 

     if (txn == null || !txn.IsActive) return; 

     if (filterContext.Exception == null || filterContext.ExceptionHandled) 
     { 
      session.Transaction.Commit(); 
     } 
     else 
     { 
      session.Transaction.Rollback(); 
      session.Clear(); 
     } 
    } 
} 
+0

Suena bien. Pero, ¿cómo se establece la sesión entonces? Establece la propiedad de acción para inyectar. Pero, ¿qué función se inyecta? – Joel

+1

Esto dependerá de cómo haya configurado las dependencias con StructureMap. Normalmente haríamos algo como 'For () .HttpContextScoped(). Use (ctx => ctx.GetInstance (). OpenSession())' –

+0

Comprobaré que – Joel

0

Quizás quiso implementar su propio IDependencyResolver que usa StructureMap? Parece que no debes tenerlo, porque configuraste la sesión como HttpContext con ámbito y, sin embargo, estás viendo sesiones separadas durante la misma solicitud.

+0

sí, tengo. He publicado mis implementaciones arriba. –

2

No sé si sería útil, pero con los filtros de acción MVC 3 ahora se almacenan en caché en lugar de crear una nueva instancia al comienzo de cada solicitud. Entonces, si estás inyectando dependencias en el constructor, no funcionará bien. ¿Podría publicar su implementación de FilterAttributeFilterProvider?

Cuestiones relacionadas