IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

NHibernate Discussion :

[WCF][NHibernate] HttpModule pour gérer les sessions NHibernate


Sujet :

NHibernate

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2009
    Messages : 35
    Points : 24
    Points
    24
    Par défaut [WCF][NHibernate] HttpModule pour gérer les sessions NHibernate
    Bonjour à tous

    3 couches dans mon "projet" :
    1. Accés aux données avec NHibernate (via Fluent NHibernate)
    2. Service Librairy (avec les références de l’accès aux données)
    3. WebService (.svc) (avec les références au Service Librairy)


    Je voudrais que les sessions (NHibernate) soient créées et détruites dans le même timing que les requêtes http. J'ai vu et lu beaucoup d'articles sur le net qui parle de ce système. Malgré cela je ne parviens pas réussir cette prouesse.

    Voici ce que j'ai fait :

    ** Web service **

    web.config

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <httpModules>
    	<add name="NHibernateHttpModule" type="Data.Connection.HttpModule.NHibernateHttpModule"/>
    </httpModules>
    ** Service Librairy **

    Appel à une methode du contract

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public IList<ContactData> GetContactInfo(string param){
    (...)
    ContactRepository.GetContacts(param);
    Le repository est créé de cette manière

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    private IContactRepository ContactRepository {
                get {
                    if (_contactRepository == null)
                        _contactRepository = RepositoryFactory<IContactRepository>.CreateRepository();
                    return _contactRepository;
                }
                set { ;}
            }
    ** Accès aux données **

    Repository contact fait appel à la propriété Session

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ICriteria criteria = Session.CreateCriteria<Contact>();
    C'est ici que ça ne se passe pas bien. _session me renvoi null

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    protected ISession Session{
                get {
                    if (_session == null) { 
                        //_session = FNSettings.CreateSession();
                        log.Debug("Create new session");
                        _session = FNSettings.GetCurrentSession();
                    }
                    return _session;
                }
            }
    HttpModule dans la couche d'accès au données

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    public class NHibernateHttpModule : IHttpModule
        {
     
            public void Dispose(){ ; }
     
            public void Init(HttpApplication context)
            {
                context.EndRequest += ApplicationEndRequest;
                context.BeginRequest += ApplicationBeginRequest;
            }
     
            public void ApplicationBeginRequest(object sender, EventArgs e) {
                NHibernate.Context.CurrentSessionContext.Bind(FNSettings.CreateSession());
            }
     
            public void ApplicationEndRequest(object sender, EventArgs e) {
                ISession currentSession = NHibernate.Context.CurrentSessionContext.Unbind(FNSettings.SessionFactory);
                currentSession.Close();
                currentSession.Dispose();
            }
        }
    Create Session

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public static ISession CreateSession() {
                return SessionFactory.OpenSession();
            }
    SessionFactory

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public static ISessionFactory SessionFactory {
                get {
                    if (_sessionFactory == null)
                        _sessionFactory = FNSettings.CreateSessionFactory();
                    return _sessionFactory;
                }
            }
    Configuration Nhibernate (via Fluent NHibernate)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    private static FluentConfiguration FNConfiguration{
                get {
                    if (_fnConfiguration == null){
                        var cfg = OracleClientConfiguration.Oracle10
                            .ConnectionString(c => c.Is(FNSettings.ConnectionString))
                            .Dialect<NHibernate.Dialect.Oracle10gDialect>()
                            .ShowSql();
     
                        _fnConfiguration = Fluently.Configure()
                            .Database(cfg)
                            .ExposeConfiguration(
                                c => c.SetProperty("current_session_context_class", "web")) //use for HttpModule 
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ContactMap>());
                    }
                    return _fnConfiguration;
                }
            }
    Voilà qqu'un peut-il m'aider ?

    Bien à vous

  2. #2
    Membre confirmé Avatar de NicoL__
    Homme Profil pro
    Architecte
    Inscrit en
    Janvier 2011
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Janvier 2011
    Messages : 399
    Points : 577
    Points
    577
    Par défaut
    Tout ces morceaux de codes ne donne pas la vision de l'architecture des classes.
    Mais comme ça je dirais que ton NHibernateHttpModule me semble un peu light et bien qu'il gère le close des sessions hibernate ne gère pas vraiment leur création et leur cycle de vie.
    Je pense qu'il faut ce genre de méthode devrait se trouver dans NHibernateHttpModule
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    public static ISession CurrentSession
        {
            get
            {
                if (HttpContext.Current != null)
                {                           
                    HttpContext currentContext = HttpContext.Current;
                    ISession session = currentContext.Items[KEY] as ISession;
                    if (session == null)
                    {
                        session = SessionHelper.OpenSession(); 
                        currentContext.Items[KEY] = session;
                    }
                    return session;
                }
            }
        }
    Et de passer par NHibernateHttpModule.CurrentSession() pour récupérer la bonne session hibernate.

    Après le SessionHelper peut utiliser Fluent pour créer la session mais n'utilise pas un singleton (sinon une session pour tout le monde et toutes les requêtes). Mais les sessions doivent être gérer au niveau du HttpContext pour une session par requête.

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2009
    Messages : 35
    Points : 24
    Points
    24
    Par défaut
    Bonjour

    Merci de votre réponse.

    Je viens de logger le tout et je me rend compte que c'est vraiment à l'appel de la GetCurrentSession que j'ai un souci.

    Donc mon HttpModule travail très bien. Lors du bind je log l'id de la session. De même je log l'id de la session lors du unbind.

    Les deux sont identiques.

    Je travail avec des WCF peut être que la gestion de context ("current_session_context_class" , "web") n'est pas adapté pour les wcf. J'ai également essayer managed_web, mais même résultat.

    Le but est d'utiliser le GetCurrentSession de ma SessionFactory. Je continue donc mes recherches

    Bien à vous

  4. #4
    Membre confirmé Avatar de NicoL__
    Homme Profil pro
    Architecte
    Inscrit en
    Janvier 2011
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Janvier 2011
    Messages : 399
    Points : 577
    Points
    577
    Par défaut
    Vous voulez tour simplement dire que l'appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    _session = FNSettings.GetCurrentSession();
    ne fonctionne pas et renvoie null. Je ne vois pas trop à quoi correspond FNSettings.
    Mais que donne le la ligne commenté
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    _session = FNSettings.CreateSession()
    Mais à mon sens ça devrait ressemble à ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    _session = FNSettings.SessionFactory.OpenSession()

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2009
    Messages : 35
    Points : 24
    Points
    24
    Par défaut
    Bonjour

    FNSettings est une classe abstraite contenant toute la logique de configuration de Fluent Nhibernate.

    Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public static ISession CreateSession() {
                return SessionFactory.OpenSession();
            }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      public static ISession GetCurrentSession() {
                return SessionFactory.GetCurrentSession();
            }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public static ISessionFactory SessionFactory {
                get {
                    if (_sessionFactory == null){
                        _sessionFactory = FNSettings.CreateSessionFactory();
                    }
                    return _sessionFactory;
                }
            }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    private static FluentConfiguration FNConfiguration{
                get {
                    if (_fnConfiguration == null){
                        var cfg = OracleClientConfiguration.Oracle10
                            .ConnectionString(c => c.Is(FNSettings.ConnectionString))
                            .Dialect<NHibernate.Dialect.Oracle10gDialect>()
                            .ShowSql();
     
                        _fnConfiguration = Fluently.Configure()
                            .Database(cfg)
                            .CurrentSessionContext<NHibernate.Context.WebSessionContext>() //use for HttpModule 
                            //.ExposeConfiguration(
                            //    c => c.SetProperty("current_session_context_class", "web")) 
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ContactMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<PersonMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ScreeningMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<SxCurrentAccountMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<AgencyMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CurrentAccountMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ClientMap>())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<OpeningHoursMap>());
                    }
                    return _fnConfiguration;
                }
            }
    Juste pour info voici a quoi ressemble la dernière version de mon HttpModule.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public void ApplicationBeginRequest(object sender, EventArgs e) {
                ISession session = FNSettings.CreateSession();
                //NHibernate.Context.ManagedWebSessionContext.Bind(HttpContext.Current, FNSettings.CreateSession());
                NHibernate.Context.WebSessionContext.Bind(session);
     
                //log
                log.Debug("ApplicationBeginRequest - Bind Session : " + ((ISessionImplementor)session).SessionId);
     
            }
     
            public void ApplicationEndRequest(object sender, EventArgs e) {
                //ISession session = NHibernate.Context.ManagedWebSessionContext.Unbind(HttpContext.Current, FNSettings.SessionFactory);
                ISession session = NHibernate.Context.WebSessionContext.Unbind(FNSettings.SessionFactory);
     
                if (session != null) {
     
                    //log
                    log.Debug("ApplicationEndRequest - Unbind session : " + ((ISessionImplementor)session).SessionId);
     
                    if (session.Transaction != null
                        && session.Transaction.IsActive){
                        session.Transaction.Rollback();
     
                        //log
                        log.Warn("ApplicationEndRequest - Force transaction to rollback");
                    }else{
                        session.Flush();
     
                        //log
                        log.Debug("ApplicationEndRequest - Session were flush");
                    }
                    session.Close();
                    session.Dispose();
                }
            }
    C'est comme si au moment de l'appel de GetCurrentSession il ne trouve pas le context Http et que du coup il me renvoi null...

    Pourtant ce que j'ai fait c'est
    1. Dans la configuration ajouter un ligne pour dire que le current context était gérer "web"
    2. Créer un Http module qui lit le context http à une session NHibernate
    3. ajouter dans le webconfig tout ce qu'il faut


    Pour qu'au final l'appel de GetCurrentSession ne fonctionne pas MAIS que le http module fait bien son boulot.

    Punaise, là je suis vraiment paumé

  6. #6
    Membre confirmé Avatar de NicoL__
    Homme Profil pro
    Architecte
    Inscrit en
    Janvier 2011
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Janvier 2011
    Messages : 399
    Points : 577
    Points
    577
    Par défaut
    Il faudrait vérifier l'état de la SessionFactory

    Mais sinon je ne vois pas, et je ne vois pas l’intérêt de FNSettings puisque fluent a déjà une interface simplifiée.

    Il y a peut-être un problème de lock... en tout cas ça manque de l'implémentation des singletons que tu as réalisé. Et en web le contexte est forcement multithreadé.

    Un truc touvé sur internet qui semble simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    static readonly object factorylock = new object();
     
    public ISession OpenSession()
    {
        lock (factorylock)
        {
           if (_sessionFactory == null)
           {
                var cfg = Fluently.Configure().
                     Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")).
                   Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>());
                _sessionFactory = cfg.BuildSessionFactory();
                BuildSchema(cfg);
            }
        }
        return _sessionFactory.OpenSession();
    }

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2009
    Messages : 35
    Points : 24
    Points
    24
    Par défaut
    Mais sinon je ne vois pas, et je ne vois pas l’intérêt de FNSettings puisque fluent a déjà une interface simplifiée.
    Pour justement pouvoir créer un singleton sur la construction des la session factory.

    Il y a peut-être un problème de lock... en tout cas ça manque de l'implémentation des singletons que tu as réalisé. Et en web le contexte est forcement multithreadé.
    FNSetting est une classe abstraite et chaque propriétés est static. Alors je veux bien que ça ne ressemble pas au traditionnelle pattern singleton mais ça revient bien au même. L'objet est créé une fois et m'est renvoyé à chaque appel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public static ISessionFactory SessionFactory {
                get {
                    if (_sessionFactory == null){
                        _sessionFactory = FNSettings.CreateSessionFactory();
                    }
                    return _sessionFactory;
                }
            }
    Concernant le bout de code que tu m'as donné, c'est exactement ce que j'ai pour l'instant et ça ne m'arrange pas pour la raison suivante :

    J'ai deux entité : une entité activity et une entité userActivity. L'une et l'autre sont liées. Ceci implique que quand je sauve une activité, je sauve également une userActivity. Ceci doit être fait dans une même transaction, pour cela il faut donc que je récupère la même session et non créer (OpenSession) pour l'insert de l'activity et l'insert du userActivity.

    J'ai vraiment essayé un tas de chose mais rien n'y fait. Est-ce que quelqu'un pourrait m'aider. Le projet est en train de grandir et ce problème commence à être très dérangeant.

    Merci pour votre aide.

  8. #8
    Membre confirmé Avatar de NicoL__
    Homme Profil pro
    Architecte
    Inscrit en
    Janvier 2011
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Janvier 2011
    Messages : 399
    Points : 577
    Points
    577
    Par défaut
    Je me replonge dans ce post, désolé pour mon orthographe, j'ai même eu du mal à me relire.
    En fait je ne vois qu'une chose c'est que votre singleton n'est pas threadsafe il faudrait l'implémenter ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    public static ISessionFactory SessionFactory {
                get {
                    lock(factorylock){
                        if (_sessionFactory == null){
                              _sessionFactory = FNSettings.CreateSessionFactory();
                        }
                   }
                   return _sessionFactory;
                }
            }
    Mais sinon je n'arrive pas à bien identifier sur votre problème.

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2009
    Messages : 35
    Points : 24
    Points
    24
    Par défaut
    C'est quelque chose que j'ai du mal à comprendre moi même. J'aurais du mal à vous expliquez mon problème. Mais ce que je sais expliquer par contre c'est ce que je dois parvenir à faire.

    Avec Nhibernate la formation de ISession n'est pas très coûteuse en ressource mais n'est pas thread-safe.

    Je veux que lorsque quelqu'un appel une méthode du web service, une ISession soit créer au début de l'appel et soit détruite en fin de l'appel. Ceci afin que tous les objets métiers créés par Nhibernate ne perdent pas la session qui les a créer et leurs permettre de se sauver eux même en appelant la session qui a été créé lors de l'appel.

    Vu qu'une session serait créer par appel, je n'aurais pas de souci en ce qui concerne les threads.

    Voilà. Sincèrement je commence à perdre espoir. Existe t'il de la littérature qui me permettrait de mettre tout cela au clair ?

    Merci pour votre suivi

    Edit
    En faite je crois que ce concept s'approche très fort du Unit Of Work. Mais ceci est normalement pris en charge par Nhibernate.

    Help

  10. #10
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Points : 2 202
    Points
    2 202
    Par défaut
    C'est inutile de faire ça, un web service est par essence state less et une session longue sur Nhibernate est une bombe atomique à terme.

    Il suffit d'utiliser une session clean dans un unit of work et d'attacher les entités à la session.

    De toutes les façons le mode que tu cherces à produire est anti-concurrent puisque tu peux avoir potentiellement n sessions gérant l'état de la même entité.
    Ce qui n'est pas en soit souhaitable.

    Depuis le temps que j'utilise Nhibernate pour faire des archis distribué, j'en ai retenu une chose essentielle : si j'en viens à tripatouiller les sessions et à chercher à les maintenir en vie, alors je me suis trompé.

    Good luck -Et sinon Nhibernate burrow te donnera des pistes, mais qui s'en sert ??? (ici

  11. #11
    Membre habitué
    Homme Profil pro
    Architecte C#
    Inscrit en
    Février 2003
    Messages
    78
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte C#

    Informations forums :
    Inscription : Février 2003
    Messages : 78
    Points : 144
    Points
    144
    Par défaut
    Bonjour,
    S'il s'agit d'un service WCF, pourquoi ne pas utiliser la classe WCFOperationSessionContext ?
    (http://nikosbaxevanis.com/2011/03/30...essioncontext/)

    Pour ce qui est de la gestion de la concurrence, je recommande très fortement la lecture du livre "NHibernate in action" qui donne des tas de pistes sur comment gérer l'accès concurrentiel et les problèmes les plus connus.

    Bonne journée

Discussions similaires

  1. Script pour gérer les sessions utilisateurs sous Ubuntu
    Par thucydide dans le forum Shell et commandes GNU
    Réponses: 4
    Dernier message: 08/06/2012, 11h51
  2. Quels modules Perl pour gérer les documents XML ?
    Par djibril dans le forum Modules
    Réponses: 8
    Dernier message: 02/12/2010, 23h54
  3. [Info] Conseils pour gérer les ressources
    Par calogerogigante dans le forum Eclipse Java
    Réponses: 10
    Dernier message: 05/07/2009, 12h49
  4. Réponses: 13
    Dernier message: 07/02/2007, 12h10
  5. Méthode simple pour gérer les collisions
    Par Hyoga dans le forum OpenGL
    Réponses: 2
    Dernier message: 19/02/2005, 13h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo