Audit log with nHibernate

An audit log is one of the most requested features in small to enterprise level business applications. I am sure you will have been in a situation where a business manager has requested an audit report for a particular business process or event. The reality is that we live in an evolving world and everyday something significant happens around us. Most business applications respond to these events by writing a record indicating the sequence of events. Let’s look at the following questions:

  • What has happened?
  • When did it happen?
  • Who recorded the change in the system?
  • What was the old state?
  • What is the new state?

As you may noticed, this is very simple form of an audit log. The  Audit log could be stored in any file format  on any physical media. The most common format to store an audit log is a delimited file format, however, well structured XML or database table could also be very effective . There are many different ways to implement an audit log, the implementation you use will be heavily influenced by the requirements of the business. In my opinion, the audit log should be kept very simple and effective whatever the implementation strategy.
NHibernate exposes interceptor and event listener contracts. In this post, I’ll demonstrate an audit log implementation using event listeners. Let’s look at few event listener contracts.

  • IPostInsertEventListener
  • IPostUpdateEventListener
  • IPostDeleteEventListener
  • IPreInsertEventListener
  • IPreUpdateEventListener
  • IPreDeleteEventListener
  • IPersistEventListener
  • ILoadEventListener

These events contracts are available in NHibernate.Event namespace, along with many more not listed here. Let’s implement IPostUpdateEventListener for illustration purposes.

public class AuditUpdateListener : IPostUpdateEventListener
{
        private string GetPropertValue(object[] state, int index)
	{
		var properyValue = state[index];
		if (properyValue == null)
			return string.Empty;
		else if (properyValue is BaseEntity)
			return ((BaseEntity)properyValue).Id.ToString();
		else
			return properyValue.ToString();
	}
	public void OnPostUpdate(PostUpdateEvent @event)
	{
		if (@event.Entity is AuditTrailEntry)
			throw new InvalidOperationException("Audit trail entry is not allowed to update.");

		var dirtyPropertyIndexes = @event.Persister.FindDirty(@event.State, @event.OldState, @event.Entity, @event.Session);
		var session = @event.Session.GetSession(EntityMode.Poco);
		foreach (var index in dirtyPropertyIndexes)
		{
			var propertyOldValue = GetPropertValue(@event.OldState, index);
			var propertyNewValue = GetPropertValue(@event.State, index);
			session.Save(new AuditTrailEntry
			{
				EntityFullName = @event.Entity.GetType().FullName,
				EntityShortName = @event.Entity.GetType().Name,
				EntityId = (int)@event.Id,
				PropertyName = @event.Persister.PropertyNames[index],
				AuditType = "Update",
				AuditDateTime = DateTime.Now,
				PropertyNewValue = propertyNewValue,
				PropertyOldValue = propertyOldValue,
				AuditedBy = Environment.UserName
			});
		}
	}
}

You may have noticed that I am storing the audit log in a database table. You could customise the physical format to meet your requirements. The above code will log all changes, however, you could log selected entities or properties by decorating them with attributes. Audit logs are very simple and most importantly, very easy to implement using nHibernate.

, , ,

Leave a comment

Unit of work and Repository Pattern with nHibernate and linq – II

The code for this post can be found here

This is a second post in a series on unit of work and repository pattern. In Part 1 I have discussed unit of work and generic repository pattern with linq. In this part, I’ll be unit testing the repository pattern with a simple model to achieve persistence ignorance. Lets look at the simple model that I am using for illustration purposes.

Let’s look at the key sections of unit test code (available in BlogPostTest).

private ISessionFactory CreateConfiguration()
{
	IPersistenceConfigurer persistenceConfigurer =
		MsSqlConfiguration.MsSql2008.ConnectionString
				(c => c.FromConnectionStringWithKey("RepositoryPatternDemo"));

	Configuration cfg = persistenceConfigurer.ConfigureProperties(new Configuration());

	var persistenceModel = new PersistenceModel();
	var assembly = typeof(BlogPostMap).Assembly;
	persistenceModel.AddMappingsFromAssembly(assembly);
	persistenceModel.Configure(cfg);
	return cfg.BuildSessionFactory();
}

