Are you valid?

The next thing we wanted to implement to our Core project was a business object validation. There are several paths we could take:

Because we qualified this application as a learning project, and we haven't used VAB before, we decided to try it. VAB enables developers to attach validation rules to properties in declarative way, by using attributes. Rules can also be defined in config file, allowing the simple rules change after the application is deployed, but that wasn't a requirement for this project.

Most of our entity classes now have properties that look like this:

[NotNullValidator]

[StringLengthValidator(3, 15,

    MessageTemplateResourceName = "ProductCodeLength",

    MessageTemplateResourceType = typeof (Product))]

public virtual string Code { get; set; }

 

[NotNullValidator]

[StringLengthValidator(1, 50,

    MessageTemplateResourceName = "ProductNameLength",

    MessageTemplateResourceType = typeof (Product))]

public virtual string Name { get; set; }

In order to avoid cluttering of code with localized error messages, they are moved to project resources, hence the MessageTemplateResourceName and MessageTemplateResourceType arguments.

And now the big thing... In order to check the object validity, IsValid() method is added to Entity<T> class (base class for all our entity objects, defined as Entity<T> where T: Entity<T>). IsValid is calling VAB Validator to check the property values on the object and returns true if object is valid. The first version looked like this:

public virtual bool IsValid()

{

    return Validation.Validate((T)this);

}

Validation class is a façade. It creates a Validator object and tests for validity in a single line of code. Validate method is actually generic - Validation.Validate<T>((T)this), but T argument is redundant.

The cast to type T was necessary, because the Validator isn't smart enough to go down the inheritance hierarchy. If we call Validate(this), it would try to find validation rules on the current class (Entity<T>), but not on inheriting classes. I.e. public class Product : Entity<Product>. By having a cast to T (Product in this case), we ensure that the rules are checked on the Product class.

This worked fine up until recently, when our entity model became more advanced:

public class Warehouse : Entity<Warehouse>

...

public class CommissionerWarehouse : Warehouse

...

IsValid method couldn't handle the CommissionerWarehouse class. The cast to T in Validate method gave it only a Warehouse type to check. CommissionerWarehouse rules were ignored. In order to handle this case, IsValid was rewritten to use the Validator object directly:

public virtual bool IsValid()

{

    var entityValidator = ValidationFactory.CreateValidator(GetType());

    return entityValidator.Validate(this).IsValid;

}

The code is twice as long as before, but I can live with it :)

The magic of this method allows easier Unit Testing (more on that later) and validation calls from the client without the reference to Validation Application Block assemblies. We could add some method to return error messages too, but that's not necessary, since we are using Martin Bennedik's WPF Integration for VAB.

The core of all applications

Today, I'll talk a little about the core of our application. There are many terms to describe it: business layer/model, entity layer/model, domain layer/model, middle layer... Whatever you want to call it, it still represents the problem you are trying to solve.

Being highly influenced by some articles we read, we mostly use Domain-Driven Design terms. So, our core project is called Peniko.Domain (Peniko is the name of my friends' company). This project started as a collection of POCO classes without much behavior, just enough to accommodate NHibernate's needs, but as we moving toward the higher layers of the application, it's getting more "intelligent". Below is the current class diagram.

Peniko.Domain class diagram

All class names are in English, except for PdvRate class. It should be VatRate (PDV = VAT), but we prefer our local acronym. You'll notice that all classes inherit from Entity<T> and below is how this class looked when it's created. The class is copied from here and renamed to Entity.

public abstract class Entity<T> where T : Entity<T>

{

    private int? _oldHashCode;

 

    /// <summary>

    /// Gets or sets the object Id.

    /// </summary>

    public virtual Guid Id { get; protected set; }

 

    /// <summary>

    /// ...

    /// </summary>

    public override bool Equals(object obj)

    {

        var other = obj as T;

        if (other == null) return false;

 

        // Handle the case of comparing two NEW objects

        var otherIsTransient = Equals(other.Id, Guid.Empty);

        var thisIsTransient = Equals(Id, Guid.Empty);

        if (otherIsTransient && thisIsTransient)

            return ReferenceEquals(other, this);

 

        return other.Id.Equals(Id);

    }

 

    /// <summary>

    /// ...

    /// </summary>

    public override int GetHashCode()

    {

        // Once we have a hash code we'll never change it

        if (_oldHashCode.HasValue) return _oldHashCode.Value;

 

        var thisIsTransient = Equals(Id, Guid.Empty);

 

        // When this instance is transient, we use the base GetHashCode()

        // and remember it, so an instance can NEVER change its hash code.

        if (thisIsTransient)

        {

            _oldHashCode = base.GetHashCode();

            return _oldHashCode.Value;

        }

 

        return Id.GetHashCode();

    }

 

    public static bool operator ==(Entity<T> x, Entity<T> y)

    {

        return Equals(x, y);

    }

 

    public static bool operator !=(Entity<T> x, Entity<T> y)

    {

        return !(x == y);

    }

}

 

There is one more important concept added to Domain project. Repository pattern interfaces. We are reevaluating the decision to have the repository interfaces in domain layer, but for now they are there. The purpose of repositories is to communicate with the mapping layer (load entities, save and delete them). Because CRUD operations are common for all our repositories, we created a base IRepository<T> interface. It looks like this:

public interface IRepository<T> where T : Entity<T>

{

    void Add(T entity);

    void Update(T entity);

    void Delete(T entity);

    T GetById(Guid id);

    IList<T> GetAll();

}

Other interfaces inherit IRepository<T>, and add more methods if necessary. I.e. IProductRepository looks like this:

public interface IProductRepository : IRepository<Product>

{

    Product GetProductByCode(string code);

    Product GetProductByBarCode(string barCode);

}

The classes implementing Repository interfaces are located in Peniko.DataAccess project. I'll talk more about them more in one of the following posts.

NHibernate learning material

We started off by learning NHibernate. Up until then I didn't use any OR/M (I don't consider LINQ 2 SQL to be a full OR/M). Most of the time, the code I wrote used ADO.NET DataReaders, alone or inside CSLA.NET DataPortal_xxx methods.

Since there is a lot of buzz lately about OR/Ms, Entity Framework is about to be released, it already got a vote of no confidence from ALT.NET group, etc, the good learning material isn't so hard to find. When I'm learning something new, I'm trying to find and use best good practices, established by the people who have been used that technology for a longer time. The following posts/articles/screencasts really helped me and my team to get on the right track.

The NHibernate FAQ by Gabriel Schenker is a great starting point:

Steve Bohlen's excellent screencast series Summer of NHibernate.

Ben Scheirman's series of articles about NHibernate and Domain Driven Design:

Davy Brion's NHibernate posts.

THE article NHibernate Best Practices with ASP.NET by Billy McCafferty. I'm looking forward for a chance to try his S#arp Architecture on a real project.

David Veeneman's NHibernate Made Simple article.

All of the above links will not only teach you how to use NHibernate, but also give you a start with Domain Driven Design, Unit Testing, Integration Testing, logging etc. Enjoy!