34 Stimmen

Problem bei der Verwendung von SQLite :memory: mit NHibernate

Ich verwende NHibernate für meine Dataacess, und für eine Weile nicht ich habe mit SQLite für lokale Integrationstests. Ich habe eine Datei verwendet, aber ich dachte, ich würde aus der :memory: Option. Wenn ich einen der Integrationstests starte, scheint die Datenbank erstellt zu werden (NHibernate spuckt die Tabellenerstellungs-SQL aus), aber die Interaktion mit der Datenbank verursacht einen Fehler.

Hat jemand jeder NHibernate mit einer In-Memory-Datenbank arbeiten bekommen? Ist das überhaupt möglich? Die Verbindungszeichenfolge, die ich verwende, ist die folgende:

Data Source=:memory:;Version=3;New=True

41voto

Sean Carpenter Punkte 7651

Eine SQLite-Speicherdatenbank existiert nur so lange, wie die Verbindung zu ihr offen bleibt. Um sie in Unit-Tests mit NHibernate zu verwenden:
1. Öffnen Sie zu Beginn Ihres Tests eine ISession (vielleicht in einer [SetUp]-Methode).
2. Verwenden Sie die Verbindung aus dieser Sitzung in Ihrem SchemaExport-Aufruf.
3. Verwenden Sie dieselbe Sitzung für Ihre Tests.
4. Beenden Sie die Sitzung am Ende des Tests (eventuell mit einer [TearDown]-Methode).

21voto

decates Punkte 3276

Ich konnte eine SQLite-In-Memory-Datenbank verwenden und vermeiden, dass ich das Schema für jeden Test neu erstellen musste, indem ich die SQLite-Funktion Unterstützung für 'Shared Cache' die es ermöglicht, eine In-Memory-Datenbank über mehrere Verbindungen hinweg gemeinsam zu nutzen.

Ich habe Folgendes getan AssemblyInitialize (Ich verwende MSTest):

  • Konfigurieren Sie NHibernate (Fluently) für die Verwendung von SQLite mit der folgenden Verbindungszeichenfolge:

    FullUri=file:memorydb.db?mode=memory&cache=shared
  • Verwenden Sie diese Konfiguration, um ein hbm2ddl zu erstellen. SchemaExport Objekt, und führen Sie es auf einer anderen Verbindung aus (aber wieder mit derselben Verbindungszeichenfolge).

  • Lassen Sie diese Verbindung, auf die ein statisches Feld verweist, offen, bis AssemblyCleanup zu diesem Zeitpunkt wird es geschlossen und entsorgt. Das liegt daran, dass SQLite mindestens eine aktive Verbindung zur In-Memory-Datenbank benötigt, um zu wissen, dass sie noch benötigt wird, und um Aufräumarbeiten zu vermeiden.

Vor jedem Test wird eine neue Sitzung erstellt, und der Test läuft in einer Transaktion, die am Ende zurückgerollt wird.

Hier ist ein Beispiel für den Test-Assembler-Code:

[TestClass]
public static class SampleAssemblySetup
{
    private const string ConnectionString = "FullUri=file:memorydb.db?mode=memory&cache=shared";
    private static SQLiteConnection _connection;

    [AssemblyInitialize]
    public static void AssemblyInit(TestContext context)
    {
        var configuration = Fluently.Configure()
                                       .Database(SQLiteConfiguration.Standard.ConnectionString(ConnectionString))
                                       .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("MyMappingsAssembly")))
                                       .ExposeConfiguration(x => x.SetProperty("current_session_context_class", "call"))
                                       .BuildConfiguration();

        // Create the schema in the database
        // Because it's an in-memory database, we hold this connection open until all the tests are finished
        var schemaExport = new SchemaExport(configuration);
        _connection = new SQLiteConnection(ConnectionString);
        _connection.Open();
        schemaExport.Execute(false, true, false, _connection, null);
    }

    [AssemblyCleanup]
    public static void AssemblyTearDown()
    {
        if (_connection != null)
        {
            _connection.Dispose();
            _connection = null;
        }
    }
}

