Try fast search NHibernate

31 March 2009

Ensuring updates on Flush

Two assertions:

  • I like the dirty check of NHibernate because I can work without worry about explicit updates.
  • I don’t like the dirty check of NHibernate because I can’t have full control about updates and the dirty-check, in my application, is slow.

Both true ? Can we implements something to prevent Auto-Dirty-Check on flush ?

Domain

Domain

Test

Part of the configuration
<property name="generate_statistics">true</property>

I’m using NHibernate SessionFactory statistics to check some operation.

Populating DB
public void FillDb()
{
sessionFactory.EncloseInTransaction(session =>
{
for (int i = 0; i < 100; i++)
{
var reptileFamily = ReptileFamilyBuilder
.StartRecording()
.WithChildren(2)
.Build();

session.Save(ReptilesfamilyEntityName, reptileFamily);
}

for (int i = 0; i < 100; i++)
{
var humanFamily = HumanFamilyBuilder
.StartRecording()
.WithChildren(1)
.Build();

session.Save(HumanfamilyEntityName, humanFamily);
}
});
}

In a transaction I’m creating 100 Family<Reptile> and 100 Family<Human>. Each Family<Reptile> has a father, a mother and two children (total 5 entities). Each Family<Human> has a father, a mother and one children (total 4 entities). The DB will have 900 entities states (the Family is mapped to use all cascade).

The test
public void ShouldNotAutoUpdate()
{
FillDb();

using (ISession s = sessionFactory.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var reptiles = s.CreateQuery("from ReptilesFamily")
.Future<Family<Reptile>>();

var humans = s.CreateQuery("from HumanFamily")
.Future<Family<Human>>();

ModifyAll(reptiles);
ModifyAll(humans);

sessionFactory.Statistics.Clear();

s.Update(ReptilesfamilyEntityName, reptiles.First());
s.Update(HumanfamilyEntityName, humans.First());

tx.Commit();
}

sessionFactory.Statistics.EntityUpdateCount
.Should().Be.Equal(7);

CleanDb();
}

After populate the DB I’m loading and modifying all instances of Human and Reptile (that mean 400 entities of Reptile and 300 entities of Human). The result is that I have 900 entities loaded and 700 modified in a session.

In the two session.Update I’m calling explicitly the update only for the first Family<Reptile> and the first Family<Human> (that mean only for 7 entities).

The test assertion is:

sessionFactory.Statistics.EntityUpdateCount
.Should().Be.Equal(7);

The summary is that even if I have 700 modified entities,  NHibernate should update only 7 entities because I call explicitly Update only for two families.

How change the default behavior

If you are familiar with NH2.0.0 and above you can imagine which will be the place where look… yes, Events/Listeners.

As first the configuration where you can see which events I’m using and which listeners and in which order will be executed.

            <event type="delete">

<
listener
class="DisableAutoDirtyCheck.PreDeleteEventListener, DisableAutoDirtyCheck"/>

<
listener
class="NHibernate.Event.Default.DefaultDeleteEventListener, NHibernate"/>

</
event>

<
event type="update">

<
listener
class="DisableAutoDirtyCheck.PreUpdateEventListener, DisableAutoDirtyCheck"/>

<
listener
class="NHibernate.Event.Default.DefaultUpdateEventListener, NHibernate"/>
</
event>

<
listener
class="DisableAutoDirtyCheck.PostLoadEventListener, DisableAutoDirtyCheck"
type="post-load"/>
Tricks

The real Dirty-Check happen in the DefaultFlushEntityEventListener using the session state. All entities loaded, in what is commonly named session-cache, are loaded in the Session.PersistenceContext. To be very short the PersistenceContext is a set of EntityEntry. An EntityEntry is the responsible to maintain the state and the Status of an entity.

The real trick behind all this matter is this extension:

public static class Extensions
{
private static readonly FieldInfo statusFieldInfo =
typeof (EntityEntry).GetField("status",BindingFlags.NonPublic | BindingFlags.Instance);

public static void BackSetStatus(this EntityEntry entry, Status status)
{
statusFieldInfo.SetValue(entry, status);
}
}
Listeners implementation
public class PostLoadEventListener : IPostLoadEventListener
{
public void OnPostLoad(PostLoadEvent @event)
{
EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);
entry.BackSetStatus(Status.ReadOnly);
}
}

After load an entity, the instance is marked as ReadOnly but maintaining the loaded-state (maintain the loaded state is the reason to use the above trick).

public class PreUpdateEventListener : ISaveOrUpdateEventListener
{
public static readonly CascadingAction ResetReadOnly = new ResetReadOnlyCascadeAction();

public void OnSaveOrUpdate(SaveOrUpdateEvent @event)
{
var session = @event.Session;
EntityEntry entry = session.PersistenceContext.GetEntry(@event.Entity);
if (entry != null && entry.Persister.IsMutable && entry.Status == Status.ReadOnly)
{
entry.BackSetStatus(Status.Loaded);
CascadeOnUpdate(@event, entry.Persister, @event.Entity);
}
}

private static void CascadeOnUpdate(SaveOrUpdateEvent @event, IEntityPersister persister, object entity)
{
IEventSource source = @event.Session;
source.PersistenceContext.IncrementCascadeLevel();
try
{
new Cascade(ResetReadOnly, CascadePoint.BeforeFlush, source).CascadeOn(persister, entity);
}
finally
{
source.PersistenceContext.DecrementCascadeLevel();
}
}
}

