Précédent   Forum du club des développeurs et IT Pro > Dotnet > Accès aux données > NHibernate
NHibernate Forum d'entraide sur l'utilisation du mappeur objet/relationnel NHibernate.
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 24/06/2011, 11h33   #1
diplomegalo
Candidat au titre de Membre du Club
 
Inscription : 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
diplomegalo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 04/07/2011, 16h12   #2
NicoL__
Membre expérimenté
 
Avatar de NicoL__
 
Homme Nicolas
Inscription : janvier 2011
Messages : 390
Détails du profil
Informations personnelles :
Nom : Homme Nicolas
Localisation : France

Informations forums :
Inscription : janvier 2011
Messages : 390
Points : 559
Points : 559
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.
NicoL__ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/07/2011, 16h35   #3
diplomegalo
Candidat au titre de Membre du Club
 
Inscription : juin 2009
Messages : 35
Détails du profil
Informations forums :
Inscription : juin 2009
Messages : 35
Points : 14
Points : 14
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
diplomegalo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/07/2011, 17h13   #4
NicoL__
Membre expérimenté
 
Avatar de NicoL__
 
Homme Nicolas
Inscription : janvier 2011
Messages : 390
Détails du profil
Informations personnelles :
Nom : Homme Nicolas
Localisation : France

Informations forums :
Inscription : janvier 2011
Messages : 390
Points : 559
Points : 559
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()
NicoL__ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/07/2011, 08h54   #5
diplomegalo
Candidat au titre de Membre du Club
 
Inscription : juin 2009
Messages : 35
Détails du profil
Informations forums :
Inscription : juin 2009
Messages : 35
Points : 14
Points : 14
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é
diplomegalo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/07/2011, 12h16   #6
NicoL__
Membre expérimenté
 
Avatar de NicoL__
 
Homme Nicolas
Inscription : janvier 2011
Messages : 390
Détails du profil
Informations personnelles :
Nom : Homme Nicolas
Localisation : France

Informations forums :
Inscription : janvier 2011
Messages : 390
Points : 559
Points : 559
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();
}
NicoL__ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/01/2012, 13h51   #7
diplomegalo
Candidat au titre de Membre du Club
 
Inscription : juin 2009
Messages : 35
Détails du profil
Informations forums :
Inscription : juin 2009
Messages : 35
Points : 14
Points : 14
Citation:
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.

Citation:
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.
diplomegalo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 23/01/2012, 11h50   #8
NicoL__
Membre expérimenté
 
Avatar de NicoL__
 
Homme Nicolas
Inscription : janvier 2011
Messages : 390
Détails du profil
Informations personnelles :
Nom : Homme Nicolas
Localisation : France

Informations forums :
Inscription : janvier 2011
Messages : 390
Points : 559
Points : 559
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.
NicoL__ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/02/2012, 11h57   #9
diplomegalo
Candidat au titre de Membre du Club
 
Inscription : juin 2009
Messages : 35
Détails du profil
Informations forums :
Inscription : juin 2009
Messages : 35
Points : 14
Points : 14
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
diplomegalo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/06/2012, 23h29   #10
B.AF
Membre Expert
 
Inscription : février 2005
Messages : 1 238
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 238
Points : 1 655
Points : 1 655
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
B.AF est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/07/2012, 13h28   #11
Vimaire
Membre habitué
 
Homme Alexandre Trigueros
Architecte C#
Inscription : février 2003
Messages : 74
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 : 74
Points : 133
Points : 133
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
Vimaire est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 02h51.


 
 
 
 
Partenaires

Hébergement Web