2009-03-10 9 views
7

Ich lerne Fluent NHibernate und habe die semi-geniale PersistenceSpecification-Klasse durchlaufen.Zurückrollen von Datensätzen, die von PersistenceSpecifications in Fluent NHibernate erstellt wurden

Ich habe es in einem Komponententest eingerichtet, um meine Zuordnungen zu überprüfen, und es funktioniert gut. Es verlässt jedoch den Datensatz in der Datenbank, wenn Sie fertig sind. Ich habe versucht, es in eine Transaktion zu werfen, so kann ich die Änderungen zurücksetzen, aber ich erhalte einen Fehler:

System.ObjectDisposedException: Kann nicht auf ein Objekt zugreifen. Objektname: 'AdoTransaction' ..

Ohne eine Transaktion muss ich die IDs des Datensatzes herausfinden, sie abrufen und löschen und das scheint nicht sehr elegant.

Irgendwelche Gedanken?

EDIT:

Hier ist der Code-Schnipsel:

  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

Ich habe auch festgestellt, dass das Wrapping des VerifyTheMappings-Aufrufs mit System.EnterpriseServices.ServiceDomain.Enter() und SetAbort()/Leave() ebenfalls funktioniert. –

Antwort

9

Versuchen Sie, den IsolationLevel für die Transaktion festzulegen. Dieser Code-Schnipsel für mich gearbeitet:

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

Bekommt meine Stimme, genau das, was ich gesucht habe! – cgreeno

+0

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

2

Die PersistenceSpecification in der Regel mit einer In-Memory-Datenbank wie SQLite verwendet wird, das ist, warum er nichts zurückrollen. Ich glaube, dass es eine Konstruktorüberladung gibt, die eine ISession Instanz nimmt, haben Sie versucht, eine Transaktion von dort zu bekommen, die dann nachher zurückbringt?

+0

Yup, ich habe mein eigenes ISession Objekt von einem SessionFactory.OpenSession() Aufruf erstellt. Von dort habe ich eine Transaktion gestartet und nach dem Aufruf von VerifyTheMappings() habe ich versucht, die Transaktion rückgängig zu machen und habe einen Fehler erhalten. Ich werde das vollständige Code-Snippet unten posten. –

2

Ich denke, das Problem hier ist VerifyTheMappings() ruft TransactionSave(), die eine tx.Commit() auf die Datenbank ausführt. Wie James anmerkte, scheint diese Technik gut zu funktionieren, um In-Memory-Testtechniken wegzuwerfen. Dies würde nicht funktionieren im Falle von Test-Mappings gegen eine ältere Datenbank.

0

ich denke, dass es sehr wichtig ist, dieses Testen mit Ihrem realen db zu machen, um zu sehen, dass seine Tabellendefinition r ok ist, also habe ich eine sehr einfache Klasse entwickelt, die einen Crud-Test auf einer gemappten Entität durchführt und einen Rollback durchführt das Ende;

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 in meinem Projekt ist die einfachste Schnittstelle meiner Entitäten

die Klasse die NUnit.Framework verwendet, aber u kann es tun, mit jedem Test-Framework u

wollen sessionfactoryprovider liefern muss die ISession obj

hier ist ein Beispiel für die Verwendung

/// <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

IsolationLevel.ReadUncommitted Einstellung funktioniert, aber nur nebenbei bemerkt, da alle es tut, ist die Sitzung zu sagen, dass es, ohne dass eine neue Transaktion (ein Dirty Read, in DBMS parlance) lesen kann - Daher muss Session.Transaction.Commit() keine Datenbanktransaktion vor dem Lesen der Verifikation durchführen. Dies bedeutet auch, dass es nicht unbedingt testet, was Sie zu testen glauben! (Ich denke auch, dass dies wahrscheinlich fragwürdige Unterstützung in Nicht-MS SQL-Datenbanken hat). Die Antwort von Leebrandt funktioniert wegen des expliziten Rollbacks, nicht der Isolationsstufe (NB. Zum Zeitpunkt der Antwort half dies mehr als jetzt, siehe Hinweis unten).

Die gute Nachricht ist, dass der richtige Weg, dies zu tun ist, nur die Transaktion manuell zurücksetzen. Session.Transaction wird automatisch ersetzt, wenn die Transaktion festgeschrieben wird, also müssen Sie einen Verweis darauf halten, und Sie müssen sowieso einen explizit öffnen, da TransactionalSave() überprüft, ob die aktuelle Transaktion aktiv ist und erstellt (und disponiert!) eigenes wenn nicht. Ich teste der Regel alle meine Zuordnungen in der gleichen Halterung, wo ich überprüfen, auch die Fabrik-Erstellung und ein paar andere Infrastruktur Ausdauer Dinge, so Ich mag das folgende Muster für diese die Sanitär zu halten:

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

Offensichtlich Fügen Sie Ihre eigene Test-Framework-spezifische Terminologie und Attribute/Anmerkungen überall hinzu, aber Sie erhalten die Idee.


Ich habe bemerkt, gerade jetzt, wie alt diese Frage ist: dieses Verhalten in this commit im Juli 09, bestehende Transaktionen festgelegt war schön zu handhaben, so dass die oben genannten Arbeiten! Klar, das warst du ursprünglich sowieso.