Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 11 sur 11
  1. #1
    Candidat au titre de Membre du Club
    Inscrit en
    juin 2009
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : juin 2009
    Messages : 35
    Points : 14
    Points
    14

    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 :
    1
    2
    3
    4
     
    <httpModules>
    	<add name="NHibernateHttpModule" type="Data.Connection.HttpModule.NHibernateHttpModule"/>
    </httpModules>
    ** Service Librairy **

    Appel à une methode du contract

    Code :
    1
    2
    3
    4
     
    public IList<ContactData> GetContactInfo(string param){
    (...)
    ContactRepository.GetContacts(param);
    Le repository est créé de cette manière

    Code :
    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 :
    ICriteria criteria = Session.CreateCriteria<Contact>();
    C'est ici que ça ne se passe pas bien. _session me renvoi null

    Code :
    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 :
    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 :
    1
    2
    3
    4
     
    public static ISession CreateSession() {
                return SessionFactory.OpenSession();
            }
    SessionFactory

    Code :
    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 :
    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 expérimenté Avatar de NicoL__
    Homme Profil pro Nicolas
    Inscrit en
    janvier 2011
    Messages
    398
    Détails du profil
    Informations personnelles :
    Nom : Homme Nicolas
    Localisation : France

    Informations forums :
    Inscription : janvier 2011
    Messages : 398
    Points : 571
    Points
    571

    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 :
    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
    Candidat au titre de Membre du Club
    Inscrit en
    juin 2009
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : juin 2009
    Messages : 35
    Points : 14
    Points
    14

    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 expérimenté Avatar de NicoL__
    Homme Profil pro Nicolas
    Inscrit en
    janvier 2011
    Messages
    398
    Détails du profil
    Informations personnelles :
    Nom : Homme Nicolas
    Localisation : France

    Informations forums :
    Inscription : janvier 2011
    Messages : 398
    Points : 571
    Points
    571

    Par défaut

    Vous voulez tour simplement dire que l'appel :
    Code :
    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 :
    1
    2
     
    _session = FNSettings.CreateSession()
    Mais à mon sens ça devrait ressemble à ça
    Code :
    1
    2
     
    _session = FNSettings.SessionFactory.OpenSession()

  5. #5
    Candidat au titre de Membre du Club
    Inscrit en
    juin 2009
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : juin 2009
    Messages : 35
    Points : 14
    Points
    14

    Par défaut

    Bonjour

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

    Exemple :

    Code :
    1
    2
    3
    public static ISession CreateSession() {
                return SessionFactory.OpenSession();
            }
    Code :
    1
    2
    3
      public static ISession GetCurrentSession() {
                return SessionFactory.GetCurrentSession();
            }
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    public static ISessionFactory SessionFactory {
                get {
                    if (_sessionFactory == null){
                        _sessionFactory = FNSettings.CreateSessionFactory();
                    }
                    return _sessionFactory;
                }
            }
    Code :
    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 :
    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 expérimenté Avatar de NicoL__
    Homme Profil pro Nicolas
    Inscrit en
    janvier 2011
    Messages
    398
    Détails du profil
    Informations personnelles :
    Nom : Homme Nicolas
    Localisation : France

    Informations forums :
    Inscription : janvier 2011
    Messages : 398
    Points : 571
    Points
    571

    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 :
    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
    Candidat au titre de Membre du Club
    Inscrit en
    juin 2009
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : juin 2009
    Messages : 35
    Points : 14
    Points
    14

    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 :
    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 expérimenté Avatar de NicoL__
    Homme Profil pro Nicolas
    Inscrit en
    janvier 2011
    Messages
    398
    Détails du profil
    Informations personnelles :
    Nom : Homme Nicolas
    Localisation : France

    Informations forums :
    Inscription : janvier 2011
    Messages : 398
    Points : 571
    Points
    571

    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 :
    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
    Candidat au titre de Membre du Club
    Inscrit en
    juin 2009
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : juin 2009
    Messages : 35
    Points : 14
    Points
    14

    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 Expert
    Inscrit en
    février 2005
    Messages
    1 243
    Détails du profil
    Informations forums :
    Inscription : février 2005
    Messages : 1 243
    Points : 1 688
    Points
    1 688

    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 Alexandre Trigueros
    Architecte C#
    Inscrit en
    février 2003
    Messages
    77
    Détails du profil
    Informations personnelles :
    Nom : Homme Alexandre Trigueros
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte C#

    Informations forums :
    Inscription : février 2003
    Messages : 77
    Points : 140
    Points
    140

    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

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •