2010-06-02 2 views
2

Я ввел отображение для бизнеса-объекта, который имеет (среди прочих) свойства под названием «Имя»:NHibernate непреднамеренного ленивого свойство загрузка

public class Foo : BusinessObjectBase 
{ 
    ... 
    public virtual string Name { get; set; } 
} 

По какой-то причине, когда я принести «Foo» объекты, NHibernate кажется, применить ленивым свойство загрузки (для простых свойств, а не ассоциации):

следующий кусок кода генерирует п + 1 операторы SQL, о котором первый только распаковывает идентификаторы, а остальные п Позовите Имя для каждой записи:

ISession session = ...IQuery query = session.CreateQuery(queryString); 
ITransaction tx = session.BeginTransaction(); 

List<Foo> result = new List<Foo>(); 
foreach (Foo foo in query.Enumerable()) 
{ 
    result.Add(foo); 
} 

tx.Commit(); 
session.Close(); 

производит:

select foo0_.FOO_ID as col_0_0_ from V1_FOO foo0_ 
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 81 
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 36470 
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_ 
    WHERE foo0_.FOO_ID=:p0;:p0 = 36473 

Аналогично, следующий код приводит к LazyLoadingException после закрытия сеанса:

ISession session = ... 
ITransaction tx = session.BeginTransaction(); 
Foo result = session.Load<Foo>(id); 
tx.Commit(); 
session.Close(); 

Console.WriteLine(result.Name); 

После this post, «ленивых свойствами. .. редко является важной функцией для включения ... (и) в Hibernate 3, по умолчанию отключена ».

Так что я делаю неправильно? Мне удалось обойти LazyLoadingException, сделав NHibernateUtil.Initialize(foo), но еще хуже - это n + 1 sql-заявления, которые приносят мое приложение на колени.

Это, как отображение выглядит следующим образом:

<class name="Foo" table="V1_FOO"> 
    ... 
    <property name="Name" column="NAME"/> 
</class> 

КСТАТИ: Абстрактный базовый «BusinessObjectBase» класс инкапсулирует свойство ID, который служит в качестве внутреннего идентификатора.

+0

какая версия NHibernate вы используете? – Jaguar

+0

2.1.2.4000; hmm thats <3. Угадайте, что ленивые свойства еще не поддерживаются в этой версии? В любом случае, как сказал Стефан, проблема связана не с ленивой загрузкой имущества. – chiccodoro

+0

обновил мой ответ с помощью образцов кода и дальнейших предложений. –

ответ

6

Я не думаю, что это связано с ленивой загрузкой недвижимости. Это скорее связано с использованием Enumerable и Load.

Посмотрите на reference documentation о Enumerable:

... Итератор будет загружать объекты на спроса, используя идентификаторы возвращенного посредством первоначального запроса SQL (п + 1 выбирает всего).

Либо использовать пакет выборки, чтобы уменьшить количество запросов (в отображении класса)

<class name="Foo" table="V1_FOO" batch-size="20"> 

... или использовать List вместо Enumerable:

IQuery query = session.CreateQuery(queryString); 
List<Foo> result query.List<Foo>(); 

Примечание: Enumerable имеет смысл только в том случае, если вы не ожидаете, что вам нужен весь результат, или в особых случаях, когда вы не хотите иметь их все в памяти одновременно (тогда вам нужно Evict, чтобы удалить их). В большинстве случаев List - это то, что вам нужно.


В случае Load, только прокси создается (без запроса не выполняется). При первом доступе к нему он загружается. (Это очень мощный, например, чтобы использовать этот прокси-сервер в качестве аргументов фильтров в запросах, или связать его с другим объектом, без необходимости загрузки его содержимого.) Если вам нужно его содержимое, используйте Get вместо

using (ISession session = ...) 
using (ITransaction tx = session.BeginTransaction()) 
{ 
    Foo result = session.Get<Foo>(id); 

    tx.Commit(); 
} 
// could still fail in case of lazy loaded references 
Console.WriteLine(result.Name); 

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

using (ISession session = ...) 
using (ITransaction tx = session.BeginTransaction()) 
{ 
    Foo result = session.Load<Foo>(id); 
    // should always work fine 
    Console.WriteLine(result.Name); 

    tx.Commit(); 
} 
+0

Очень точный и полезный ответ, спасибо. Следуя вашей связанной справочной документации, решение первой проблемы будет: IList result = query.List (), или если мне нужен список, список result = query.List () .ToList (); - Если это так, вы можете включить этот код в свой ответ для ссылки будущих читателей? – chiccodoro

+0

Я теперь реализовал .List <>(), и он работает намного лучше, чем раньше! (Конечно, поскольку связь с базой данных равна O (1) вместо O (n) сейчас) – chiccodoro

Смежные вопросы