2009-03-10 5 views
7

Estoy aprendiendo algo de NHibernate y he encontrado la clase PersistenceSpecification casi increíble.Recuperando registros creados por PersistenceSpecifications en Fluither NHibernate

Lo he configurado en una prueba unitaria para verificar mis asignaciones y funciona muy bien. Sin embargo, deja el registro en la base de datos cuando termina. Intenté tirarlo en una transacción para poder deshacer los cambios, pero aparece un error:

System.ObjectDisposedException: no se puede acceder a un objeto eliminado. Nombre del objeto: 'AdoTransaction' ..

Sin una transacción tengo que averiguar las identificaciones del registro, recuperarlas y eliminarlas y eso no parece muy elegante.

¿Alguna idea?

EDIT:

Aquí está el fragmento de código:

  var factory = GetSessionFactory(); 
      using (var session = factory.OpenSession()) 
      using (var transaction = session.BeginTransaction()) 
      { 
       new PersistenceSpecification<TimePeriod>(session) 
         .CheckProperty(x => x.EndDate, DateTime.Today) 
         .VerifyTheMappings(); 
       transaction.Rollback(); 
      } 
+0

También he encontrado que el ajuste de la llamada a VerifyTheMappings con un System.EnterpriseServices.ServiceDomain.Enter() y SetAbort()/Leave() también funciona. –

Respuesta

9

Intente establecer IsolationLevel en la transacción. Este fragmento de código que funcionó para mí:

using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted)) 
{ 
    new PersistenceSpecification<Event>(_session) 
     .CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1)) 
     .VerifyTheMappings(); 
    trans.Rollback(); 
} 
+0

¡Obtiene mi voto, exactamente lo que estaba buscando! – cgreeno

+0

System.Data.IsolationLevel, no System.Transactions.IsolationLevel ... – rohancragg

2

El PersistenceSpecification por lo general se utiliza con una base de datos en memoria como SQLite, es por eso que no rueda nada a cambio. Creo que hay una sobrecarga de constructor que toma una instancia de ISession, ¿ha intentado obtener una transacción desde allí y luego volverla a generar?

+0

Sí, creé mi propio objeto ISession desde una llamada SessionFactory.OpenSession(). A partir de ahí comencé una transacción y luego de la llamada a VerifyTheMappings() intenté deshacer la transacción y obtuve un error. Publicaré el fragmento de código completo a continuación. –

2

Creo que el problema aquí es VerifyTheMappings() llama a TransactionSave() que hace un tx.Commit() a la base de datos. Como James indicó, esta técnica parece funcionar muy bien para tirar las técnicas de prueba en la memoria. Esto no funcionaría en el caso de correlaciones de prueba con una base de datos heredada.

0

creo que es muy importante hacer esta prueba con su DB real, para ver que la definición de sus tablas es correcta, así que desarrollé una clase muy simple que realizaba una prueba Crud en una entidad mapeada y retrocedía a el fin;

internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable 
{ 
    public T EntityToTest { get; set; } 
    public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; } 
    public GenericMappingTesterWithRealDB() 
    { 
     Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null); 
    } 

    public void RunTest() 
    { 
     using (ISession session = SessionFactoryProvider.NewSession) 
     using (ITransaction transaction = session.BeginTransaction()) 
     { 
      try 
      { 
       session.Save(EntityToTest); 
       var item = session.Get<T>(EntityToTest.ID); 
       Assert.IsNotNull(item); 
       if (PerformEntityManipulationBeforeUpdate != null) 
       { 
        PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest); 
       } 
       session.Update(EntityToTest); 
       session.Delete(EntityToTest); 
       session.Save(EntityToTest); 
      } 
      catch (Exception e) 
      { 
       Assert.Fail(e.Message, e.StackTrace); 
      } 
      finally 
      { 
       transaction.Rollback(); 
      } 
     } 
    } 
} 

IIdentifiable en mi proyecto es la interfaz más básica de mis entidades

la clase está utilizando el nunit.framework pero u puede hacerlo con cada marco de pruebas u desea

sessionfactoryprovider necesita para abastecer la ISession obj

aquí es una muestra de su uso

/// <summary> 
/// Testing the mapping of our entities. 
/// there must be a server connection for this kind of test. 
/// </summary> 
[TestFixture] 
internal class someMappingTest 
{ 
    [Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")] 
    [Timeout(20000)] 
    public void checkthatMappingWorks() 
    { 
     // creatw the new entity 
     TestedType testOn = new TestedType(); 

     // set the initialization values 
     testOn.Name = "TestProfileExecution"; 

     // create the test object 
     new GenericMappingTesterWithRealDB<TestedType> 
     { 
      // assign an entity 
      EntityToTest = testOn, 

      // assign new values for update check 
      PerformEntityManipulationBeforeUpdate = 
       delegate(TestedType testedTypeBeingTested) 
        { 
         return testedTypeBeingTested.Name = "Updateing Test"; 
        } 
     }. 
     // call run test to perform the mapping test. 
     RunTest(); 

    } 
} 
1

Configuración IsolationLevel.ReadUncommitted va a funcionar, pero sólo de paso, ya que todo lo que hace es decirle a la sesión que se puede leer sin necesidad de una nueva transacción (una lectura sucia, en DBMS jerga) - por lo que Session.Transaction.Commit() no tiene que comprometer una transacción de base de datos antes de que se lea la verificación. ¡Esto también significa que no necesariamente está probando lo que piensas que está probando! (También creo que esto tiene soporte probablemente cuestionable entre las bases de datos no MS SQL). La respuesta de leebrandt funciona debido a la reversión explícita, no al nivel de aislamiento (nb. En el momento de la respuesta, esto ayudó más de lo que lo hace ahora, consulte la nota a continuación).

La buena noticia es que la forma correcta de hacerlo es simplemente deshacer la transacción manualmente. Session.Transaction se reemplaza automáticamente cada vez que se compromete la transacción, por lo que deberá mantener una referencia al mismo, y deberá abrir uno explícitamente de todos modos, ya que TransactionalSave() comprueba si la transacción actual está activa y crea (¡y dispone!) Su propio si no. Me suelen probar todas mis asignaciones en el mismo aparato, donde también comprobar la creación de la fábrica y algunas otras cosas persistencia de infraestructura, por lo que me gusta el siguiente patrón de esto para mantener la tubería hacia abajo:

class TestFixture { 
    static ISessionFactory factory = CreateMyFactorySomehowHere(); 

    ISession session; 
    ITransaction tx; 

    public void Setup() 
    { 
     session = factory.OpenSession(); 
     tx = session.BeginTransaction(); 
    } 

    public void Cleanup() 
    { 
     tx.Rollback(); 
     tx.Dispose(); 
     session.Close(); 
    } 

    public void TestAMappingForSomething() 
    { 
     var spec = new PersistenceSpecification<Something> (session); 
     spec.VerifyTheMappings(); 
    } 
} 

Obviamente, inserte su propia terminología y atributos/anotaciones específicos del marco de prueba donde sea, pero se entiende la idea.


he notado ahora la edad que esta pregunta es: este comportamiento se fijó en this commit en julio 09, para manejar las transacciones existentes muy bien por lo que los trabajos anteriores! Claramente esto es lo que estabas haciendo originalmente de todos modos.

Cuestiones relacionadas