2010-12-15 3 views
0

mon domaine est un aéroport, qui contient plusieurs terminaux, et chaque terminal contient des zones etc.
étant donné que le nombre d'entités aéroport/terminal/zone est très petit, je voudrais:
1. charger toute la hiérarchie avec impatience lors de la récupération d'un aéroport.
(en utilisant la configuration suivante couramment:
nHibernate chargement impatient- comportement de mise à jour étrange

//eagerly load terminals 
mapping.HasMany(x => x.Terminals).Not.LazyLoad() 
      .Cache.ReadWrite(); 

)
2. permettent la mise en cache de niveau 2, de sorte que tous les récupérations d'un objet de l'aéroport ne serait pas frapper la DB.

le chargement et la mise en cache désirés fonctionnent bien, mais le test suivant produit un comportement étrange.
(le code suivant récupère une entité de l'aéroport deux fois (ne pas frapper la DB la deuxième fois) et met à jour l'un d'entre eux.)

 [TestMethod] 
    public void TestSecondLevelCache() 
    { 
     Airport firstAirport = null, secondAirport = null; 

     Console.WriteLine("first select"); 
     using (ISession session = this.SessionFactory.OpenSession()) 
     { 
      using (ITransaction transaction = session.BeginTransaction()) 
      { 
       //the idea here is to see whether there are two calls to DB here. check the sql output 
       AirportDAO dao = new AirportDAO(session); 
       firstAirport = dao.GetAirport(); 
       transaction.Commit(); 
      } 
     } 

     Console.WriteLine("second select"); 
     using (ISession session = this.SessionFactory.OpenSession()) 
     { 
      using (ITransaction transaction = session.BeginTransaction()) 
      { 
       //the idea here is to see whether there are two calls to DB here. check the sql output 
       AirportDAO dao = new AirportDAO(session); 
       secondAirport = dao.GetAirport(); 
       transaction.Commit(); 
      } 
     } 

     Console.WriteLine("Are those the same airport instance? " + firstAirport.Equals(secondAirport)); 

     Console.WriteLine("now adding a terminal"); 
     using (ISession session = this.SessionFactory.OpenSession()) 
     { 
      using (ITransaction transaction = session.BeginTransaction()) 
      { 
       secondAirport.Terminals.Add(new Terminal() { Name = "terminal added to second airport", Zones = new List<Zone>() }); 
       session.Update(secondAirport); 
       transaction.Commit(); 
      } 
     } 
     //this Assert fails, since firstAirport != secondAirport 
     Assert.IsNotNull(firstAirport.Terminals.FirstOrDefault(t => t.Name.Contains("second airport"))); 
    } 

voir la sortie résultante:

sélectionnez d'abord .
NHibernate: SELECT airport0_.Id comme Id36_0_, airport0_.Name comme Name36_0_, airport0_.IsDeleted comme IsDeleted36_0_ dE dbo [Airport] airport0_ OÙ [email protected]; @ p0 = 1

NHibernate: SE LECT terminals0_.Airport_id comme Airport4_1_, terminals0_.Id comme Id1_, terminals0_.Id comme Id50_0_, terminals0_.Name comme Name50_0_, terminals0_.IsDeleted comme IsDeleted50_0_, terminals0_.Airport_id comme Airport4_50_0_ FROM dbo. [Terminal] terminaux0_ WHERE [email protected] .; @ p0 = 1

NHibernate: SELECT zones0_.Terminal_id comme Terminal4_1_, zones0_.Id comme Id1_, zones0_.Id comme Id51_0_, zones0_.Name comme Name51_0_, zones0_.IsDeleted comme IsDeleted51_0_, zones0_.Terminal_id comme Terminal4_51_0_ à partir de DBO [ zone] zones0_ OÙ [email protected]; @ p0 = 2


deuxième sélectionnez
Sont ceux de la même instance de l'aéroport? Faux

ajoutant maintenant un terminal
NHibernate: sélectionner next_hi de dbo._uniqueKey avec (UPDLOCK, rowlock)
NHibernate: mise à jour dbo._uniqueKey défini next_hi = @ p0 où next_hi = @ p1; p0 = @ 17 @ NHibernate: INSERT INTO dbo. [Terminal] (Nom, IsDeleted, Airport_id, Id) VALEURS (@ p0, @ p1, @ p2, @ p3), @ p0 = 'terminal ajouté au deuxième aéroport', p1 = 16

@ p1 = Faux, @ p2 = NULL, @ p3 = 16
NHibernate: UPDATE dbo. [Aéroport] SET Nom = @ p0, IsDeleted = @ p1 OERE ID = @ p2; @ p0 = 'aéroport d'essai', @ p1 = Faux, @ p2 = 1

NHibernate: UPDATE dbo. [T erminal] SET Nom = @ p0, IsDeleted = @ p1, Airport_id = @ p2 O WH Id = @ p3; @ p0 = 'terminal de test', @ p1 = Faux, @ p2 = 1, @ p3 = 2


NHibernate: UPDATE dbo. [Zone] SET Nom = @ p0, IsDeleted = @ p1, ID_terminal = @ p2 O Id Id = @ p3; @ p0 = 'zone de test', @ p1 = Faux, @ p2 = 2, @ p3 = 3

NHibernate: MISE À JOUR dbo.[Terminal] SET Airport_id = @ = p0 où id @ p1; @ p0 = 1, @ p1 = 16



mes problèmes sont les suivants: 1.
le comportement de mise à jour étrange qui met à jour tout .. .
2. le fait que firstAirport et secondAirport ne sont pas le même objet (peut-être que je manque quelque chose cache de niveau 2?)

merci à l'avance,
Jhonny

Répondre

1

Le fait que firstAirport et secondAirport ne soient pas le même objet est dû au fait que les types de référence sont comparés par défaut pour l'égalité de référence. Comme vous chargez le premier et secondAirport à l'aide de NHibernate ISession séparés, le cache n'est pas utilisé, puisque session1 ne sait rien sur session2 et vice versa.
Le modèle 'Identity' implémenté par la session de NHibernate est étendu à cette session.

Vous pouvez remplacer ce comportement en remplaçant les méthodes Equals et GetHashcode correctement. Vous pouvez remplacer la méthode Equals, de sorte que l'égalité soit déterminée en fonction de l'ID d'un aéroport, par exemple.

Le comportement de mise à jour étrange est dû au fait que vous mettez à jour l'objet dans une autre session, puis la session à partir de laquelle il a été récupéré. Vous devriez voir l'ISEssion comme UnitOfWork. Il est donc préférable de charger l'objet et de sauvegarder l'objet dans la même session, au lieu de faire ces opérations chacun dans sa propre session. (Vous pouvez également résoudre ce problème en "verrouillant" l'objet aéroport existant dans la session que vous utilisez pour effectuer la mise à jour).

using (ISession session = this.SessionFactory.OpenSession()) 
     { 
      using (ITransaction transaction = session.BeginTransaction()) 
      { 

       session.Lock (secondAirport, LockMode.None); 

       secondAirport.Terminals.Add(new Terminal() { Name = "terminal added to second airport", Zones = new List<Zone>() }); 
       session.Update(secondAirport); 
       transaction.Commit(); 
      } 
     } 
+0

Merci! cela semble fonctionner. Aussi, à propos de firstAirport! = SecondAirport, mon but était de voir si lorsqu'une session met à jour un aéroport, l'autre session le voit immédiatement (sans appeler Get()). Je vois maintenant que c'est une supposition tout à fait ridicule. Évidemment, si je veux voir les changements faits à un objet par une session différente, je dois charger cet objet encore. –

+0

également, consultez la solution d'Ayende, en utilisant Merge() - http://ayende.com/Blog/archive/2009/11/08/nhibernate-ndash-cross-session-operations.aspx –

Questions connexes