nHibernate eager loading - странное поведение обновления

мой домен - это аэропорт, который содержит несколько терминалов, и каждый терминал содержит зоны и т. д.
поскольку количество объектов аэропорта / терминала / зоны очень мало, я хотел бы:
1. загрузить всю иерархию с нетерпением при поиске аэропорта.
(с использованием следующей быстрой конфигурации:

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

)
2. Включите кэширование 2-го уровня, чтобы все извлечения объекта аэропорта не попадали в БД.

жадная загрузка и кеширование работают нормально, но следующий тест вызывает странное поведение.
(следующий код дважды извлекает объект аэропорта (второй раз не попадает в БД) и обновляет один из них.)

        [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")));
    }

увидеть результат:

first select
NHibernate: SELECT airport0_.Id as Id36_0_, airport0_.Name as Name36_0_, airport0_.IsDeleted as IsDeleted36_0_ FROM dbo.[Airport] airport0_ WHERE airport0_.Id=@p0;@p0 = 1

NHibernate: SELECT terminals0_.Airport_id as Airport4

//eagerly load terminals
mapping.HasMany(x => x.Terminals).Not.LazyLoad()
            .Cache.ReadWrite();
, terminals0_.Id as Id1_, terminals0_.Id as Id50_0_, terminals0_.Name as Name50_0_, terminals0_.IsDeleted as IsDeleted50_0_, terminals0_.Airport_id as Airport4_50_0_ FROM dbo.[Terminal] terminals0_ WHERE terminals0_.Airport_id=@p0;@p0 = 1

NHibernate: SELECT zones0_.Terminal_id as Terminal4
//eagerly load terminals
mapping.HasMany(x => x.Terminals).Not.LazyLoad()
            .Cache.ReadWrite();
, zones0_.Id as Id1_, zones0_.Id as Id51_0_, zones0_.Name as Name51_0_, zones0_.IsDeleted as IsDeleted51_0_, zones0_.Terminal_id as Terminal4_51_0_ FROM dbo.[Zone] zones0_ WHERE zones0_.Terminal_id=@p0;@p0 = 2


second select
Are those the same airport instance? False

now adding a terminal
NHibernate: select next_hi from dbo._uniqueKey with (updlock, rowlock)
NHibernate: update dbo._uniqueKey set next_hi = @p0 where next_hi = @p1;@p0 = 17, @p1 = 16

NHibernate: INSERT INTO dbo.[Terminal] (Name, IsDeleted, Airport_id, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 'terminal added to second airport', @p1 = False, @p2 = NULL, @p3 = 16
NHibernate: UPDATE dbo.[Airport] SET Name = @p0, IsDeleted = @p1 WHERE Id = @p2;@p0 = 'test airport', @p1 = False, @p2 = 1

NHibernate: UPDATE dbo.[Terminal] SET Name = @p0, IsDeleted = @p1, Airport_id = @p2 WHERE Id = @p3;@p0 = 'test terminal', @p1 = False, @p2 = 1, @p3 = 2

NHibernate: UPDATE dbo.[Zone] SET Name = @p0, IsDeleted = @p1, Terminal_id = @p2 WHERE Id = @p3;@p0 = 'test zone', @p1 = False, @p2 = 2, @p3 = 3

NHibernate: UPDATE dbo.[Terminal] SET Airport_id = @p0 WHERE Id = @p1;@p0 = 1, @p1 = 16



my problems are:
1. the strange update behaviour which updates everything...
2. the fact that firstAirport and secondAirport are not the same object (maybe I'm missing something about 2nd level cache?)

thanks in advance,
Jhonny


person J. Ed    schedule 15.12.2010    source источник


Ответы (1)


Тот факт, что firstAirport и secondAirport не являются одним и тем же объектом, связан с тем, что ссылочные типы по умолчанию сравниваются на равенство ссылок.

Поскольку вы загружаете first и secondAirport с использованием отдельных сеансов NHibernate ISession, кеш не используется, поскольку session1 ничего не знает о session2 и наоборот.
Шаблон «Identity», который реализуется сеансом NHibernate, ограничен к этой сессии вне курса.

Вы можете переопределить это поведение, правильно переопределив методы Equals и GetHashcode. Вы можете переопределить метод Equals, чтобы равенство определялось, например, на основе «Id» аэропорта.

Странное поведение обновления связано с тем, что вы обновляете объект в другом сеансе, а не в сеансе, из которого он был получен. Вы должны увидеть ISEssion как UnitOfWork. Итак, лучше всего загружать объект и сохранять объект в одном сеансе, вместо того, чтобы выполнять эти операции каждую в своем собственном сеансе. (Вы также можете решить эту проблему, «заблокировав» существующий объект аэропорта в сеансе, который вы также используете для выполнения обновления).

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();
            }
        }
person Frederik Gheysels    schedule 15.12.2010
comment
Спасибо! это, кажется, работает. Кроме того, в отношении firstAirport! = SecondAirport моя цель состояла в том, чтобы увидеть, будет ли, когда один сеанс обновляет аэропорт, его сразу же видит другой сеанс (без вызова Get ()). Теперь я понимаю, что это довольно глупое предположение. Очевидно, что если я хочу увидеть изменения, внесенные в объект в другом сеансе, я должен снова загрузить этот объект. - person J. Ed; 15.12.2010
comment
также ознакомьтесь с решением Айенде, используя Merge () - ayende.com/Blog/archive/2009/11/08/ - person J. Ed; 15.12.2010