Geeks With Blogs
// ThomasWeller C#/.NET software development, software integrity, life as a freelancer, and all the rest

This series of posts discusses the use of an Aspect-oriented programming approach for implementing the validation part of a business domain, using the ValidationAspects aspect library, that sits on top of the PostSharp aspect weaver and allows for placing Design by Contract - style attributes on properties and method arguments, as well as for easy runtime validation of an object's state.

This is the second post of a three-part series that discusses the use of these validation aspects throughout the different layers of an application. Each part covers a separate layer as follows:

  • The first part showed in some detail how validation aspects can be applied to the domain layer's business objects both for property value/argument interception and object state validation. We developed a small sample domain there, that's used in the following parts.
  • The second part now (this one) will look at how these validations can be integrated into a persistence layer that's using NHibernate.
  • The third part finally will use the validation aspects to perform (mostly client-sided) validation in an ASP.NET MVC GUI. The xVal framework will be used to bring the server-side validations to the web pages.

 A short recap

In the previous post, I introduced a Person class (our "domain"), which allows for validation of property values, method arguments, and internal object state. Property and  argument values are directly intercepted during invocation of the property setter or method, whereas state validation is done by calling Validate() on the object and then checking the return value. The below tests show the typical usage:

[Test]

public void PersonDoesNotValidate()

{

    var person = CreateInvalidPerson();

 

    ValidationResult result = person.Validate();

 

    Assert.IsFalse(result.IsValid,

                   "Object validation succeeded, but was expected to fail.");

}

 

[Test]

public void CannotSetATooHighAge()

{

    Person person = new Person();

    Assert.Throws<ValidationException>(() => person.Age = 1234);

}

The ValidationResult has also a ValidationException property, which contains the exception(s) that were thrown along the validation way...

Our goal now is to reuse this validation mechanism in the persistence component of our system: A business class shall be saved or updated only if it is valid.

Bringing persistence to the domain

First, let's bring basic persistence capabilities to our project - which until now contains only the "naked" domain - with NHibernate as our underlying storage system. To do this, we add a new [projectname].Persistence (and of course a relating [projectname].Persistence.Test) project to our solution, and reference the [projectname].Domain project as well as all required NH assemblies from it. After adding the *.hbm mapping file for the Person class to the new assembly, these basic tests for saving/updating a Person instance will pass (after doing some additional configuration, of course, but this is not relevant here):

[Test]

public void CanSaveValidPerson()

{

    using (ISession session = this.CreateSession())

    using (ITransaction tx = session.BeginTransaction())

    {

        var person = CreateValidPerson();

 

        Assert.Multiple(() =>

        {

            Assert.AreNotEqual(0, session.Save(person));

            Assert.DoesNotThrow(() => tx.Commit());

        });

    }

}

 

[Test]

public void CanUpdateValidPerson()

{

    using (ISession session = this.CreateSession())

    using (ITransaction tx = session.BeginTransaction())

    {

        var person = CreateValidPerson();

        object id = session.Save(person);

 

        // Remove the object from the session cache, thus effectively simulating two different sessions.

        session.Evict(person); 

 

        person = session.Get<Person>(id);

        person.Age -= 1;

        session.Update(person);

 

        Assert.DoesNotThrow(() => tx.Commit());

    }

}

Now that we have our persistence mechanism basically up and running, we can think about hooking our validation mechanism to it, so that it automatically gets called before an object is saved or updated, and the following tests will also pass:

[Test]

public void CannotSaveInvalidPerson()

{

    using (ISession session = this.CreateSession())

    using (ITransaction tx = session.BeginTransaction())

    {

        var person = CreateValidPerson();

        person.Email = "abc-def@ghi.jkl"; // makes the person invalid...

 

        Assert.Multiple(() =>

        {

            var exception =

                Assert.Throws<HibernateException>(() => session.Save(person));

            Assert.IsInstanceOfType<ValidationException>(exception.InnerException);

        });

 

        Assert.Throws<AssertionFailure>(() => tx.Commit());

    }

}

 

[Test]

public void CannotUpdateInvalidPerson()

