Archive

Archive for janvier 2013

NHibernate : Session is closed !

janvier 23, 2013 Laisser un commentaire

Si comme moi, vous avez utilisé NHibernate comme OR/M sur des applications web suffisamment complexes (pas nécessairement en terme métier mais en termes de flux entre le serveur et la couche client), vous aviez probablement rencontré l’exception « intermittente » : ObjectDisposedException => Session is closed ! , qui se déclenche d’une manière complètement non reproductible (le débogueur attaché au processus web n’arrive même pas à situer le point de départ de l’exception dans le code). Et en release, rien ne se manifeste visuellement mais les requêtes effectuées par NHibernate derrière ne se passent plus normalement.

Origine probable du problème : une session NHibernate est ouverte au démarrage d’une demande http, et est fermée par je-ne-sais-quoi, avant que certaines parties du code ait besoin d’un rafraîchissement des objets POCO ou de certaines collections (par lazy-load)… et donc effectivement : la session en cours d’utilisation est subitement fermée => boom !

Dans certaines discussions sur le web, beaucoup de gens proposent la non-utilisation du lazy-loading, mais à mon avis :

–          Cela ne résout que partiellement le problème (on évite le rafraîchissement des collections ou des propriétés en lazy-load mais on n’évite pas le rafraîchissement des objets POCO)

–          et déjà, le lazy-loading est  un besoin évident pour certains types d’application (je ne vais quand même pas requêter des milliers de ligne si je n’ai besoin que des infos d’une table parent, et que si le chargement de ces milliers de lignes n’est nécessaire qu’à la demande de l’utilisateur)

Pour y remédier, voici selon moi les précautions à prendre (cela après des jours de recherche google et de refactoring de codes) :

  • Dans une application web, ne jamais utiliser les sessions NHibernate dans un bloc using. Car à la fin du « using », l’objet est détruit et au moment du rafraîchissement ou du lazy-loading, bah, le message le dit si bien « Session is closed ! ». Opter par l’initialisation/fermeture de session en adoptant le pattern OSIV (Open Session In View) qui signifie : créer une session au démarrage d’une demande http (Application_BeginRequest) et disposer cette même instance à la fin de la demande (Application_EndRequest). Avec les versions 3.xxx de NHibernate, on peut rajouter une ligne de code à chaque instanciation de session, pour dire que la session est « bindée » avec un contexte Web :

ManagedWebSessionContext.Bind(HttpContext.Current, session);

  • Si de plus, l’application tourne avec un moteur d’IoC ; utiliser une injection des objets Session au niveau de la couche de présentation (controller dans le cas d’une architecture MVC) et dire au moteur IoC de garder la session ouverte pendant le traitement d’une demande http : i.e. LifeStyle = PerWebRequest.

Exemple avec Windsor Castle :

container.Register(Component.For<ISession>().Instance(session).LifeStyle.PerWebRequest);

  • Et surtout surtout ! (ce qui nous est arrivé récemment) : ne jamais utiliser plusieurs fabriques de session (SessionFactory). Si par besoin métier, on veut avoir différentes façons d’accéder à la base, rattacher plusieurs sessions (ISession) à une instance unique de SessionFactory ; mais jamais en créant plusieurs instances de SessionFactory…
Publicités
Catégories :Microsoft .Net