2010-07-03 8 views
6

[O] Cómo definir un ciclo de vida StructureMap para UoW a ser consumido por las peticiones HTTP y puestos de trabajo de cuarzoStructureMap devuelve un objeto de sesión nHibenrate dispuesta a partir de hilos ámbito local

tengo esta aplicación web que utiliza SM para IoC. Estoy usando el alcance HybridHttpOrThreadLocalScoped para almacenar mis objetos ness nHibernate. Esto funciona bien en una sesión por moda de solicitud para mis solicitudes web.

Pero también tengo quartz.net que programa un par de trabajos. El trabajo usa la misma unidad de trabajo para obtener el objeto ISession. En este escenario, cuando el planificador inicia el trabajo, todo funciona bien al principio y el trabajo funciona bien por un par de veces HASTA que se repita la identificación del hilo de trabajo.

Imagine que, cuando se programa el trabajo, comience a ejecutarse en hilos con identificadores 11, 12, 13 y luego con el id. De hilo 11 nuevamente. En este punto, structuremap devuelve un objeto de sesión que ya está dispuesto y obtengo "System.ObjectDisposedException: Session is closed!" error.

Por lo que puedo ver, la sesión se guarda en el almacenamiento local de subprocesos y después de que dispongo la sesión al final de mi unidad de trabajo, el objeto de sesión se mantiene en el almacenamiento local de subprocesos. Parece que después de que el hilo termina su almacenamiento local no se borra y de alguna manera cuando se crea un nuevo subproceso con el mismo ID, structuremap busca la sesión en el almacenamiento local de subprocesos antiguo (que se supone borrado para el nuevo subproceso) Creo) y devuelve el objeto de sesión que ya está dispuesto.

Preguntas:

  1. ¿Hay una manera de borrar el almacenamiento local de hilo (en extinción)?
  2. ¿Hay un equivalente de "ReleaseAndDisposeAllHttpScopedObjects" para objetos con ámbito de subprocesos?
  3. ¿Hay alguna forma de anular (o expulsar) el objeto eliminado, por lo que incluso si SM lo busca, no lo encontrará y tendrá que crear una nueva instancia?

Espero haber aclarado mi pregunta. Esto ha tomado un par de horas de mi tiempo y todavía no he encontrado una manera de evitarlo. Agradezco cualquier sugerencia:>

Actualización: he añadido mi propia solución para hacer una UoW servida por el trabajo StructureMap con ambas peticiones http y puestos de trabajo de cuarzo. Avíseme si tiene una solución mejor/más fácil/más simple.

+0

¿Está gestionando sus IJobs de cuarzo con StructureMap? –

+0

@Mauricio: Estoy usando StructureMap en mi aplicación. Sin embargo, no estoy seguro de lo que quiere decir con la gestión de trabajos de cuarzo por StructeMap:> – kaptan

+0

¿sus instancias de Quartz IJob son administradas por StructureMap? En otras palabras: ¿registra sus trabajos en el contenedor? –

Respuesta

1

¿Por qué no crear una nueva sesión para los trabajos de cuarzo? Una unidad de trabajo suele ser una operación transaccional en el db. No puedo imaginar que los trabajos de cuarzo estén relacionados transaccionalmente con las solicitudes/respuestas web. Crear nuevas sesiones no es costoso. ¿Es esta una posibilidad?

+0

Sí. Quiero crear una nueva sesión para cada trabajo de cuarzo. Pero dado que estoy usando la idea de UoW, quiero que sea consistente en mi aplicación. Por lo tanto, no quiero crear sesiones directamente para trabajos de cuarzo. Quiero crear una instancia UoW. Pero al mismo tiempo quiero usar StructureMap para obtener una instancia de UoW. Es por eso que terminé definiendo un ciclo de vida híbrido en StructeMap para mi UoW de modo que devolviera la UoW apropiada para cada solicitud de HTTP o cada ejecución de trabajo de cuarzo. – kaptan

+0

@kaptan: estoy de acuerdo con @rcravens. Lo que he hecho es obtener una instancia de ISessionFactory (ObjectFactory.GetInstance ()) y abrir una nueva sesión cuando mi trabajo se dispare. – LeftyX

2

Estaba revisando lo que hice para hacer que StructureMap funcione con UoW por Http y UoW por trabajo de cuarzo y decidí compartir mi solución aquí.

Así que la idea era que quería usar el alcance híbrido de StructureMap para obtener una instancia de UoW cuando hay un contexto http y también obtener una instancia diferente de UoW por subproceso cuando no hay contexto http (como cuando un trabajo de cuarzo incendios). De esta manera:

For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<UnitOfWork>(); 