As you may have noticed, I am using SQL Server 2008 database to test NHibernate mapping. I know you are probably thinking, why do we need database to test mappings. Well, the answer is that you don’t need a functional database to automate tests for properties, cascades and mappings with NHibernate. Ayende has written a very good post on unit testing NHibernate mappings in memory.  

[SetUp]
public void Init()
{
	_unitOfWork = new UnitOfWork(_sessionFactory);
	_repository = new Repository<int, BlogPost>(_unitOfWork.Session);
	_post = new BlogPostBuilder()
			.Title("Repository Pattern")
			.SubTitle("agilefreak.workpress.com")
			.Text("Unit of work and repository pattern rocks")
			.PublicationDate(DateTime.Now)
			.AuthorName("Naz Ali")
			.Build();
}

I have initialised few members in above code. Let’s look one of the tests.

[Test]
public void Can_retrieve_blogpost_from_repository_using_linq_expression()
{
	var comment = GetComment();
	_post.AddCommant(comment);
	_repository.Add(_post);

	var savedPost = _repository.FilterBy(p => p.AuthorName == "Naz Ali"
					&& p.Comments.Where(c => c.Rating > 5).Count() > 0
				);
	Assert.That(savedPost.Count(), Is.EqualTo(1), "Repository must return a post for given criteria");
	Assert.That(savedPost.First().Id, Is.EqualTo(_post.Id), "Repository returned invalid post for given criteria");
	Assert.That(savedPost.First().Comments.Count(), Is.EqualTo(1), "BlogPost must contain one comment");
}

The highlighted lines in the above code are very interesting to note here. A linq expression is passed to repository and collection result is returned. If you want to understand how nHibernate queries work, Ayende has great post on the topic available here. Here is the full source code for a test class.

namespace RepositoryPattern.Tests
{
	[TestFixture]
	public class BlogPostTest
	{
		static ISessionFactory _sessionFactory = null;
		UnitOfWork _unitOfWork = null;
		BlogPost _post;
		Repository<int, BlogPost> _repository;

		[TestFixtureSetUp]
		public void Setup()
		{
			_sessionFactory = CreateConfiguration();
		}

		[SetUp]
		public void Init()
		{
			_unitOfWork = new UnitOfWork(_sessionFactory);
			_repository = new Repository<int, BlogPost>(_unitOfWork.Session);
			_post = new BlogPostBuilder()
						.Title("Repository Pattern")
						.SubTitle("agilefreak.workpress.com")
						.Text("Unit of work and repository pattern rocks")
						.PublicationDate(DateTime.Now)
						.AuthorName("Naz Ali")
						.Build();
		}

		private ISessionFactory CreateConfiguration()
		{
			IPersistenceConfigurer persistenceConfigurer =
				MsSqlConfiguration.MsSql2008.ConnectionString
						(c => c.FromConnectionStringWithKey("RepositoryPatternDemo"));

			Configuration cfg = persistenceConfigurer.ConfigureProperties(new Configuration());

			var persistenceModel = new PersistenceModel();
			var assembly = typeof(BlogPostMap).Assembly;
			persistenceModel.AddMappingsFromAssembly(assembly);
			persistenceModel.Configure(cfg);
			return cfg.BuildSessionFactory();
		}

		private static Comment GetComment()
		{
			return new CommentBuilder()
						.Email("n.ali@blog.com")
						.CommentDate(DateTime.Now)
						.Rating(9)
						.Text("blog comment")
						.Build();
		}

		[TearDown]
		public void RollbackUnitOfWork()
		{
			_unitOfWork.Rollback();
			_unitOfWork.Dispose();
		}

		[TestFixtureTearDown]
		public void CleanUp()
		{
			_sessionFactory.Dispose();
		}