When an entity is explicitly updated, before execute the default behavior I’m restoring the Status of the loaded entity (obviously for all the entities loaded an involved in cascade actions).

Conclusion

Can we have full control over NHibernate’s updates  ?     Yes, we can!! ;-)

Code available here.



kick it on DotNetKicks.com

25 March 2009

NUnitEx : assertion.Should().Be.Fluent();

Continuing the implementation from the previous post, NUnitEx look nice and useful.

I tried to check available features “simulating” the use of NUnitEx assertions inside some frameworks where I am involved.

Before show some new examples, I would thank to Davy, Steve and Gustavo for their help in the API definition (by the way if you find something wrong is only my fault).

New constrain chain

string something = "something";

something.Should()
.StartWith("so")
.And
.EndWith("ing")
.And
.Contain("meth");

something.Should()
.StartWith("so")
.And
.EndWith("ing")
.And
.Not.Contain("body");
var ints = new[] { 1, 2, 3 };
ints.Should()
.Have.SameSequenceAs(new[] { 1, 2, 3 })
.And
.Not.Have.SameSequenceAs(new[] { 3, 2, 1 })
.And
.Not.Be.Null()
.And
.Not.Be.Empty();

New ValueOf

ValueOf is a new property available in certain cases when we know the type during “constraints-chain”.

var instanceOfClass = new D2();

instanceOfClass.Should()
.Be.AssignableTo<D1>()
.And.Be.OfType<D2>()
.And.ValueOf.StringProperty
.Should().Be.Null();

New Actions constrains

(new Action(() => new AClass(null)))
.Should().Throw<ArgumentNullException>()
.And.
ValueOf.ParamName
.Should().Be.Equal("obj");

Obviously you can mix the classic assertion with NUnitEx API:

Assert.Throws<ArgumentNullException>(() => new AClass(null))
.ParamName.Should().Be.Equal("obj");

Exceptions assertions

These are some examples using NUnitEx syntax + LINQ

Giving:

public class SillyClass
{
public SillyClass(object obj)
{
if (obj == null)
{
throw new ArgumentException("mess",
new ArgumentNullException("mess null",
new ArgumentOutOfRangeException("obj")));
}
}
}

possible assertions are:

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerExceptions()
.OfType<ArgumentOutOfRangeException>().First()
.ParamName.Should().Be.Equal("obj");

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerExceptions().Select(e => e.GetType())
.Should().Contain(typeof(ArgumentOutOfRangeException));

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerException
.Should().Be.OfType<ArgumentNullException>()
.And.ValueOf.Message.Should().Be.Equal("mess null");

What next

I didn’t see so many interest to have API available for NUnit2.4.8 so I would jump this step.

In term of NUnitEx internals what will be interesting is its implementation for xUnit and MbUnit, if somebody have interest I’m happy to share the project (somebody named Lars for example ;-) ).


kick it on DotNetKicks.com

22 March 2009

NUnitEx : News

After the very good feed back of the previous post, the new look of NUnitEx is:

Bollean Constraints:

actual.Should().Be.True();
actual.Should().Be.False();

Object Constraints:

actual.Should().Be.Equal(something);
actual.Should().Be.Null();
actual.Should().Be.SameInstanceAs(something);
actual.Should().Be.InstanceOf<SomeClass>();
actual.Should().Be.OfType<SomeClass>();
actual.Should().Be.AssignableFrom<SomeClass>();
actual.Should().Be.AssignableTo<SomeClass>(); 
actual.Should().Be.BinarySerializable();
actual.Should().Be.XmlSerializable();

Each constraint allow negation as : actual.Should().Not.Be.Equal(something);

Struct/Struct? Constraints

actual.Should().Be.LessThan(maxvalue);
actual.Should().Be.GreaterThan(minvalue);
actual.Should().Be.LessThanOrEqual(maxvalue);
actual.Should().Be.GreaterThanOrEqual(minvalue);
actual.Should().Be.Equal(actual);

Each constraint allow negation as : actual.Should().Not.Be.Equal(minvalue);

String Constraints

actual.Should().Be.Equal("a");
actual.Should().Be.Empty();
actual.Should().Be.Null();
actual.Should().Contain("some");
actual.Should().StartWith("some");
actual.Should().EndWith("ing");
actual.Should().Match("ome[tT]h");

Each constraint allow negation as : actual.Should().Not.Be.Equal("a string");

Enumerable Constraints

actual.Should().Have.SameSequenceAs(new[] { 1, 2, 3 });
actual.Should().Be.Null();
actual.Should().Be.Empty();
actual.Should().Be.SameInstanceAs(expected);
actual.Should().Contain(expected);
actual.Should().Have.SameValuesAs(new[] { 3, 2, 1 });
actual.Should().Have.UniqueValues();
actual.Should().Be.SubsetOf(new[] { 1, 2, 3, 4 });
actual.Should().Be.Ordered();

Each constraint allow negation as : actual.Should().Not.Have.SameSequenceAs(new[] { 3, 1 });

What next

Allow concatenation:

actual.Should().Not.Be.Null()
.And.Contain(expected)
.And.Have.UniqueValues();

Perhaps some new custom assertions for IEnumerable<T>.

Some constraints for Action as, for example:

new Action(() => new Something(null)).Should().Throw<ArgumentNullException>()
.Where.Message.Should().StartsWith("The parameter");

At last I’m studying the possibility to have the same API for others Unit-Test frameworks as xUnit, MbUnit and so on.

Remember that your feedback is very important.



kick it on DotNetKicks.com