{

    using (ISession session = this.CreateSession())

    using (ITransaction tx = session.BeginTransaction())

    {

        var person = CreateValidPerson();

 

        object id = session.Save(person);

 

        // Remove the object from the session cache, thus effectively simulating two different sessions.

        session.Evict(person);

 

        person = session.Get<Person>(id);

        person.Email = "abc-def@ghi.jkl"; // makes the person invalid...

        session.Update(person);

 

        Assert.Multiple(() =>

        {

            var exception =

                Assert.Throws<HibernateException>(() => tx.Commit());

            Assert.IsInstanceOfType<ValidationException>(exception.InnerException);

        });

    }

}

As you can see, trying to save or update an invalid object is expected to throw a HibernateException, with its InnerException set to the ValidationException that was thrown during the validation process.

Using NHibernate events for validation

NHibernate (from version 2.0 on) provides a rich event architecture, that lets you selectively interfere with its internal operations in almost every conceivable way.  Essentially, every ISession method correlates to an event; and in many cases, there is a respective Pre/Post pair - see NH's source code (the configuration-file XSD or the NHibernate.Event namespace)  for a full list of defined event types. For each event, there's a relating listener interface, which you can implement to define the required custom action. For our validation purposes, we use the PreInsert and PreUpdate events. The resulting listener looks like this:

/// <summary>

/// NH event listener to perform object state validation before inserting or updating

/// an entity. Throws a <see cref="HibernateException"/>, if validation fails.

/// </summary>

public class ValidationEventListener : IPreInsertEventListener, IPreUpdateEventListener

{

    public bool OnPreInsert(PreInsertEvent @event)

    {

        ThrowIfValidationFails(@event.Entity);

        return false; // will cause NH to continue

    }

 

    public bool OnPreUpdate(PreUpdateEvent @event)

    {

        ThrowIfValidationFails(@event.Entity);

        return false; // will cause NH to continue

    }

 

    private static void ThrowIfValidationFails(object entity)

    {

        ValidationResult result = entity.Validate();

 

        if (!result.IsValid)

        {

            throw new HibernateException("Object validation failed", result.ValidationException);

        }

    }

 

} // class ValidationEventListener

The last thing, that's left to do, is to register our new event listener with NHibernate's configuration, so that it will automatically be called in the appropriate places:

var configuration = [NHibernate configuration goes here...]

 

var validationEventListener = new ValidationEventListener();

configuration.SetListener(ListenerType.PreInsert, validationEventListener);

configuration.SetListener(ListenerType.PreUpdate, validationEventListener);

 

sessionFactory = configuration.BuildSessionFactory();

That's it. All in all, the integration of our validation mechanism into NHibernate took us 14 effective lines of code. This is an acceptable effort, I think ;-)...

The sample solution

The sample solution (VS 2008) for the above can be downloaded from here. It's an evolution of the sample code from the first part, to which I added the here shown persistence layer (in the .Persistence assembly) and a relating test assembly (.Persistence.Test).

Some additional notes on this solution:

  • The test code uses an SQLite in-memory database (namely its ADO.NET version System.Data.SQLite, to be exact) for quick-and-dirty demonstration purposes. However, this approach should not be used for integration tests in real-life projects, because SQLite does not have all the features of a fully-blown DBMS (e.g. no unique/foreign key constraints).
  • Because the validation aspects of the Person class should not be invoked when hydrating an object instance from the database, properties with explicitly declared backing fields are used in the Person class. This allows  the use of a  nosetter strategy in the mapping file, thus circumventing the properties' setter and instead populating the relating field directly .
  • The mapping uses an interface IPerson as proxy for the Person class. While there are many benefits from programming to an interface instead of a concrete implementation in general, the immediate reason here was to avoid the necessity of making the Person members virtual.

 

kickit
shoutit
delicious facebook digg reddit linkedin stumbleupon technorati mrwong yahoo google-48x48 twitter email favorites
Posted on Wednesday, November 18, 2009 7:33 AM NHibernate , Architecture and Design , Aspect-oriented programming | Back to top


Comments on this post: Validating business objects with AOP (2: persistence with NHibernate)

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Thomas Weller | Powered by: GeeksWithBlogs.net | Join free