		[Test]
		public void Can_add_blogpost_to_repository()
		{
			_repository.Add(_post);

			var savedPost = _repository.FindBy(_post.Id);
			Assert.That(savedPost, Is.Not.EqualTo(null), "Repository must return valid post for given Id");
			Assert.That(savedPost.Id, Is.EqualTo(_post.Id), "Repository returned invalid post for given Id");
		}

		[Test]
		public void Can_add_blogpost_With_Comments_to_repository()
		{
			var comment = GetComment();
			_post.AddCommant(comment);
			_repository.Add(_post);

			var savedPost = _repository.FindBy(_post.Id);
			Assert.That(savedPost.Comments.Count(), Is.EqualTo(1), "Repository must return post with one comment");
			Assert.That(savedPost.Comments.First().Id, Is.EqualTo(comment.Id));
		}

		[Test]
		public void Can_retrieve_blogpost_from_repository_using_linq_expression()
		{
			var comment = GetComment();
			_post.AddCommant(comment);
			_repository.Add(_post);

			var savedPost = _repository.FilterBy(p => p.AuthorName == "Naz Ali"
											&& p.Comments.Where(c => c.Rating > 5).Count() > 0
										);
			Assert.That(savedPost.Count(), Is.EqualTo(1), "Repository must return a post for given criteria");
			Assert.That(savedPost.First().Id, Is.EqualTo(_post.Id), "Repository returned invalid post for given criteria");
			Assert.That(savedPost.First().Comments.Count(), Is.EqualTo(1), "BlogPost must contain one comment");
		}

		[Test]
		public void Can_Delete_Blogpost_from_repository()
		{
			_repository.Add(_post);
			var savedPost = _repository.FindBy(_post.Id);
			_repository.Delete(savedPost);

			savedPost = _repository.FindBy(_post.Id);
			Assert.That(savedPost, Is.EqualTo(null), "Blog post should be removed from repository");
		}

	}
}

The repository and unit of work patterns are both very powerful if implemented correctly. They provide a lot of flexibility, extensibility and lower the development and testing friction.

The code for this post can be found here

, , , ,

Leave a comment

Unit of work and Repository Pattern with nHibernate and linq

The code for this post can be found here

I just implemented Unit of Work and Repository Pattern on one of my very exciting new project. The project itself architected using Onion architecture and domain model strictly followed Domain Driven Design principles. The implementation of repository pattern provided me flexibility and abstraction to achieve persistence ignorance. After much deliberation and discussion I selected nHibernate and linq as an implementation technology however, the project architecture made it possible to replace this implementation with another technology i.e. Entity Framework.

So, let’s not waste any time and jump into code.

namespace RepositoryPattern.Infrastructure
{
	public interface IEntityKey<TKey>
	{
		TKey Id { get; }
	}
}

IEntityKey interface is part of core infrastructure project and provides generic identity to domain model objects.

namespace RepositoryPattern.Infrastructure
{
	public interface IReadOnlyRepository<TKey, TEntity> where TEntity : class, IEntityKey<TKey>
	{
		IQueryable<TEntity> All();
		TEntity FindBy(Expression<Func<TEntity, bool>> expression);
		IQueryable<TEntity> FilterBy(Expression<Func<TEntity, bool>> expression);
		TEntity FindBy(TKey id);
	}
}

IReadOnlyRepository provides data fetch services and exposes IQueryable interface. As you may already know that  the use of IQueryable interface is controversial. A few well respected people in .NET neighbourhood expressed their data access concerns but I simply love IQueryable. I recommend you investigate these concerns and make your own decision. In my case, this interface only exposed to well defined. controlled and secure service where all these concern are dealt with. FindBy and FilterBy methods accepts Expression. The expression provides flexibility and compatibility with linq but also boosts performance by delaying the execution to achieve optimised queries to database.

namespace RepositoryPattern.Infrastructure
{
	public interface IPersistRepository<TEntity> where TEntity : class
	{
		bool Add(TEntity entity);
		bool Add(IEnumerable<TEntity> items);
		bool Update(TEntity entity);
		bool Delete(TEntity entity);
		bool Delete(IEnumerable<TEntity> entities);
	}
}

