2010-01-14 5 views
2

J'ai écrit un integrationtest qui échoue:nhibernate devrais-je vider la session explicitement après "SaveOrUpdate"?

[Test] 
    public void Find_WorkItemWithMatchingDescriptionExistsInRepository_ReturnsWorkItem() 
    { 
     // arrange 
     WorkItemRepository repository = new WorkItemRepository(IsolatingFactory); 

     const string Description = "A"; 
     WorkItem itemWithMatchingDescription = WorkItem.Create(1, Description); 
     repository.Commit(itemWithMatchingDescription); 

     // act 
     List<WorkItem> searchResult = repository.Find(Description); 

     // assert 
     CollectionAssert.Contains(searchResult, itemWithMatchingDescription); 
    } 

Lorsque vous regardez la sortie sql de NHibernate je remarque qu'il crée une instruction_select pour l'opération repository.Find(), mais aucune opération d'insertion avant la sélection. Si je mets un explicite session.Flush() après le référentiel.Commit cela fonctionne comme prévu, mais je trouve ce comportement étrange. Pourquoi nhibernate vider la session automatiquement lorsqu'il y a des insertions en attente et qu'une requête est effectuée sur la session?

Je trouve cela dans la documentation:

9.6. Flush

De temps en temps, ISession exécute les instructions SQL nécessaires pour synchroniser l'état de la connexion ADO.NET avec l'état des objets stockés en mémoire. Ce processus, flush, arrive par défaut aux points suivants

* from some invocations of Find() or Enumerable() 
* from NHibernate.ITransaction.Commit() 
* from ISession.Flush() 

Il semble que le comportement de NHibernate par défaut est de rincer avant quering par trouver. Mon référentiel utilise Ling pour nhibernate pour la spécification de la requête, c'est peut-être un bug.

Mise à jour

Si je change la cartographie de:

public WorkItemClassMap() 
    { 
     Not.LazyLoad(); 

     Id(wi => wi.Id).GeneratedBy.Assigned(); 
     Map(wi => wi.Description).Length(500); 
     Version(wi => wi.LastChanged).UnsavedValue(new DateTime().ToString()); 
    } 

Pour:

public WorkItemClassMap() 
    { 
     Not.LazyLoad(); 

     Id(wi => wi.Id).GeneratedBy.Identity(); 
     Map(wi => wi.Description).Length(500); 
    } 

Il fonctionne comme prévu (débusque NHibernate l'insert à la base de données avant d'effectuer la requête). Je ne vois pas de bonnes raisons pour ce comportement, donc je suppose que c'est un bug. Cela ressemble à un besoin de faire un vidage manuel avant d'interroger dans mes dépôts, ce qui n'est pas quelque chose que je veux vraiment faire.

+0

ressemble à votre référentiel.Commit ne valide pas réellement ITransaction. est-ce? – dotjoe

+0

Non, mon dépôt (est sans savoir) pas responsable de la transaction parce qu'il n'a pas commencé la transaction (mais croyez-moi, il y a une transaction :-)) – Marius

Répondre

2

Est-ce que WorkItem dans votre exemple est mappé avec un ID composite? Je viens de coder un test simple. Ma classe factice serait automatiquement vidée quand j'avais une seule colonne d'identité. J'ai cependant écrit le même test avec un ID composite et j'ai obtenu le comportement que vous avez décrit ci-dessus.

+0

Vous êtes définitivement sur quelque chose ici. Je n'utilise pas de clés composites, mais la clé primaire a été affectée et j'utilise également un champ Version. Si j'ai enlevé le champ version et aussi changé en clé primaire pour par exemple "Généré par Identité" cela fonctionne comme prévu, cela doit être un bug. – Marius

3

Vous devez utiliser différentes instances ISession pour les méthodes d'insertion et de sélection. Il est préférable que ces sessions sont dans un bloc using afin qu'ils soient éliminés de manière appropriée et à l'intérieur d'une transaction de base de données:

using (var session = sessionFactory.OpenSession()) 
using (var tx = session.BeginTransaction()) 
{ 
    // perform your insert here 
    tx.Commit(); 
} 
+0

Ouais comme ça :) –

+3

Je ne sais pas comme cette contrainte. Il devrait être parfaitement acceptable de créer une transaction et d'effectuer à la fois la lecture et l'écriture dans la même transaction. – Marius

+0

C'est acceptable. L'important est de créer et de commettre l'ITransaction pour chaque unité de travail. Vous devriez le faire de toute façon et comme le dit la documentation, il provoque automatiquement un flush. – Aaronaught

0

En supposant que votre référentiel crée dépérir ou prend une session qui existe déjà. Lorsque vous créez le WorkItem et que vous le validez, il se place sur une liste de tâches pour cette session. Si votre référentiel ne crée pas de transaction et que votre méthode de validation ne valide pas explicitement la transaction. Ensuite, la mise à jour ou l'enregistrement attendra la fermeture de la session. Ainsi, bien que vous puissiez voir l'objet que vous avez créé, vous ne pourrez pas obtenir l'identité de la portée, par exemple, jusqu'à ce que vous fermiez la session. Certainement searchResult ne saura rien de l'objet que vous avez créé sans une vidange ou une transaction de fermeture.

Il existe des périls avec des transactions identiques aux sessions en général mais si vous créez de nouveaux objets, vous devez utiliser des transactions explicites. NHProfiler vous avertira volontiers à ce sujet.

+0

J'utilise des transactions explicites, tout le test d'intégration s'exécute en une seule session/transaction Je ne vois toujours pas de bonne raison quant à savoir pourquoi nhibernate ne s'autoflush pas en voyant que j'effectue une requête – Marius

+0

Ouais, je ne dis pas son idéal, je l'ai découvert hier, quand j'avais un objet enfant que je venais d'ajouter (et appelé session NHupofiler vous avertira à ce sujet, j'ai choisi d'avoir mon interface de dépôt exposer des méthodes pour commencer et valider des transactions sur une session en cours. , ce Je suis en théorie pas lié à nhibernate. Cheers, Mark –

Questions connexes