2011-04-14 8 views
8

Tengo un sistema que después de recibir un mensaje - lo encola (escribe en una tabla), y otro proceso sondea el DB y lo descoloca para procesarlo. En mis pruebas automáticas he fusionado las operaciones en el mismo proceso, pero no puedo (conceptualmente) fusionar las sesiones de NH de las dos operaciones.Error aleatorio al probar con NHibernate en un SQLite en memoria db

Naturalmente - surgen problemas.

He leído todo lo que pude sobre cómo hacer que la combinación SQLite-InMemory-NHibernate funcione en el mundo de las pruebas, pero ahora me he encontrado con pruebas RANDOMLY fallidas, debido a errores "sin dicha tabla". Para dejar en claro: "aleatorio" significa que la misma prueba con la misma configuración exacta y el código a veces fallarán.

tengo la siguiente configuración SQLite:

return SQLiteConfiguration 
.Standard 
.ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;")) 
.Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close"); 

Al principio de mi prueba (cada prueba) voy a buscar el proveedor de sesión "estática", y ruego a tirar de la base de datos existente limpia, y volver a crear el esquema:

public void PurgeDatabaseOrCreateNew() 
{ 
    using (var session = GetNewSession()) 
    using (var tx = session.BeginTransaction()) 
    { 
      PurgeDatabaseOrCreateNew(session); 
      tx.Commit(); 
    } 
} 

private void PurgeDatabaseOrCreateNew(ISession session) 
{ 
    //http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx 
    new SchemaExport(_Configuration) 
     .Execute(false, true, false, session.Connection, null); 
} 

así que sí, que está en una sesión diferente, pero la conexión se agruparon en SQLite, por lo que la próxima sesión se crea verá el esquema generado. Sin embargo, aunque la mayoría de las veces funciona, a veces la operación posterior de "enqueue" fallará porque no puede ver una tabla para mis mensajes entrantes. Además, parece que se produce al máximo una o dos veces por ejecución de conjunto de pruebas; no todas las pruebas fallan, solo la primera (y a veces otra. No estoy seguro de si es la segunda o no).

La peor parte es la aleatoriedad, naturalmente. Me he dicho a mí mismo que lo he solucionado varias veces ahora, simplemente porque simplemente "dejó de fallar". Al azar.

Esto ocurre en FW4.0, System.Data.SQLite versión x86, Win7 64b y 2008R2 (tres máquinas diferentes en total), NH2.1.2, configurado con FNH, en TestDriven.NET 32b precesos y NUnit console 32b procesos .

¿Ayuda?

+0

Tengo este problema también. Casi todas las pruebas pasan, pero de vez en cuando fallan una o dos pruebas con errores de "no hay tabla", y si las vuelvo a ejecutar, pasan. Creo que es solo que SQLite está recreando la conexión en el grupo de conexiones, al azar. –

Respuesta

8

Hola, estoy bastante seguro de que tengo exactamente el mismo problema que usted. Abro y cierro varias sesiones por prueba de integración. Después de excavar a través de la agrupación de conexiones SQLite y experimentar un poco, he llegado a la siguiente conclusión:

El código de agrupación SQLite almacena en caché la conexión utilizando WeakReferences, que no es el mejor option para el almacenamiento en caché, ya que la referencia a la (s) conexión (es) se borrarán cuando no haya una referencia normal (fuerte) a la conexión y se ejecute el GC. Como no se puede predecir cuándo se ejecuta el GC, esto explica la "aleatoriedad". Intente agregar un GC.Collect(); entre cerrar uno y abrir otra sesión, su prueba siempre fallará.

Mi solución fue la de almacenar en caché la conexión entre sesiones de apertura a mí mismo, así:

public class BaseIntegrationTest 
{ 
    private static ISessionFactory _sessionFactory; 
    private static Configuration _configuration; 
    private static SchemaExport _schemaExport; 

    // I cache the whole session because I don't want it and the 
    // underlying connection to get closed. 
    // The "Connection" property of the ISession is what we actually want. 
    // Using the NHibernate SQLite Driver to get the connection would probably 
    // work too. 
    private static ISession _keepConnectionAlive; 

    static BaseIntegrationTest() 
    { 
     _configuration = new Configuration(); 
     _configuration.Configure(); 
     _configuration.AddAssembly(typeof(Product).Assembly); 
     _sessionFactory = _configuration.BuildSessionFactory(); 
     _schemaExport = new SchemaExport(_configuration); 

     _keepConnectionAlive = _sessionFactory.OpenSession(); 
    } 

    [SetUp] 
    protected void RecreateDB() 
    { 
     _schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null); 
    } 

    protected ISession OpenSession() 
    { 
     return _sessionFactory.OpenSession(_keepConnectionAlive.Connection); 
    } 
} 

Cada uno de mis integrationtests hereda de esta clase, y pide openSession() para obtener una sesión. RecreateDB es llamado por NUnit antes de cada prueba debido al atributo [SetUp].

Espero que esto lo ayude a usted o a cualquier otra persona que reciba este error.

+0

Cambiar el mecanismo de almacenamiento en caché de SQLite probablemente sería la solución más limpia, pero no estoy seguro de hacer eso todavía ;-) – dvdvorle

+0

Así es exactamente como lo resolví. Perdón por no haber publicado la solución antes. – Arielr

0

Lo único que se tiene en cuenta es que usted está dejando la sesión al azar abierta después de la prueba. Debe asegurarse de que cualquier ISession existente esté cerrada antes de abrir otra. Si no está utilizando la instrucción using() o llamando a Dispose() de forma manual, la sesión todavía podría estar viva en alguna parte y provocar esas excepciones aleatorias.