In the previous articles of this series we saw how to initialize the environment to use NHibernate.Envers and how to query it for getting information about the revisions it creates.

It’s now time to focus on the methods that allows us to actually retrieve the different revisions details (such as the state of the whole entity at a given revision and similar) and the history of the modifications an object has been subject to.

Once again our operation entry point is the IAuditReader object, but this time we will use its CreateQuery() method.

CreateQuery() returns an AuditQueryCreator object that exposes all the functions we need to perform search queries against the database; AuditQueryCreator provides a series of normal methods that specifies the entity we want to look for using a typeof(T) argument as their first parameter. It also exposes the generic version of those methods and we’ll focus our attention on those (because I hate to write down typeof() everywhere, if I can avoid it), the non generic functions are however very similar and their usage should be clear anyway.

Each of these generic functions will return an IEntityAuditQuery<T> object, these kind of objects work exactly like the usual NHibernate ICriteria API: we can add search criteria to filter out the results, order the results, perform pagination, add projections and so on.

To get the results from an IEntityAuditQuery object we can use two methods:

  • .Single() - it will return a single instance of the class representing the result, or an exception if more than one result is available.
  • .Results() - it will return an enumeration of the class representing the result.

Actually we have tree different methods we can use to build up queries against our revision database tables, let’s see them in detail:

ForEntitiesAtRevision<T>

Method signature: IEntityAuditQuery<T> ForEntitiesAtRevision<T>(long revision)

The query built using this method will ultimately return to us an IEnumerable<T> (or a single instance of T, or the result of a projection), the enumeration will contains all the instances of the specified type of entity at the requested revision; one or more filtering conditions can be added:

[Test]
public void ForEntitiesAtRevision()
{
	using (ISession s = _nh.SessionFactory.OpenSession())
	{
		// look for a non existing revision
		using (var tx = s.BeginTransaction())
		{
			IAuditReader auditReader = s.Auditer();
			var rev = auditReader.CreateQuery().ForEntitiesAtRevision<Person>(1).Results();
			Assert.IsNotNull(rev);
			Assert.That(rev.Count() == 2);
			// print the data on the console
			Utils.PrintEntityList(rev);
			tx.Commit();
		}
	}
}

in line 10 I am asking to see all the entities of type Person at their revision number 1, given my test domain this is the result:

***** NHibernate.Envers.Tests.Querying.Test2.ForEntitiesAtRevision
System.Linq.Enumerable+WhereSelectListIterator`2[System.Collections.IDictionary,NHibernate.Envers.Tests.Domain.Person] , count: 2
Id: 1, Name: Jhon, Surname: Doe, Note: 
Games:
Id: 1, Name: g0, Type: , Rating: 0, Note: note0
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
---

Id: 2, Name: Jane, Surname: Doe, Note: 
Games:
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
Id: 3, Name: g2, Type: , Rating: 2, Note: note2
---

As you can see I have two entity at that revision number; if I wanted to filter out the results I could have written something similar in line 10:

var rev = auditReader.CreateQuery().ForEntitiesAtRevision<Person>(1)
.Add(AuditEntity.Property("Name").Eq("Jhon")).Results();

ForRevisionsOf<T>

Method signature: IEntityAuditQuery<T> ForRevisionsOf<T>(bool includesDeleted)

The query built using this method will ultimately return to us an IEnumerable<T> (or a single instance of T, or the result of a projection), the enumeration will contains all the instances of the specified type of entity; one or more filtering conditions can be added.

If includesDeleted is set to true the revisions where the entities were deleted will be returned (the default value is ‘false’).

The results of the query will be sorted in ascending order by the revision number, unless an order or projection is added.

Here’s an example:

[Test]
public void ForRevisionsOf()
{
	using (ISession s = _nh.SessionFactory.OpenSession())
	{
		// look for a non existing revision
		using (var tx = s.BeginTransaction())
		{
			IAuditReader auditReader = s.Auditer();
			var rev = auditReader.CreateQuery().ForRevisionsOf<Person>(true).Results();
			Assert.IsNotNull(rev);
			Utils.PrintEntityList(rev);
			tx.Commit();
		}
	}
}

This is the ‘messy’ result:

***** NHibernate.Envers.Tests.Querying.Test2.ForRevisionsOf
System.Linq.Enumerable+WhereSelectListIterator`2[System.Collections.IDictionary,NHibernate.Envers.Tests.Domain.Person] , count: 4
Id: 1, Name: Jhon, Surname: Doe, Note: 
Games:
Id: 1, Name: g0, Type: , Rating: 0, Note: note0
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
---

