Problema con SQLite: memory: con NHibernate


Uso NHibernate para mi dataacess, y por un tiempo no he estado usando SQLite para pruebas de integración local. He estado usando un archivo, pero pensé que sacaría la opción: memory:. Cuando enciendo cualquiera de las pruebas de integración, la base de datos parece estar creada (NHibernate escupe el sql de creación de tabla) pero la interfaz con la base de datos causa un error.

¿Alguien ha conseguido que NHibernate trabaje con una base de datos en memoria? ¿Es posible? La cadena de conexión I'm usando es esto:

Data Source=:memory:;Version=3;New=True
Author: Chris Canal, 2008-10-10

8 answers

Una base de datos de memoria SQLite solo existe mientras la conexión a ella permanezca abierta. Para usarlo en pruebas unitarias con NHibernate:
1. Abra una ISession al principio de su prueba (tal vez en un método [SetUp]).
2. Utilice la conexión de esa sesión en la llamada de SchemaExport.
3. Usa esa misma sesión en tus pruebas.
4. Cierre la sesión al final de su prueba (tal vez en un método [Desmontaje]).

 38
Author: Sean Carpenter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-10 23:54:10

Pude usar una base de datos SQLite en memoria y evitar tener que reconstruir el esquema para cada prueba usando el soporte de SQLite para 'Shared Cache', que permite que una base de datos en memoria se comparta entre conexiones.

Hice lo siguiente en AssemblyInitialize (estoy usando MSTest):

  • Configure NHibernate (con fluidez) para usar SQLite con la siguiente cadena de conexión:

    FullUri=file:memorydb.db?mode=memory&cache=shared
    
  • Usar esa configuración para crear un hbm2ddl.SchemaExport objeto, y ejecutarlo en una conexión separada (pero con la misma cadena de conexión de nuevo).

  • Deje esa conexión abierta, y referenciada por un campo estático, hasta AssemblyCleanup, momento en el que se cierra y se elimina. Esto se debe a que SQLite necesita que se mantenga al menos una conexión activa en la base de datos en memoria para saber que todavía es necesaria y evitar ordenarla.

Antes de que se ejecute cada prueba, se crea una nueva sesión, y la prueba se ejecuta en una transacción que se revierte al final.

Aquí hay un ejemplo del código de nivel de ensamblado de prueba:

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

Y una clase base para cada clase de prueba unitaria/accesorio:

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 gestión de recursos podría mejorar, lo admito, pero estas son pruebas unitarias después de todo (mejoras sugeridas bienvenidas!).

 17
Author: decates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-22 15:36:24

Estamos usando SQLite en memoria para todas nuestras pruebas de base de datos. Estamos utilizando una única conexión ADO para las pruebas que se reutiliza para todas las sesiones NH abiertas por la misma prueba.

  1. Antes de cada prueba: crear conexión
  2. Crear esquema en esta conexión
  3. Ejecutar prueba. La misma conexión se utiliza para todas las sesiones
  4. Después de la prueba: cerrar conexión

Esto también permite ejecutar pruebas con varias sesiones incluidas. La SessionFactory también se crea una vez para todas las pruebas, porque la lectura de los archivos de asignación toma bastante tiempo.


Editar

Uso de la Caché compartida

Desde el Sistema.Datos.Sqlite 1.0.82 (o Sqlite 3.7.13), existe una Caché compartida , que permite que varias conexiones compartan los mismos datos, también para bases de datos en memoria . Esto permite la creación de la base de datos en memoria en una conexión y usarla en otra. (No lo he intentado todavía, pero en teoría, esto debería trabajo):

  • Cambie la cadena de conexión a file::memory:?cache=shared
  • Abra una conexión y cree el esquema
  • Mantenga esta conexión abierta hasta el final de la prueba
  • Deje que NH cree otras conexiones (comportamiento normal) durante la prueba.
 9
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-28 01:19:47

Tuve problemas similares que duraron incluso después de abrir la ISession como se indicó anteriormente, y agregar "Pooling=True;Max Pool Size=1" a mi cadena de conexión. Ayudó, pero todavía tenía algunos casos en los que la conexión se cerraba durante una prueba (generalmente justo después de realizar una transacción).

Lo que finalmente funcionó para mí fue establecer la propiedad "connection.release_mode " a "on_close" en mi configuración de SessionFactory.

Mi configuración en la aplicación.archivo de configuración ahora mira likes esto:

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

Espero que ayude!

 8
Author: Julien Bérubé,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-21 16:51:01

Solo una suposición salvaje, pero es la salida sql de NHibernate usando un comando no soportado por sqlite?

Además, ¿qué sucede si se utiliza un archivo en lugar de memoria? (System. IO.Path.GetTempFileName() funcionaría creo...)

 0
Author: chills42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-10 16:13:51

Lo estoy haciendo con Rhino Commons. Si no desea utilizar Rhino Commons, puede estudiar la fuente y ver cómo lo hace. El único problema que he tenido es que SQLite no admite transacciones anidadas. Esto me obligó a cambiar mi código para admitir las pruebas de integración. Las pruebas de integración con la base de datos en memoria son tan increíbles que decidí que era un compromiso justo.

 0
Author: Tim Scott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 03:23:20

He tenido muchos problemas con la base de datos de memoria SQLite. Así que ahora estamos usando SQLite trabajando con archivos en un disco ramdrive.

 0
Author: Anders B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-29 16:13:45

Solo quiero agradecer a decates. He estado tratando de resolver esto durante un par de meses y todo lo que tenía que hacer era agregar

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

A la cadena de conexión en mi archivo de configuración nhibernate. También usando just NHibernate con *.hbm.xml y no FNH y realmente no tenía que modificar mi código en absoluto!

 0
Author: Drexter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-09 17:01:31