IPersistRepository is responsible for writing data to repository. I have separated read and write interfaces as our services may only require read only facility or vice versa.

namespace RepositoryPattern.Infrastructure
{
	public interface IUnitOfWork : IDisposable
	{
		void Commit();
		void Rollback();
	}
}

IUnitOfWork interface implements unit of work pattern, encapsulates the transaction functionalities by exposing commit and rollback operations.

Now we have set-up our infrastructure abstraction, let’s implement infrastructure layer using nHibernate in a separate project called RepositoryPattern.Data.Orm.nHibernate. As I mentioned above, if we like to implement the infrastructure layer using Entity Framework; we could add another project called RepositoryPattern.Data.Orm.EntityFramework. Lets worry about nHibernate implementation using linq for now.

namespace RepositoryPattern.Data.Orm.nHibernate
{
	public class Repository<TKey, T> : IPersistRepository<T>,
		IReadOnlyRepository<TKey, T> where T : class, IEntityKey<TKey>
	{
		private readonly ISession _session;
		public Repository(ISession session)
		{
			_session = session;
		}

		public bool Add(T entity)
		{
			_session.Save(entity);
			return true;
		}

		public bool Add(System.Collections.Generic.IEnumerable<T> items)
		{
			foreach (T item in items)
			{
				_session.Save(item);
			}
			return true;
		}

		public bool Update(T entity)
		{
			_session.Update(entity);
			return true;
		}

		public bool Delete(T entity)
		{
			_session.Delete(entity);
			return true;
		}

		public bool Delete(System.Collections.Generic.IEnumerable<T> entities)
		{
			foreach (T entity in entities)
			{
				_session.Delete(entity);
			}
			return true;
		}

		public IQueryable<T> All()
		{
			return _session.Query<T>();
		}

		public T FindBy(System.Linq.Expressions.Expression<Func<T, bool>> expression)
		{
			return FilterBy(expression).SingleOrDefault();
		}

		public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<Func<T, bool>> expression)
		{
			return All().Where(expression).AsQueryable();
		}

		public T FindBy(TKey id)
		{
			return _session.Get<T>(id);
		}
	}
}

Repository class implements IPersistRepository and IReadOnlyRepository interfaces. This generic implementation provides CRUD operations to all entities and keeps code DRY. The generic functionality could be extended to add entity specific logic by using Decorator Pattern.

The most important part of the implementation is a constructor of Repository class. The instance of ISession is injected via constructor injection. The injection of session instance provides flexibility to control lifetime of object via unit of work or IOC container  i.e. Castle Windsor. The session make full use of linq features supported in nHibernate 3 as shown below.

return _session.Query<T>();

Now we have repository pattern implemented, lets proceed to implement unit of work pattern.

namespace RepositoryPattern.Data.Orm.nHibernate
{
	public class UnitOfWork : IUnitOfWork
	{
		private readonly ISessionFactory _sessionFactory;
		private readonly ITransaction _transaction;
		public ISession Session { get; private set; }

		public UnitOfWork(ISessionFactory sessionFactory)
		{
			_sessionFactory = sessionFactory;
			Session = _sessionFactory.OpenSession();
			Session.FlushMode = FlushMode.Auto;
			_transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted);
		}

		public void Commit()
		{
			if (!_transaction.IsActive)
			{
				throw new InvalidOperationException("Oops! We don't have an active transaction");
			}
			_transaction.Commit();
		}

		public void Rollback()
		{
			if (_transaction.IsActive)
			{
				_transaction.Rollback();
			}
		}

		public void Dispose()
		{
			if (Session.IsOpen)
			{
				Session.Close();
			}
		}
	}
}

The implementation of IUnitOfWork is very simple and clean, again the most important part of the code is constructor. Instance of ISessionFactory is injected via constructor injection. The life time of ISessionFactory is controlled by IOC container.

The implementation of unit of work and repository pattern provides flexibility, extensibility and kept code clean and most importantly DRY.

The code for this post can be found here

, , , ,

6 Comments

Follow

Get every new post delivered to your Inbox.