Domanda

Uso NHibernate per il mio dataacess e per un po 'non ho usato SQLite per i test di integrazione locale. Ho usato un file, ma ho pensato di uscire dall'opzione: memory :. Quando accendo uno qualsiasi dei test di integrazione, il database sembra essere stato creato (NHibernate sputa la sql di creazione della tabella) ma l'interfaccia con il database provoca un errore.

Qualcuno ha ottenuto NHibernate che lavora con un database in memoria? È anche possibile? La stringa di connessione che sto usando è questa:

Data Source=:memory:;Version=3;New=True
È stato utile?

Soluzione

Un database di memoria SQLite esiste solo fino a quando la connessione rimane aperta. Per utilizzarlo nei test unitari con NHibernate:
1. Aprire una ISession all'inizio del test (magari con un metodo [SetUp]).
2. Utilizzare la connessione da quella sessione nella chiamata SchemaExport.
3. Utilizzare la stessa sessione nei test.
4. Chiudere la sessione al termine del test (magari con un metodo [TearDown]).

Altri suggerimenti

Sono stato in grado di utilizzare un database in-memory di SQLite ed evitare di dover ricostruire lo schema per ogni test utilizzando il supporto di SQLite per "Cache condivisa" , che consente di condividere un database in memoria tra più connessioni.

Ho fatto quanto segue in AssemblyInitialize (sto usando MSTest):

  • Configura NHibernate (fluentemente) per usare SQLite con la seguente stringa di connessione:

    FullUri=file:memorydb.db?mode=memory&cache=shared
    
  • Usa quella configurazione per creare un oggetto hbm2ddl. SchemaExport ed eseguilo su una connessione separata (ma con quella stessa stringa di connessione di nuovo).

  • Lascia aperta quella connessione e referenziata da un campo statico, fino a AssemblyCleanup , a quel punto viene chiusa e smaltita. Questo perché SQLite necessita di almeno una connessione attiva da conservare nel database in memoria per sapere che è ancora necessario ed evitare di riordinare.

Prima dell'esecuzione di ogni test, viene creata una nuova sessione e il test viene eseguito in una transazione che viene ripristinata alla fine.

Ecco un esempio del codice a livello di assembly di prova:

[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;
        }
    }
}

E una classe base per ogni classe / apparecchio di test unitario:

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

La gestione delle risorse potrebbe migliorare, lo ammetto, ma dopo tutto si tratta di test unitari (miglioramenti suggeriti, benvenuto!).

Stiamo usando SQLite in memoria per tutti i test del nostro database. Stiamo utilizzando una singola connessione ADO per i test che viene riutilizzata per tutte le sessioni NH aperte dallo stesso test.

  1. Prima di ogni test: crea una connessione
  2. Crea schema su questa connessione
  3. Esegui test. La stessa connessione viene utilizzata per tutte le sessioni
  4. Dopo il test: chiudi connessione

Ciò consente anche l'esecuzione di test con diverse sessioni incluse. SessionFactory viene inoltre creato una volta per tutti i test, poiché la lettura dei file di mapping richiede parecchio tempo.


Modifica

Uso della cache condivisa

Dal momento che System.Data.Sqlite 1.0.82 (o Sqlite 3.7.13 ), esiste un Cache condivisa , che consente a più connessioni di condividere gli stessi dati, anche per Database in memoria . Ciò consente la creazione del database in memoria in una connessione e lo utilizza in un'altra. (Non l'ho ancora provato, ma in teoria dovrebbe funzionare):

  • Cambia la stringa di connessione in file::memory:?cache=shared
  • Apri una connessione e crea lo schema
  • Mantieni aperta questa connessione fino alla fine del test
  • Consenti a NH di creare altre connessioni (comportamento normale) durante il test.

Ho avuto problemi simili che sono durati anche dopo l'apertura di ISession come indicato sopra e l'aggiunta di " Pooling = True; Dimensione massima del pool = 1 " alla mia stringa di connessione. Mi ha aiutato, ma avevo ancora alcuni casi in cui la connessione si chiudeva durante un test (di solito subito dopo aver eseguito una transazione).

Quello che alla fine ha funzionato per me è stato impostare la proprietà " connection.release_mode " a " on_close " nella mia configurazione di SessionFactory.

La mia configurazione nel file app.config ora è simile a questa:

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

Spero che sia d'aiuto!

Solo un'ipotesi selvaggia, ma l'output sql di NHibernate utilizza un comando non supportato da sqlite?

Inoltre, cosa succede se si utilizza un file anziché la memoria? (System.IO.Path.GetTempFileName () funzionerebbe penso ...)

Lo sto facendo con Rhino Commons . Se non vuoi usare Rhino Commons puoi studiare la fonte per vedere come lo fa. L'unico problema che ho riscontrato è che SQLite non supporta le transazioni nidificate. Questo mi ha costretto a cambiare il mio codice per supportare i test di integrazione. I test di integrazione con nel database di memoria sono così fantastici, ho deciso che era un giusto compromesso.

Ho avuto molti problemi con il database di memoria SQLite. Quindi ora stiamo usando SQLite lavorando con i file su un disco rigido.

Voglio solo ringraziare i decreti. Ho cercato di risolverlo per un paio di mesi e tutto quello che dovevo fare era aggiungere

FullUri=file:memorydb.db?mode=memory&cache=shared

alla stringa di connessione nel mio file config nhibernate. Anche usando solo NHibernate con * .hbm.xml e non FNH e non ho dovuto modificare il mio codice!

Ho avuto lo stesso errore, quando ho dimenticato di importare il pacchetto SQLite Nuget.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top