Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / NHibernate - loading trees / 19 сообщений из 19, страница 1 из 1
03.02.2011, 11:02
    #37094772
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Всем доброго времени суток. Вопрос вот такой вот. Решил оптимизировать работу с деревом категорий. С одной стороны, хотелось бы для отдельных случаев оставить возможность работать с деревом через ленивую загрузку. Но в ситуациях с редактированием деревьев нужно загружать их полностью. Самый простой вариант, который пришел, выключить ленивую загрузку подкатегорий и установить batch="очень много", тогда запросов на БД (при загрузке деревьев категорий) идет очень мало. Но при этом возможность работать лениво (даже при установке в запросах _session.QueryOver<Category>().Where(category => category.Id == id).Fetch(x => x.ChildCategories).Lazy ) пропадает... Таким образом, подумал, LazyLoad отключать нельзя, но нужно попробовать загрузить все категории одним запросом, прописанным на TSQL вручную. Нашел статью , в которой говорится, что есть такая возможность в NHibernate. Обрадовался, реализовал:

Код: plaintext
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.
public class Category : DomainObject<long>
{
    public Category()
    {
        ChildCategories = new List<Category>();
    }

    public virtual string Name { get; set; }

    public virtual Category Parent { get; set; }
    public virtual IList<Category> ChildCategories { get; private set; }

    public virtual void AddChild(Category category)
    {
        if (category.Parent !=null && category.Parent != this)
        {
            category.Parent.RemoveChild(category);
        }

        ChildCategories.Add(category);
        category.Parent = this;
    }

    public virtual void RemoveChild(Category category)
    {
        if (ChildCategories.Contains(category))
        {
            ChildCategories.Remove(category);
            category.Parent = null;
        }
    }
}

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateLab.Domain" namespace="NHibernateLab.Domain" default-lazy="true">

  <class name="Category" table="Category">
   
    <id name="Id" column="CategoryID" type="long">
      <generator class="native"/>
    </id>
    
    <property name="Name" column="Name" type="string"/>

    <many-to-one name="Parent" class="Category" column="ParentId"/>
    
    <bag name="ChildCategories" cascade="all" inverse="true">
      <key column="ParentId"/>
      <one-to-many class="Category"/>  
    </bag>
    
  </class>
  
</hibernate-mapping>

Код: plaintext
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.
public Category GetFullTree(long Id)
{
    string query = @"WITH Hierachy(CategoryId, Name, Level)
                        AS
                        (
	                        SELECT CategoryID, Name, 0 AS Level
	                        FROM Category c
	                        WHERE c.CategoryID = :id
	
	                        UNION ALL
	
	                        SELECT cat.CategoryId, cat.Name, hier.Level + 1
	                        FROM Category cat
	                        INNER JOIN Hierachy hier
	                        ON cat.ParentId = hier.CategoryId
                        )
                        SELECT CategoryId, Name
                        FROM Hierachy
                        WHERE Level >= 0";


    List<Category> list = _session.CreateSQLQuery(query)
                    .AddEntity(typeof (Category))
                    .SetInt64("id", Id)
                    //.SetCacheable(true)
                    .List<Category>().ToList();

    Category result = list.Where(cat => cat.Parent == null).First();

    return result;
}

Метод GetFullTree реально инициализирует громадную коллекцию объектов, и возвращает root клиентскому коду. Но при первом обращении клиентского кода к любому rootCategory.ChildCategories[x], на базу данных уходит запрос на получение этого Child'а. Это, видимо, потому, что NHibernate-то создал кучу объектов при TSQL-запросе, но не связал их, и обернул проксиками. Таким образом проблема: lazy отключать нельзя, потому, что из кода его включить не удается, а загружать через custom-tsql нельзя, так как объекты не связываются друг с другом, оборачиваются в прокси, и при первом обращении к подобъектам будут сгенерированы запросы на базу данных. Игрался уже с second-level cache

Код: plaintext
1.
2.
3.
4.
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateLab.Domain" namespace="NHibernateLab.Domain" default-lazy="true">

  <class name="Category" table="Category">
    <cache usage="read-write"/>  
.......

Код: plaintext
1.
2.
3.
4.
5.
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
  <session-factory name="NHibernate.Test">
.........
    <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
    <property name="cache.use_second_level_cache">true</property>