Id: 2, Name: Jane, Surname: Doe, Note: 
Games:
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
Id: 3, Name: g2, Type: , Rating: 2, Note: note2
---

Id: 2, Name: Jane, Surname: Doe, Note: Modified
Games:
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
Id: 3, Name: g2, Type: , Rating: 2, Note: note2
---

Id: 1, Name: Jhon, Surname: Doe, Note: Modified
Games:
Id: 1, Name: g0, Type: , Rating: 0, Note: note0
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
---

To be honest this function is not so useful without adding some filters.

ForHistoryOf<TEntity>

Method signature: IEntityAuditQuery<IRevisionEntityInfo<TEntity, DefaultRevisionEntity>> ForHistoryOf<TEntity>(bool includesDeleted)

The query built using this method will ultimately return to us an IEnumerable<IRevisionEntityInfo<TEntity, DefaultRevisionEntity>> (or a single instance of IRevisionEntityInfo<TEntity, DefaultRevisionEntity>, or the result of a projection), the enumeration will contains all the revision information of the instances of the specified type of entity; one or more filtering conditions can be added.

If includesDeleted is set to true the revisions where the entities were deleted will be returned (the default value is ‘false’).

The results of the query will be sorted in ascending order by the revision number, unless an order or projection is added.

This function is extremely useful because you can obtain detailed information about the revision number, the timestamp and the kind of operation (added, modified, deleted) alongside with the snapshot of the entity itself; all these information are available through an implementation of the IRevisionEntityInfo<TEntity, TRevisionEntity> interface.

Here’s an example:

[Test]
public void ForHistoryOf()
{
	using (ISession s = _nh.SessionFactory.OpenSession())
	{
		// look for a non existing revision
		using (var tx = s.BeginTransaction())
		{
			IAuditReader auditReader = s.Auditer();
			var rev = auditReader.CreateQuery().ForHistoryOf<Person>(true).Results();
			Assert.IsNotNull(rev);
			Utils.PrintRevisionInfoList(rev);
			tx.Commit();
		}
	}
}

And here’s the results I get with my domain:

***** NHibernate.Envers.Tests.Querying.Test2.ForHistoryOf
System.Linq.Enumerable+<CastIterator>d__aa`1[NHibernate.Envers.Query.IRevisionEntityInfo`2[NHibernate.Envers.Tests.Domain.Person,NHibernate.Envers.DefaultRevisionEntity]] , count: 4
Rev=1 Timestamp=2011-07-13T14:19:32.1470000 Op=Added
Id: 1, Name: Jhon, Surname: Doe, Note: 
Games:
Id: 1, Name: g0, Type: , Rating: 0, Note: note0
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
---

Rev=1 Timestamp=2011-07-13T14:19:32.1470000 Op=Added
Id: 2, Name: Jane, Surname: Doe, Note: 
Games:
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
Id: 3, Name: g2, Type: , Rating: 2, Note: note2
---

Rev=2 Timestamp=2011-07-13T14:19:32.1630000 Op=Modified
Id: 2, Name: Jane, Surname: Doe, Note: Modified
Games:
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
Id: 3, Name: g2, Type: , Rating: 2, Note: note2
---

Rev=2 Timestamp=2011-07-13T14:19:32.1630000 Op=Modified
Id: 1, Name: Jhon, Surname: Doe, Note: Modified
Games:
Id: 1, Name: g0, Type: , Rating: 0, Note: note0
Id: 2, Name: g1, Type: , Rating: 1, Note: note1
---

With this post we have covered pretty much all the basic functionalities that NHibernate.Envers offers us to query the revision database tables, take a look at the new examples in the test solution if you want to see it in action live:

Cya next.

Related Content