La UoW para http funcionó bien. El problema era UoW por hilo.

Esto es lo que sucede. Cuando un trabajo de quratz se desencadena, extrae un hilo del grupo de subprocesos y comienza a ejecutar el trabajo con ese subproceso. Cuando el trabajo comienza solicito una UoW. StructureMap busca en el almacenamiento local que ese subproceso devuelva el UoW, pero como no puede encontrar ninguno, crea una instancia y lo guarda en el almacenamiento local del subproceso. Obtengo la UoW, luego perfom Begin, Commit, Dispose y todo está bien.

El problema ocurre cuando se extrae un hilo del grupo de subprocesos que se utilizó antes para iniciar un trabajo (y se usó un UoW). Aquí, cuando solicita un UoW, StructureMap busca en el caché (enrutar el almacenamiento local) y encuentra un UoW y se lo devuelve. ¡Pero el problema es que UoW está dispuesto!

Así que realmente no podemos usar UoW ​​por hilo para trabajos de cuarzo porque los hilos en sí mismos no están dispuestos y contienen los viejos UoW eliminados en caché. Básicamente, el ciclo de vida de un hilo no coincide con el ciclo de vida de un trabajo de cuarzo. Es por eso que creé mi propio ciclo de vida para un trabajo de cuarzo.

En primer lugar he creado mi propia clase ciclo http-cuarzo vida híbrida:

public class HybridHttpQuartzLifecycle : HttpLifecycleBase<HttpContextLifecycle, QuartzLifecycle> 
{ 
    public override string Scope { get { return "HybridHttpQuartzLifecycle"; } } 
} 

Entonces creé mi clase QuartzLifecyle:

public class QuartzLifecycle : ILifecycle 
{ 

    public void EjectAll() 
    { 
     FindCache().DisposeAndClear(); 
    } 

    public IObjectCache FindCache() 
    { 
     return QuartzContext.Cache; 
    } 

    public string Scope { get { return "QuartzLifecycle"; } } 
} 

entonces necesito para crear alguna clase contexto como HttpContext de cuarzo para mantener la información relacionada con el contexto. Así que creé la clase QuartzContext. Cuando se activa un trabajo de cuarzo, el JobExecutionContext para ese trabajo debe registrarse en QuartzContext. Entonces la caché real (MainObjectCache) para las instancias de StructureMap se creará bajo ese JobExecutionContext específico. Así que de esta manera, una vez que finalice la ejecución del trabajo, la memoria caché también desaparecerá y no tendremos problemas para eliminar UoW ​​en el caché.

Además, dado que _jobExecutionContext es ThreadStatic, cuando solicitemos el caché desde QuartzContext, devolverá el caché del JobExecutionContext que se guarda para el mismo hilo. Por lo tanto, cuando se ejecutan varios trabajos al mismo tiempo, sus JobExecutionContexts se guardan por separado y tendremos cachés separados para cada trabajo en ejecución.

public class QuartzContext 
{ 

    private static readonly string _cacheKey = "STRUCTUREMAP-INSTANCES"; 

    [ThreadStatic] 
    private static JobExecutionContext _jobExecutionContext; 

    protected static void Register(JobExecutionContext jobExecutionContext) 
    { 
     _jobExecutionContext = jobExecutionContext; 
     _jobExecutionContext.Put(_cacheKey, new MainObjectCache()); 
    } 

    public static IObjectCache Cache 
    { 
     get 
     { 
      return (IObjectCache)_jobExecutionContext.Get(_cacheKey); 
     } 
    } 
} 

Tengo una clase abstracta llamada BaseJobSingleSession de la que derivan otros trabajos. Esta clase extiende la clase QuartzContext. Puede ver que registro el JobExecutionContext cuando se dispara el trabajo.

abstract class BaseJobSingleSession : QuartzContext, IStatefulJob 
{ 
    public override void Execute(JobExecutionContext context) 
    { 
     Register(context); 
     IUnitOfWork unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>(); 

     try 
     { 
      unitOfWork.Begin(); 

      // do stuff .... 

      unitOfWork.Commit(); 
     } 
     catch (Exception exception) 
     { 
      unitOfWork.RollBack(); 

     } 
     finally 
     { 
      unitOfWork.Dispose(); 
     } 
    } 
} 

Finalmente he definido el ciclo de vida de UoW: (. Para el ciclo de vida y clases de contexto Miré en el código fuente StructureMap para obtener la idea)

For<IUnitOfWork>().LifecycleIs(new HybridHttpQuartzLifecycle()).Use<UnitOfWork>(); 

favor comparta sus ideas, comentarios y sugerencias:>

Cuestiones relacionadas