.........

...не помогло, так как, по-видимому, он кеширует именно запросы, а TSQL-запрос сильно отличается от тех, что генерит NHibernate. Подскажите, как тут быть? Спасибо :)
...
Рейтинг: 0 / 0
03.02.2011, 16:27
    #37095832
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Если я не понятно сформулировал вопрос, скажите пожалуйста, я переформулирую. Спасибо.
...
Рейтинг: 0 / 0
04.02.2011, 08:46
    #37096885
ViPRos
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

да нахрен эти нибернейты нужны? у них ж ума нету
...
Рейтинг: 0 / 0
04.02.2011, 08:49
    #37096888
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
ViPRosда нахрен эти нибернейты нужны? у них ж ума нету
Если к NHibernate с умом подойти, то очень нужны. А по теме есть какие-нибудь идеи?
...
Рейтинг: 0 / 0
04.02.2011, 08:57
    #37096900
ViPRos
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

идея одна - простая
в БД нафиг не нужны деревя, потому что Родитель и Потомок разного типа (попроще, из разных таблиц), так что имеем n типов и их агрегатов (юнион) и скоко надо ограничений - Может состоять(агрегатный типI, агрегатный тип J,....) одноуревновые и отношений Состоит(элементагрегатный типI, элементагрегатный тип J,....)
мозгов у хибера не хватит выбрать их по уму
...
Рейтинг: 0 / 0
04.02.2011, 09:17
    #37096940
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
ViPRosPregamil,

идея одна - простая
в БД нафиг не нужны деревя, потому что Родитель и Потомок разного типа (попроще, из разных таблиц), так что имеем n типов и их агрегатов (юнион) и скоко надо ограничений - Может состоять(агрегатный типI, агрегатный тип J,....) одноуревновые и отношений Состоит(элементагрегатный типI, элементагрегатный тип J,....)
мозгов у хибера не хватит выбрать их по уму
Ну, во-первых, в моём случае родитель и потомок одного типа (попроще, в одной таблице живут). Во-вторых, если имеем n типов, то не факт, что агрегатов будет тоже n, по крайней мере не всегда так. В NHibernate для выборки таких связанных коллекций можно указать Fetch(x=>x.Collection).Eager, тогда он выберет подколлекцию сразу же, через left outer join. С выборками из разных таблиц, в общем-то, более-менее понятно, но здесь имеем дело с одной таблицей и рекурсивной выборкой всего дерева. Fetch(x=>x.Collection).Eager выбирает только один подуровень (коллекцию Children первого уровня). Но нужно всё дерево. Можно отключить ленивую загрузку и установить batchsize="много", тогда он сделает пару-тройку запросов на базу, и выберет всё дерево, но как тогда включить lazyLoading для других запросов? Из кода включить не удалось. Далее, попалась под руку статья, где указывается, что можно самому прописать рекурсивный TSQL-запрос, и подсунуть хиберу в session.CreateSQLQuery. Прописал, подсунул, получил несколько тысяч категорий в коллекции. Но друг с другом они не связаны, и в каждой категории Children'ы обёрнуты в проксики, поэтому при первом обращении к любой коллекции сразу же следует вызов на БД. А россыпью мне эти несколько тыщ категорий не нужны. Какие идеи? (кроме реализации на чистом ado.net)
...
Рейтинг: 0 / 0
04.02.2011, 10:04
    #37097045
ViPRos
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

во первых :) я не сказал что будет n агрегатов, скорееих будет >= n так как типы сами выступают в роли агрегата тоже
во вторых, неужто этот batchsize действует на всю сессиию, скорее есть возможность указать это для отдельного графа (подсхемы)
вощем это все теория, счас пацаны проснуться и скажут
...
Рейтинг: 0 / 0
04.02.2011, 11:20
    #37097323
SolYUtor
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
ViPRosсчас пацаны проснуться и скажут
Проснулся, вот ответ .
...
Рейтинг: 0 / 0
04.02.2011, 15:32
    #37098156
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
SolYUtorViPRosсчас пацаны проснуться и скажут
Проснулся, вот ответ .

Здорово, работает :) Спасибо!

Опробовал этот вариант, вот код:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
public void GetCategory(long id)
{
    Category category = _session.CreateQuery(
        "select cat from Category cat join fetch cat.ChildCategories")
        .SetResultTransformer(new DistinctRootEntityResultTransformer())
        .List<Category>()
        .Where(x => x.Id == id)
        .First();
}

