try todas tipos propagacion programacion manejo las excepciones ejemplos definicion catch arbol nhibernate transactions ninject rollback

todas - ¿Cómo revertir la transacción nHibernate cuando se produce una excepción durante la solicitud que tiene Ninject para la gestión de sesiones?



try catch definicion (2)

Uso nHibernate para ORM y Ninject para IoC. Creo sesiones nHibernate por cada ámbito personalizado (que puede asumir es por solicitud). Comienzo la transacción en Activación. Confirmo la transacción en desactivación.

El problema es que si ocurre una excepción durante la solicitud, quiero retrotraer la transacción en lugar de comprometerla. ¿Alguna idea de cómo detectar (de una manera limpia, probablemente usando el Contexto de Ninject) que ha ocurrido una excepción?

Nota: No me preocupan las excepciones que pueden ocurrir en commit que puedo detectar fácilmente en el siguiente código y función.

protected void BindWithSessionWrapper<T>(Func<IContext, T> creationFunc) where T : ISessionWrapper { Bind<T>().ToMethod(creationFunc) .InScope(x => new NinjectCustomScope()) // work in progress !!! .OnActivation(t => t.Session.BeginTransaction(IsolationLevel.ReadCommitted)) .OnDeactivation((c, t) => { t.Session.Transaction.Commit(); t.Session.Dispose(); }); }

Actualizar:

Seguí la sugerencia de @BatteryBackupUnit. Así que agregué lo siguiente al Error EventHandler:

Error += (s, e) => { HttpContext.Current.Items["ErrorRaised"] = true; };

Y modifiqué OnDeactivation para que se vea así:

OnDeactivation(t => { if ((bool?)HttpContext.Current.Items["ErrorRaised"] == true) t.Session.Transaction.Rollback(); else t.Session.Transaction.Commit(); t.Session.Dispose(); });

Funciona bien, pero sería mejor si Ninject se ocuparía de esto al establecer una bandera en el contexto si ocurriera una excepción :)


¿Qué le IHTTPModule implementar un IHTTPModule y suscribirse al evento Error ? Como se describe aquí

En el controlador de eventos Error , use System.Web.Mvc.DependencyResolver.Current.GetService(typeof (ISession)) para recuperar la sesión actual y deshacer la transacción.

Sin embargo, tenga en cuenta que, en caso de que la solicitud no use una sesión, se creará una, lo cual es bastante superfluo.

Puede hacer algo como verificar si se inició una transacción y solo luego revertirla. Pero aún crearías una sesión innecesariamente.

Puede mejorar aún más utilizando el controlador de eventos Error para establecer un HttpContext.Current.Items en HttpContext.Current.Items , como

HttpContext.Current.Items["RollbackTransaction"] = true;

y luego usarlo en la OnDeactivation de la sesión como:

.OnDeactivation((c, t) => { if(HttpContext.Current.Items.Contains("RollbackTransaction"]) { t.Session.Transaction.Rollback(); } else { t.Session.Transaction.Commit(); } t.Session.Dispose(); });

Tenga en cuenta que HttpContext es thread local, eso significa que cuando se cambian los hilos puede ser null o, en el peor de los casos, podría ser otro HttpContext .

Tenga en cuenta también que no pude probarlo para que no funcione. Comentarios apreciados.


Pasar el estado a través de HttpContext no es aceptable para mí por dos razones.

  1. Problema de HttpContext: https://.com/a/12219078/656430 )
  2. Aprobar el estado parece pasar un estado global ( https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil )

Después de muchas pruebas y errores, creo que esta debería ser una solución: suponiendo que estamos trabajando en el proyecto WebApi, al tener una transacción de reversión para todas las acciones, una vez que lleguemos a la excepción, con Ninject:

  1. instale Ninject.Extension.Factory ( https://www.nuget.org/packages/Ninject.Extensions.Factory/ ), este es un paso muy importante para inyectar ISession en el alcance de la solicitud en los filtros.
  2. utilice la siguiente configuración para enlazar ISessionFactory e ISession (hice uso de este ejemplo: Necesito un ejemplo simple de usar nhibernate + unidad de trabajo + patrón de repositorio + capa de servicio + ninject ), más ISessionInRequestScopeFactory

    Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope(); Bind<ISession>() .ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()) .InRequestScope(); // notice that we don''t need to call `BeginTransaction` at this moment Bind<ISessionInRequestScopeFactory>().ToFactory(); // you don''t need to make your implementation, the Ninject.Extension.Factory extension will help you so.

  3. el código para la interfaz ISessionInRequestScopeFactory :

    public interface ISessionInRequestScopeFactory { ISession CreateSessionInRequestScope(); // return ISession in the request scope }

  4. Utilice la inyección de filtro ninject para agregar el comportamiento de transacción a cada acción ( https://github.com/ninject/Ninject.Web.WebApi/wiki/Dependency-injection-for-filters ):

    Kernel.BindHttpFilter<ApiTransactionFilter>(System.Web.Http.Filters.FilterScope.Action) .WhenControllerHas<ApiTransactionAttribute>();

  5. agrega el atributo [ApiTransaction] al controlador:

    [ApiTransaction] public class YourApiController{ /* ... */}

  6. Así que ahora estamos vinculando el ApiTransactionFilter en YourApiController que están teniendo [ApiTransaction] Attribute

  7. Dentro de ApiTransactionFilter , debe extender AbstractActionFilter e inyectar la fábrica ISessionInRequestScopeFactory para obtener la sesión de ámbito de solicitud correcta :

    public class ApiTransactionFilter : AbstractActionFilter{ private readonly ISessionInRequestScopeFactory factory; public ApiTransactionFilter(ISessionInRequestScopeFactory factory){ this.factory = factory; } public override void OnActionExecuting(HttpActionContext actionContext) { ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory session.BeginTransaction(); // session can begin transaction here ... base.OnActionExecuting(actionContext); } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory if (actionExecutedContext.Exception == null) // NO EXCEPTION! { session.Transaction.Commit();// session commit here ... may be you like to have try catch here } else { session.Transaction.Rollback(); // session rollback here ... } base.OnActionExecuted(actionExecutedContext); } }