Und eine Basisklasse für jede Unit-Test-Klasse/Fixture:

public class TestBase
{
    [TestInitialize]
    public virtual void Initialize()
    {
        NHibernateBootstrapper.InitializeSession();
        var transaction = SessionFactory.Current.GetCurrentSession().BeginTransaction();
    }

    [TestCleanup]
    public virtual void Cleanup()
    {
        var currentSession = SessionFactory.Current.GetCurrentSession();
        if (currentSession.Transaction != null)
        {
            currentSession.Transaction.Rollback();
            currentSession.Close();
        }

        NHibernateBootstrapper.CleanupSession();
    }
}

Die Ressourcenverwaltung könnte verbessert werden, das gebe ich zu, aber es handelt sich schließlich um Unit-Tests (Verbesserungsvorschläge sind willkommen!).

9voto

Stefan Steinegger Punkte 62197

Wir verwenden SQLite im Speicher für alle unsere Datenbanktests. Wir verwenden eine einzige ADO-Verbindung für die Tests, die für alle NH-Sitzungen wiederverwendet wird, die von demselben Test geöffnet werden.

  1. Vor jedem Test: Verbindung herstellen
  2. Schema für diese Verbindung erstellen
  3. Test durchführen. Für alle Sitzungen wird die gleiche Verbindung verwendet
  4. Nach dem Test: Verbindung schließen

Dies ermöglicht auch die Durchführung von Tests, die mehrere Sitzungen umfassen. Die SessionFactory wird ebenfalls einmal für alle Tests erstellt, da das Einlesen der Mapping-Dateien einige Zeit in Anspruch nimmt.


Editar

Verwendung des Shared Cache

Seit System.Data.Sqlite 1.0.82 (oder Sqlite 3.7.13 ), gibt es eine Gemeinsamer Cache die es mehreren Verbindungen ermöglicht, dieselben Daten gemeinsam zu nutzen, auch für In-Memory-Datenbanken . Dies ermöglicht die Erstellung der In-Memory-Datenbank in einer Verbindung und ihre Verwendung in einer anderen. (Ich habe es noch nicht ausprobiert, aber theoretisch sollte dies funktionieren):

  • Ändern Sie die Verbindungszeichenfolge in file::memory:?cache=shared
  • Öffnen Sie eine Verbindung und erstellen Sie das Schema
  • Halten Sie diese Verbindung bis zum Ende des Tests offen
  • Lassen Sie NH während des Tests weitere Verbindungen herstellen (normales Verhalten).

8voto

Julien Bérubé Punkte 1236

Ich hatte ähnliche Probleme, die auch nach dem Öffnen der ISession wie oben beschrieben und dem Hinzufügen von "Pooling=True;Max Pool Size=1" zu meiner Verbindungszeichenfolge bestehen blieben. Das hat zwar geholfen, aber ich hatte immer noch einige Fälle, in denen die Verbindung während eines Tests geschlossen wurde (in der Regel direkt nach dem Übertragen einer Transaktion).

Was bei mir schließlich funktionierte, war die Einstellung der Eigenschaft "connection.release_mode" auf "on_close" in meiner SessionFactory-Konfiguration.

Meine Konfiguration in der Datei app.config sieht nun folgendermaßen aus:

  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <reflection-optimizer use="true" />
    <session-factory>
      <property name="connection.connection_string_name">testSqlLiteDB</property>
      <property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.release_mode">on_close</property>
      <property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
      <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
      <property name="query.substitutions">true=1;false=0</property>
    </session-factory>
  </hibernate-configuration>

Hoffentlich hilft das!

1voto

Anders B Punkte 3067

Ich hatte eine Menge Probleme mit der SQLite-Speicherdatenbank. Deshalb arbeiten wir jetzt mit SQLite mit Dateien auf einer Ramdrive-Platte.

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