2 Stimmen

Kann ich meine NHibernate-Sitzung leeren und eine neue Sitzung erhalten, ohne die Transaktion zu bestätigen?

Ich verwende Castle ActiveRecord für die Persistenz, und ich versuche, eine Basisklasse für meine Persistenz-Tests zu schreiben, die das folgende tun wird:

  • Öffnen Sie eine Transaktion für jeden Testfall und rollen Sie sie am Ende des Testfalls zurück, so dass ich für jeden Testfall eine saubere DB erhalte, ohne dass ich das Schema für jeden Testfall neu erstellen muss.
  • Bereitstellung der Möglichkeit, meine NHibernate-Sitzung zu flushen und mitten in einem Test eine neue zu erhalten, so dass ich weiß, dass meine Persistenzoperationen wirklich die DB und nicht nur die NHibernate-Sitzung getroffen haben.

Um zu beweisen, dass meine Basisklasse ( ARTestBase ) funktioniert, habe ich mir die folgenden Beispieltests ausgedacht.

[TestFixture]
public class ARTestBaseTest : ARTestBase
{
    [Test]
    public void object_created_in_this_test_should_not_get_committed_to_db()
    {
        ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});

        Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
    }

    [Test]
    public void object_created_in_previous_test_should_not_have_been_committed_to_db()
    {
        ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});

        Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
    }

    [Test]
    public void calling_flush_should_make_nhibernate_retrieve_fresh_objects()
    {
        var savedEntity = new Entity {Name = "test"};
        ActiveRecordMediator<Entity>.Save(savedEntity);
        Flush();
        // Could use FindOne, but then this test would fail if the transactions aren't being rolled back
        foreach (var entity in ActiveRecordMediator<Entity>.FindAll())
        {
            Assert.That(entity, Is.Not.SameAs(savedEntity));
        }
    }
}

Hier ist mein bester Versuch, die Basisklasse zu erstellen. Sie implementiert korrekt Flush() so dass der dritte Testfall bestanden wird. Allerdings werden die Transaktionen nicht zurückgesetzt, so dass der zweite Test fehlschlägt.

public class ARTestBase
{
    private SessionScope sessionScope;
    private TransactionScope transactionScope;

    [TestFixtureSetUp]
    public void InitialiseAR()
    {
        ActiveRecordStarter.ResetInitializationFlag();
        ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
        ActiveRecordStarter.CreateSchema();
    }

    [SetUp]
    public virtual void SetUp()
    {
        transactionScope = new TransactionScope(OnDispose.Rollback);
        sessionScope = new SessionScope();
    }

    [TearDown]
    public virtual void TearDown()
    {
        sessionScope.Dispose();
        transactionScope.Dispose();
    }

    protected void Flush()
    {
        sessionScope.Dispose();
        sessionScope = new SessionScope();
    }

    [TestFixtureTearDown]
    public virtual void TestFixtureTearDown()
    {
        SQLiteProvider.ExplicitlyDestroyConnection();
    }
}

Beachten Sie, dass ich einen benutzerdefinierten SQLite-Anbieter mit einer In-Memory-Datenbank verwende. Mein benutzerdefinierter Anbieter, der von dieser Blogbeitrag hält die Verbindung ständig offen, um das Schema zu erhalten. Wenn Sie dies entfernen und eine normale SQL Server-Datenbank verwenden, ändert sich das Verhalten nicht.

Gibt es eine Möglichkeit, das gewünschte Verhalten zu erreichen?

1voto

kͩeͣmͮpͥ ͩ Punkte 7635

Bei ActiveRecord bin ich mir nicht so sicher, aber in NHibernate gehört eine Transaktion zu einer Session, nicht umgekehrt.

Wenn Sie ADO.Net schon oft benutzt haben, wird dies mehr Sinn machen, da die Erstellung einer IDbTransaction müssen Sie die Verbindung nutzen. ActiveRecord's TransactionScope (und NHibnerate's ITransaction ) umhüllen im Wesentlichen eine IDbTransaction Sie müssen also die SessionScope vor dem TransactionScope .

Je nachdem, ob Sie NHibernate 1.2 GA oder NHibernate 2.* verwenden, und je nachdem, was Sie brauchen, können Sie auch Folgendes finden FlushMode Ihr SessionScope hat) ist, dass Ihr Aufruf an FindAll() kann dazu führen, dass die Sitzung trotzdem geflutet wird, da NHibernate erkennt, dass es die richtigen Daten nicht abrufen kann, ohne den letzten Aufruf von Save .

Haben Sie schon einmal versucht, mit SessionScope.Flush() anstatt eine neue SessionScope ?

0voto

Alex Scordellis Punkte 492

使用方法 SessionScope.Flush() lässt meinen dritten Test scheitern. Wie ich es verstehe, Flush() führt das SQL aus, um meine Datensätze in die DB zu schieben, entfernt aber keine Objekte aus der Sitzung. Das passt zu dem, was Sie sagen über FindAll() was eine Spülung verursacht.

Was ich wirklich will, ist SessionScope.Flush() (um den Zustand der DB mit der Sitzung zu synchronisieren) sowie SessionScope.EvictAll() (um sicherzustellen, dass ich bei nachfolgenden Abfragen neue Objekte erhalte). Mein new SessionScope() war ein Versuch der Simulation von EvictAll() .

Ihre Bemerkung, dass die Sitzung die Transaktion einschließt und nicht umgekehrt, hat mich auf eine Idee gebracht. Ich bin mir nicht sicher, wie koscher es ist, eine neue SessionScope innerhalb einer TransactionScope im Inneren eines gespülten SessionScope und erwarten, dass es an der Transaktion teilnimmt, aber es scheint zu funktionieren:

public abstract class ARTestBase
{
    private SessionScope sessionScope;
    private TransactionScope transactionScope;
    private bool reverse;
    private IList<SessionScope> undisposedScopes;

    [TestFixtureSetUp]
    public void InitialiseAR()
    {
        ActiveRecordStarter.ResetInitializationFlag();
        ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
        ActiveRecordStarter.CreateSchema();
        InitialiseIoC();
        undisposedScopes = new List<SessionScope>();
    }

    [SetUp]
    public virtual void SetUp()
    {
        sessionScope = new SessionScope();
        transactionScope = new TransactionScope(OnDispose.Rollback);
        transactionScope.VoteRollBack();
        base.CreateInstanceUnderTest();
        reverse = false;
    }

    [TearDown]
    public virtual void TearDown()
    {
        if (reverse)
        {
            sessionScope.Dispose();
            transactionScope.Dispose();
        }
        else
        {
            transactionScope.Dispose();
            sessionScope.Dispose();
        }
    }

    [TestFixtureTearDown]
    public virtual void TestFixtureTearDown()
    {
        foreach (var scope in undisposedScopes)
        {
            scope.Dispose();
        }
        SQLiteProvider.ExplicitlyDestroyConnection();
    }

    protected void Flush()
    {
        reverse = true;
        sessionScope.Flush();
        undisposedScopes.Add(sessionScope);
        sessionScope = new SessionScope();
    }
}

Wenn man weiter darüber nachdenkt, kann man damit nicht mehr als einmal pro Testfall spülen. Ich denke, ich kann damit umgehen, indem ich die Bereiche sorgfältiger verfolge. Vielleicht sehe ich mir das später an.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X