NHibernate Stadistics – Go Fabio Go
Fabio Maulo, figura importatísima en el NHibernate team, siempre estuvo contribuyendo con parches y features, y ahora está portando muchas features de Hibernate 3.2.5 GA -ya lo veiamos aquí y aquí. Y ahora está por va tras Hibernate Statistics que podemos verlo en acción en este post.
La mejor parte del post es cuando realiza el calculo del caché hit-ratio:
double queryCacheHitCount = stats.getQueryCacheHitCount(); double queryCacheMissCount = stats.getQueryCacheMissCount(); double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
Les gustó ?
NHibernate EventListeners
En este post comentábamos sobre StatelessSession como nueva feature de NHibernate, ahora bien, otra de las features disponibles en el trunk de NHibernate (portada de Hibernate) son los EventListeners. Con ellos podemos interceptar acciones llevadas a cabo por NHibernate antes o después de que se realicen como por ejemplo un Save, Update, Load, Flush entre otras.
En este ejemplo vamos a realizar acciones antes y después de que se ejecute el comando Save(object).
Definimos el EventListener:
public class MySaveEventListener : NHibernate.Event.Default.DefaultSaveOrUpdateEventListener { protected override void CascadeBeforeSave(IEventSource source, IEntityPersister persister, object entity, object anything) { Console.WriteLine("Before Save the entity " + entity); base.CascadeBeforeSave(source,persister,entity,anything); } protected override void CascadeAfterSave(IEventSource source, IEntityPersister persister, object entity, object anything) { Console.WriteLine("After Save the entity " + entity); base.CascadeAfterSave(source,persister,entity,anything); } }
Y para configurarlo programaticamente con el SessionFactory hacemos:
Configuration cfg = new Configuration(); cfg.Configure("hibernate.cfg.xml"); cfg.SetListener(ListenerType.Save, new MySaveEventListener());
Ahora realizamos un Save:
using (ISessionFactory sf = cfg.BuildSessionFactory()) { using (ISession session = sf.OpenSession()) { Foo f1 = new Foo(); f1.Misc = "m1"; f1.Name = "n1"; session.Save(f1); session.Flush(); } }
Y este es el resultado de la consola, incluyendo el SQL que NHibernate generó:
Before Save the entity Entity Foo:0:n1 NHibernate: INSERT INTO Foo (Name, Misc) VALUES (@p0, @p1); select SCOPE_IDENTIT Y(); @p0 = 'n1', @p1 = 'm1' After Save the entity Entity Foo:1:n1
Foo tiene un object id como Integer e Identity. Antes de guardarse la property Id tiene como valor 0, despues del Save tiene como valor 1 (debido a que es el primer objeto en la base).
NHibernate StatelessSession: sin caché de primer nivel
En la próxima release de NHibernate estará disponible esta feature portada desde Hibernate por Fabio Maulo, los que deseen usarla en este momento deben hacerlo desde el trunk.
Qué hace? IStatelessSession es la interfaz que debemos usar para conseguir sesiones de NHibernate que no posean caché de primer nivel. Debemos recordar que la caché de primer nivel en ISession es obligatoria y siempre la usamos cuando trabajamos con NHibernate. Trabajar sin ella era imposible hasta ahora.
Para que sirve? Especialmente hecho para realizar operaciones en masa contra la base de datos.
Miremos este código:
using (ISessionFactory sf = cfg.BuildSessionFactory()) { using (IStatelessSession session = sf.OpenStatelessSession()) { using (ITransaction tx = session.BeginTransaction()) { Foo f = new Foo(); f.Id = 1; f.Misc = "m1"; f.Name = "n1"; session.Insert(f); f.Id = 2; session.Insert(f); tx.Commit(); Debug.Assert(session.CreateQuery("from Foo") .List().Count == 2); } } }
Como verán el objeto f fué creado 1 vez y mandado a persistir dos veces con el Id cambiado, primero con ’1′ y luego con ’2′. En un escenario común con caché de primer nivel esto no funcionaría, ya que NHibernate “recordaría” al objeto f desde que fué guardado por primera vez. Y como resultado en la base habrán 2 objetos guardados.
Notese también que al guardar el objeto realizamos un Insert en vez de un Save. Las inferzaces ISession e IStatelessSession no se relacionan para nada.
Para que este ejemplo funcione con cache de primer nivel es obligatorio hacer que NHibernate se “olvide” del objeto que acabamos de guardar llamando al metodo Evit(object) -método no disponible en IStatelessSession como podrán intuir. Y se vería así:
using (ISessionFactory sf = cfg.BuildSessionFactory()) { using (ISession session = sf.OpenSession()) { using (ITransaction tx = session.BeginTransaction()) { Foo f1 = new Foo(); f1.Id = 1; f1.Misc = "m1"; f1.Name = "n1"; session.Save(f1); //Eliminamos el objeto de la cache session.Evict(f1); f1.Id = 2; session.Save(f1); tx.Commit(); Debug.Assert(session. CreateQuery("from Foo").List().Count == 2); } } }
Recursos:
Sintaxis en Rhino Mocks
Rhino Mocks es un excelente framework para realizar mocking en nuestras pruebas unitarias. Una de las buenas razones para usarlo es que no hace uso de strings para hardcodear los nombres de tipos, metodos, properties, etc, reduciendo entonces la posibilidad de error en tiempo de ejecución.
Despues de la versión 3.0.5 liberada hace un tiempo, la sintaxis para realizar mocking quedó un poco más fácil de leer y con menos posibilidades de error.
Al mirar este screencastintroductorio a Rhino Mocks te darás cuenta como se hacía antes. También salta a la vista que cualquiera puede olvidar de llamar al método VerifyCall() dejando libre la posibilidad de errores.
[Test]
public void SigmoidalFunctionDerivative()
{
INet net = Mock<INet>();
ITransferFunction tf = new SigmoidalTransferFuncion();
Expect
.Call(net.Value)
.Return(5.0);
Mocks.ReplayAll();
//f'(net) = y * ( 1 - y )
Assert.Equal(0.006648057, Math.Round(tf.Derivate(net), 9));
Mocks.VerifyAll();
}
Una forma más clara de hacerlo es:
[Test]
public void SigmoidalFunctionDerivative()
{
INet net = Mock<INet>();
ITransferFunction tf = new SigmoidalTransferFuncion();
using (Mocks.Record())
{
Expect
.Call(net.Value)
.Return(5.0);
}
using (Mocks.Playback())
{
//f'(net) = y * ( 1 - y )
Assert.Equal(0.006648057, Math.Round(tf.Derivate(net), 9));
}
}
En estos ejemplos estoy usando un helper para el metodo Mock<T>() y la property MockRepository Mocks {get; set; }. Nada de otro mundo, y se pueda mejorar:
public class TestFixtureBase { private MockRepository mocks; public MockRepository Mocks { get { if (mocks == null) { mocks = new MockRepository(); } return mocks; } } public T Mock<T>() { return Mocks.CreateMock<T>(); } }
Existe aún así otra manera de hacer lo mismo, en vez de using, hacemos uso de delegados y la clase With (no es el de VB pero creo que por ahí vino la idea):
[Test]
public void SigmoidalFunctionDerivative()
{
INet net = Mock<INet>();
ITransferFunction tf = new SigmoidalTransferFuncion();
With
.Mocks(Mocks)
.Expecting(delegate
{
Expect
.Call(net.Value)
.Return(5.0);
})
.Verify(delegate
{
//f'(net) = y * ( 1 - y )
Assert.Equal(0.006648057,
Math.Round(tf.Derivate(net), 9));
});
}
Particulamente elijo la opción 2, el usar la sintaxis con using.
xUnit.Net – Excepciones Esperadas
Para los que no estaban enterados, el autor de NUnit saca a la luz este nuevo Framework de unit testing. La intención de un nuevo Framework de Testing para .Net parecería querer reinventar la rueda, pero no lo es. Muchas practicas que se hacían en NUnit (y muchas de estas heredadas de JUnit) no van a estar disponibles -para bien de nuestro código según argumenta James Newkirk.
Uno de los cambios: No va más el ExpectedException. Y el argumento de su desaparición es válido: Puede que una linea de codigo arroje esa excepción, y no sea la linea de codigo que estamos esperando a que lo haga. Ahora debemos hacerlos así:
[Test]
public void NeuronNotConnected()
{
INeuron n = new SigmoidalNeuron();
Assert.Throws<BpnException>(
delegate
{
double foo = n.Net.Value;
});
}
Esta es la pagina del proyecto http://www.codeplex.com/xunit.
Y para correr los test con Visual Studio pueden usar http://www.testdriven.net/