2010-06-02 4 views
2

J'introduit un mappage pour un objet métier qui a (entre autres) une propriété appelée « Nom »:NHibernate chargement non intentionnelle de la propriété paresseux

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

Pour une raison quelconque, quand je vais chercher des objets « Foo », NHibernate semble appliquer le chargement de la propriété paresseux (pour les propriétés simples et non des associations):

le morceau de code suivant génère n + 1 instructions SQL, dont le premier ne récupèrera que les ids, et le reste n chercher la Nom pour chaque enregistrement:

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(); 

produit:

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 

De même, le code suivant conduit à une LazyLoadingException après session est fermée:

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

Console.WriteLine(result.Name); 

Après this post, « propriétés paresseux. .. est rarement une fonctionnalité importante pour activer ... (et) dans Hibernate 3, est désactivé par défaut. "

Alors qu'est-ce que je fais mal? J'ai réussi à contourner l'exception LazyLoadingException en faisant un NHibernateUtil.Initialize(foo) mais la pire partie sont les instructions n + 1 sql qui mettent mon application à genoux.

Voici comment le mapping ressemble à:

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

BTW: La classe de base abstraite "BusinessObjectBase" encapsule la propriété ID qui sert l'identifiant interne.

+0

quelle version de NHibernate utilisez-vous? – Jaguar

+0

2.1.2.4000; hmm thats <3. Devinez propriétés paresseux ne sont même pas encore pris en charge dans cette version? Quoi qu'il en soit, comme Stefan a souligné que le problème n'est pas dû à la charge de propriété paresseuse. – chiccodoro

+0

mis à jour ma réponse avec des exemples de code et d'autres suggestions. –

Répondre

6

Je ne pense pas que cela soit dû à une charge de propriété paresseuse. C'est plutôt à cause de l'utilisation de Enumerable et Load.

Jetez un oeil à la reference documentation au sujet Enumerable:

... Le itérateur chargera des objets sur la demande, en utilisant les identifiants renvoyés par une requête SQL initiale (n + 1 sélectionne totale).

Soit utiliser pour réduire le chargement par lot le nombre de requêtes (dans la cartographie de la classe)

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

... ou utilisez List au lieu de Enumerable:

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

Note: Enumerable n'a de sens que si vous ne vous attendez pas à ce que vous ayez besoin du résultat entier, ou dans des cas spéciaux où vous ne voulez pas les avoir tous en mémoire en même temps (alors vous avez besoin de Evict pour les enlever). Pour la plupart des cas, List est ce dont vous avez besoin.


Dans le cas de Load, seul un proxy est créé (aucune requête est effectuée). Au premier accès, il est chargé. (Ceci est très puissant, par exemple, utiliser ce proxy comme arguments de filtrage dans les requêtes ou pour l'associer à une autre entité sans qu'il soit nécessaire de charger son contenu.) Si vous avez besoin de son contenu, utilisez Get à la place

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); 

.. ou mieux encore, utilisez l'entité uniquement lorsque la session est ouverte.

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

Réponse très précise et utile, merci. Suite à votre documentation de référence liée, la solution au premier problème serait: IList result = query.List (), ou si j'ai besoin d'une liste, Liste result = query.List () .ToList (); - Si c'est vrai, vous pourriez vouloir inclure ce code dans votre réponse pour la référence des futurs lecteurs? – chiccodoro

+0

J'ai maintenant implémenté le .List <>(), et il fonctionne bien mieux qu'avant! (Bien sûr, puisque la charge de la base de données est O (1) au lieu de O (n) maintenant) – chiccodoro

Questions connexes