И вот SQL, который пошел на базу данных при выполнении этого метода:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
select category0_.CategoryID  as CategoryID1_0_,
       category1_.CategoryID  as CategoryID1_1_,
       category0_.Name        as Name1_0_,
       category0_.ParentId    as ParentId1_0_,
       category1_.Name        as Name1_1_,
       category1_.ParentId    as ParentId1_1_
from   Category category0_
       inner join Category category1_
         on category0_.ParentId = category1_.CategoryID

В результате получил проинициализированное дерево. Только есть одно но: вне сессии его использовать нельзя, так как Children-коллекции листьев этого дерева (которые, по сути, должны быть пустыми коллекциями), NHibernate оформил в виде своих проксиков, которые, опять таки, ломятся в базу.
Можно-ли каким-то образом заставить его генерировать пустые коллекции, которые при первом доступе к ним не будут ломиться в базу? Спасибо.
...
Рейтинг: 0 / 0
07.02.2011, 12:03
    #37101848
SolYUtor
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

Вы немного не то делаете. Надо грузить в список все категории, а уже корневые выбирать из этого загруженного списка.
Код: plaintext
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.
        [Test]
        public void LoadFullTree()
        {
            var parent = new Category {Caption = "Parent"};
            var child1 = new Category {Caption = "Child1"};
            var child2 = new Category {Caption = "Child2"};
            var subchild = new Category {Caption = "SubChild"};
            
            parent.AddChild(child1);
            parent.AddChild(child2);
            child2.AddChild(subchild);

            CurrentSession.PersistFlushClear(parent);

            var tree = CurrentSession
                .CreateQuery("select c from Category c join fetch c.Children")
                .SetResultTransformer(new DistinctRootEntityResultTransformer())
                .List<Category>();


            foreach (var category in tree.Where(category => category.HasChildren))
            {
                foreach (var child in category.Children)
                {
                    Trace.TraceInformation(child.Caption);
                }
            }

В результатет ни одного дополнительного запроса в базу:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
NHibernate: 
    select
        category0_.Id as Id9_0_,
        children1_.Id as Id9_1_,
        category0_.Caption as Caption9_0_,
        category0_.ParentId as ParentId9_0_,
        children1_.Caption as Caption9_1_,
        children1_.ParentId as ParentId9_1_,
        children1_.ParentId as ParentId0__,
        children1_.Id as Id0__ 
    from
        dbo_Category category0_ 
    inner join
        dbo_Category children1_ 
            on category0_.Id=children1_.ParentId

Information: 0 : Child1
Information: 0 : Child2
Information: 0 : SubChild
...
Рейтинг: 0 / 0
08.02.2011, 16:06
    #37104604
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
SolYUtorВы немного не то делаете. Надо грузить в список все категории, а уже корневые выбирать из этого загруженного списка.
Спасибо, проверил. И что самое интересное: если идти по алгоритму:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
Category tree;

using (ISession session = CreateSession())
{
    int Id = CreateAndPersistCategoryTree();
    tree = LoadCategoryTree(Id);
}

WriteTreeRecursive(tree);

т.е. в одной сессии и создать, и прочитать категории, то вне сессии (после её закрытия), всё отрабатывает по-человечески, без проксиков и поползновений в базу.

Но если сохранить дерево в одной сессии, а прочитать в совершенно другой, то NHibernate тут же обернет чайлдов категорий-листьев дерева в проксики, и попытается полезть в базу.

Написал тестовый пример. SQL базы:

Код: plaintext
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.
USE [master]
GO

IF EXISTS(SELECT name FROM sys.databases
     WHERE name = 'NHCategories')
     BEGIN
		 DROP DATABASE NHCategories
	 END
GO

CREATE DATABASE NHCategories
GO

USE [NHCategories]
GO
/****** Object:  Table [dbo].[Categories]    Script Date: 02/08/2011 14:25:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Categories](
	[Id] [int] IDENTITY( 1 , 1 ) NOT NULL,
	[Name] [nvarchar](max) NOT NULL,
	[ParentId] [int] NULL,
 CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [master]
GO

Вывод на консоль в рамках одной сессииNHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SC
OPE_IDENTITY();@p0 = 'Parent' [Type: String (4000)], @p1 = NULL [Type: Int32 (0)
]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child1' [Type: String (4000)], @p1 = 33 [Type: Int32 (0)]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child2' [Type: String (4000)], @p1 = 33 [Type: Int32 (0)]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'SubChild' [Type: String (4000)], @p1 = 35 [Type: Int32 (0)
]
NHibernate: select category0_.Id as Id0_0_, children1_.Id as Id0_1_, category0_.Name as Name0_0_, category0_.ParentId as ParentId0_0_, children1_.Name as Name0_1_, children1_.ParentId as ParentId0_1_, children1_.ParentId as ParentId0__, children1_.Id as Id0__ from Categories category0_ inner join Categories children1_on category0_.Id=children1_.ParentId
----
33 - Parent
34 - Child1
35 - Child2
36 - SubChild
Вывод на консоль в рамках разных сессийNHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Parent' [Type: String (4000)], @p1 = NULL [Type: Int32 (0)]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child1' [Type: String (4000)], @p1 = 25 [Type: Int32 (0)]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child2' [Type: String (4000)], @p1 = 25 [Type: Int32 (0)]
NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'SubChild' [Type: String (4000)], @p1 = 27 [Type: Int32 (0)]
NHibernate: select category0_.Id as Id0_0_, children1_.Id as Id0_1_, category0_.Name as Name0_0_, category0_.ParentId as ParentId0_0_, children1_.Name as Name0_1_, children1_.ParentId as ParentId0_1_, children1_.ParentId as ParentId0__, chi
ldren1_.Id as Id0__ from Categories category0_ inner join Categories children1_on category0_.Id=children1_.ParentId
-----
25 - Parent
26 - Child1
NHibernate: SELECT children0_.ParentId as ParentId1_, children0_.Id as Id1_, children0_.Id as Id0_0_, children0_.Name as Name0_0_, children0_.ParentId as ParentId0_0_ FROM Categories children0_ WHERE children0_.ParentId=@p0;@p0 = 26 [Type:Int32 (0)]
27 - Child2
28 - SubChild
NHibernate: SELECT children0_.ParentId as ParentId1_, children0_.Id as Id1_, children0_.Id as Id0_0_, children0_.Name as Name0_0_, children0_.ParentId as ParentId0_0_ FROM Categories children0_ WHERE children0_.ParentId=@p0;@p0 = 28 [Type:Int32 (0)]

Контрольный пример приложил в аттаче. Параметр метода SessionManager.OpenSession(true/false) меняет видимость сессии (возвращает новую сессию, или старую, уже использованую ранее).

Заранее благодарен, если есть идет, почему так происходит и как с этим работать.
...
Рейтинг: 0 / 0
08.02.2011, 17:16
    #37104795
SolYUtor
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

NHibernate всегда старается грузить как можно меньше данных из бд. Рецепт стар как мир - ленивая загрузка. Поэтому ему и нужны прокси - для автоматической ленивой загрузки, без дополнительных телодвижений. Сессия всегда возвращает прокси-объекты (если включена ленивая загрузка). Даже когда вы грузите всё дерево целиком с DistinctRootEntityResultTransformer, всё равно возвращаются прокси, только уже инициализированные. Прокси-объект всегда связан с той сессией, в которой он открыт, потому после закрытия сессии становится почти безполезен.
Использование сессии для одной операции, и последующие ее закрытие (как в вашем примере) - это антишаблон. Если вы работаете с деревом - то держите сессию открытой, и уже по окончании работы с деревом сбрасывайте изменения в БД ( Session.Flush() ).
...
Рейтинг: 0 / 0
08.02.2011, 17:35
    #37104860
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
SolYUtorЕсли вы работаете с деревом - то держите сессию открытой, и уже по окончании работы с деревом сбрасывайте изменения в БД ( Session.Flush() ).
Изменять категории мне не нужно, нужно только читать. Как в принципе работать с сессиями и NH мне понятно. Задача, в общем-то, немного другая: в приложении выгрузить все категории и отобразить на странице. Выгрузить одним запросом. Т.е. в базе уже есть категории, и если выгрузить их одним запросом и начать рекурсивно конвертировать автомаппером на [Category -> CategoryViewModel], то автомаппер будет обходить всё дерево. И когда дойдет до листьев, NH будет делать запросы на БД из-за проксиков. Чтобы всё работало и не падало, надо держать сессию открытой, тогда при доступе автомаппера к самым конечным подкатегориям он отправит запрос на базу. В итоге, при выборе всех категорий NH шлет один запрос на базу, и при конвертировании категории в CategoryViewModel будет еще несколько запросов на базу (в примере выше - +2 запроса, из-за Lazy Loading-а). Категорий много, поэтому мелких запросов на сервер больше тысячи, что убивает производительность. Да и попытка выкачать все категории одним махом ради одного запроса на базу и была. Если выключить LazyLoading, то не получится работать с категориями лениво. Вот и вопрос: каким именно образом выгрузить категории из базы и сконвертировать их в иерархию других классов (CategoryViewModel), чтобы при этом пошел ровно один запрос на базу данных, а не 1001 (при конвертировании)? Согласен и сессию держать открытой, и что угодно. Но как?
...
Рейтинг: 0 / 0
08.02.2011, 17:50
    #37104903
SolYUtor
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

речь идёт об ASP.NET приложении?
...
Рейтинг: 0 / 0
08.02.2011, 17:57
    #37104917
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
да, об ASP.NET MVC.
...
Рейтинг: 0 / 0
09.02.2011, 10:34
    #37105991
SolYUtor
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Pregamil,

решение ваших проблем есть тут . Гугл вам поможет :)
...
Рейтинг: 0 / 0
09.02.2011, 12:08
    #37106303
KirillMedvedev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Практически всё правильно, за исключением одной маленькой ошибки:

session.CreateQuery("from Category cat left join fetch cat.Children")

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public static Category LoadCategory(long Id)
{
    using (ISession session = SessionManager.OpenSession(true))
    {
        Category category = session.CreateQuery("from Category cat left join fetch cat.Children")
                                    .List<Category>()
                                    .Where(cat=>cat.Id == Id)
                                    .First();

        return category;
    }
}

Таким образом при закрытой сессии можно работать со всем деревом, в базу больше лезть не будет.
...
Рейтинг: 0 / 0
09.02.2011, 13:13
    #37106505
KirillMedvedev
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
А вот, по сути, и разница:

Первый вариант:
from Category cat inner join fetch cat.Children

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select category0_.Id       as Id0_0_,
       children1_.Id       as Id0_1_,
       category0_.Name     as Name0_0_,
       category0_.ParentId as ParentId0_0_,
       children1_.Name     as Name0_1_,
       children1_.ParentId as ParentId0_1_,
       children1_.ParentId as ParentId0__,
       children1_.Id       as Id0__
from   Categories category0_
       inner join Categories children1_
         on category0_.Id = children1_.ParentId

SQL output:
Id0_0Id0_1Name0_0ParentId0_0Name0_1ParentId0_0ParentId_0Id0_12ParentNULLChild111213ParentNULLChild211334Child21SubChild334


Второй вариант:
from Category cat left join fetch cat.Children
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select category0_.Id       as Id0_0_,
       children1_.Id       as Id0_1_,
       category0_.Name     as Name0_0_,
       category0_.ParentId as ParentId0_0_,
       children1_.Name     as Name0_1_,
       children1_.ParentId as ParentId0_1_,
       children1_.ParentId as ParentId0__,
       children1_.Id       as Id0__
from   Categories category0_
       left outer join Categories children1_
         on category0_.Id = children1_.ParentId

SQL output:
Id0_0Id0_1Name0_0ParentId0_0Name0_1ParentId0_0ParentId_0Id0_12ParentNULLChild111213ParentNULLChild21132NULLChild11NULLNULLNULLNULL34Child21SubChild3344NULLSubChild3NULLNULLNULLNULL

Поэтому при inner-join при доступе к самым вложенным категориям NHibernate лезет в базу, так как у него нет информации о Child-ах: есть они или нет. При left outer join лезть в базу смысла нет, так как он теперь явно понимает, что коллекция пуста.
...
Рейтинг: 0 / 0
09.02.2011, 14:31
    #37106819
Pregamil
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
NHibernate - loading trees
Thanx, всё прояснилось :)
...
Рейтинг: 0 / 0
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / NHibernate - loading trees / 19 сообщений из 19, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]