2009-03-10 8 views
7

J'apprends un peu de NHibernate Fluent et j'ai couru à travers la classe semi-impressionnante PersistenceSpecification.Restauration des enregistrements créés par PersistenceSpecifications dans Fluent NHibernate

Je l'ai configuré dans un test unitaire pour vérifier mes mappages et cela fonctionne très bien. Cependant, il laisse l'enregistrement dans la base de données une fois terminé. J'ai essayé de le lancer dans une transaction afin que je puisse restaurer les modifications, mais une erreur se produit:

System.ObjectDisposedException: impossible d'accéder à un objet éliminé. Nom de l'objet: 'AdoTransaction' ..

Sans une transaction, je dois trouver les ID de l'enregistrement, les récupérer et les supprimer et cela ne semble pas très élégant.

Des pensées?

EDIT:

Voici l'extrait de code:

  var factory = GetSessionFactory(); 
      using (var session = factory.OpenSession()) 
      using (var transaction = session.BeginTransaction()) 
      { 
       new PersistenceSpecification<TimePeriod>(session) 
         .CheckProperty(x => x.EndDate, DateTime.Today) 
         .VerifyTheMappings(); 
       transaction.Rollback(); 
      } 
+0

J'ai également constaté que l'appel VerifyTheMappings avec un System.EnterpriseServices.ServiceDomain.Enter() et SetAbort()/Leave() fonctionne également. –

Répondre

9

Essayez de définir le niveau d'isolation sur la transaction. Cet extrait a fonctionné pour moi:

using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted)) 
{ 
    new PersistenceSpecification<Event>(_session) 
     .CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1)) 
     .VerifyTheMappings(); 
    trans.Rollback(); 
} 
+0

Obtient mon vote, exactement ce que je cherchais! – cgreeno

+0

System.Data.IsolationLevel, pas System.Transactions.IsolationLevel ... – rohancragg

2

Le PersistenceSpecification est habituellement utilisé avec une base de données en mémoire comme SQLite, c'est pourquoi il ne roule pas quoi que ce soit de retour. Je crois qu'il y a une surcharge de constructeur qui prend une instance ISession, avez-vous essayé d'obtenir une transaction à partir de là puis de la reconduire après?

+0

Oui, j'ai créé mon propre objet ISession à partir d'un appel SessionFactory.OpenSession(). De là j'ai commencé une transaction et après l'appel de VerifyTheMappings() j'ai essayé d'annuler la transaction et ai eu une erreur. Je vais poster l'extrait de code complet ci-dessous. –

2

Je pense que le problème ici est VerifyTheMappings() appelle TransactionSave() qui fait un tx.Commit() à la base de données. Comme l'a indiqué James, cette technique semble bien fonctionner pour jeter les techniques de test en mémoire. Cela ne fonctionnerait pas dans le cas d'un test des mappages par rapport à une base de données existante.

0

Je pense qu'il est très important de faire ce test avec votre réel db, pour voir que sa définition de tables est ok, donc j'ai développé une classe très simple qui effectue un test de crud sur une entité mappée et revenir à la fin;

internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable 
{ 
    public T EntityToTest { get; set; } 
    public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; } 
    public GenericMappingTesterWithRealDB() 
    { 
     Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null); 
    } 

    public void RunTest() 
    { 
     using (ISession session = SessionFactoryProvider.NewSession) 
     using (ITransaction transaction = session.BeginTransaction()) 
     { 
      try 
      { 
       session.Save(EntityToTest); 
       var item = session.Get<T>(EntityToTest.ID); 
       Assert.IsNotNull(item); 
       if (PerformEntityManipulationBeforeUpdate != null) 
       { 
        PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest); 
       } 
       session.Update(EntityToTest); 
       session.Delete(EntityToTest); 
       session.Save(EntityToTest); 
      } 
      catch (Exception e) 
      { 
       Assert.Fail(e.Message, e.StackTrace); 
      } 
      finally 
      { 
       transaction.Rollback(); 
      } 
     } 
    } 
} 

IIdentifiable dans mon projet est l'interface la plus fondamentale de mes entités

la classe utilise le nunit.framework mais u peut le faire avec chaque cadre de test u want

sessionfactoryprovider doit fournir le ISession obj

est ici un échantillon d'utilisation

/// <summary> 
/// Testing the mapping of our entities. 
/// there must be a server connection for this kind of test. 
/// </summary> 
[TestFixture] 
internal class someMappingTest 
{ 
    [Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")] 
    [Timeout(20000)] 
    public void checkthatMappingWorks() 
    { 
     // creatw the new entity 
     TestedType testOn = new TestedType(); 

     // set the initialization values 
     testOn.Name = "TestProfileExecution"; 

     // create the test object 
     new GenericMappingTesterWithRealDB<TestedType> 
     { 
      // assign an entity 
      EntityToTest = testOn, 

      // assign new values for update check 
      PerformEntityManipulationBeforeUpdate = 
       delegate(TestedType testedTypeBeingTested) 
        { 
         return testedTypeBeingTested.Name = "Updateing Test"; 
        } 
     }. 
     // call run test to perform the mapping test. 
     RunTest(); 

    } 
} 
1

Réglage IsolationLevel.ReadUncommitted fonctionnera, mais seulement d'ailleurs, puisque tout ce qu'il fait est de dire la session qu'il peut lire sans avoir besoin d'une nouvelle transaction (une lecture sale, dans le langage SGBD) - donc Session.Transaction.Commit() n'a pas à valider une transaction de base de données avant la lecture de la vérification. Cela signifie également que vous n'êtes pas obligé de tester ce que vous pensez tester! (Je pense aussi que ceci a probablement un support discutable parmi les bases de données non-MS SQL). La réponse de leebrandt fonctionne à cause du rollback explicite, pas du niveau d'isolation (nb.) Au moment de la réponse, cela a aidé plus que maintenant, voir la note ci-dessous).La bonne nouvelle est que la bonne façon de faire est de simplement annuler manuellement la transaction. Session.Transaction est automatiquement remplacé à chaque fois que la transaction est validée, donc vous devrez en faire une référence, et vous devrez en ouvrir une explicitement de toute façon, car TransactionalSave() vérifie si la transaction en cours est active et crée (et dispose!) Ses posséder sinon. Je teste généralement toutes mes applications dans le même appareil, où je vérifie également la création d'usine et quelques autres de persistance des infrastructures, donc j'aime le modèle suivant pour cela pour garder la tuyauterie vers le bas:

class TestFixture { 
    static ISessionFactory factory = CreateMyFactorySomehowHere(); 

    ISession session; 
    ITransaction tx; 

    public void Setup() 
    { 
     session = factory.OpenSession(); 
     tx = session.BeginTransaction(); 
    } 

    public void Cleanup() 
    { 
     tx.Rollback(); 
     tx.Dispose(); 
     session.Close(); 
    } 

    public void TestAMappingForSomething() 
    { 
     var spec = new PersistenceSpecification<Something> (session); 
     spec.VerifyTheMappings(); 
    } 
} 

De toute évidence, insérez votre propre terminologie et attributs/annotations propres au framework de test, mais vous en avez l'idée.


Je viens maintenant remarqué à quel point cette question est ancienne: ce comportement a été corrigé dans this commit en Juillet 09, pour gérer les transactions existantes bien pour que les travaux ci-dessus! Clairement, c'est ce que vous faisiez à l'origine de toute façon.