One requirement that NHibernate imposes on your object model is that all public members must be virtual in order to support lazy loading.  I got really tired of getting a yellow-screen-of-death while working on Fail Tracker every time I added a new member to my domain and forgot to mark it as virtual.  So, I added a simple test to enforce the convention.

Here’s the error I was getting just about every single time I added a new property or method to my entities:

image

Annoying.  I prefer to catch things at compile time when possible, and at test-time otherwise.  I hate it when I catch something only at runtime.  Fortunately a little bit of reflection is all that’s needed to enforce a “all public methods and properties on entities must be virtual” convention:

[Test]
public void all_domain_entities_should_contain_only_virtual_members()
{
    var types = from t in typeof (Project).Assembly.GetTypes()
                where t.Namespace == typeof (Project).Namespace &&
                      t.IsPublic &&
                      !t.IsAbstract &&
                      t.IsClass
                select t;

    var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;

    var nonVirtualMembers = (from t in types
                             from m in t.GetMethods(bindingFlags)
                             where !m.IsVirtual
                             select t.Name + "." + m.Name).Union(
                                 from t in types
                                from p in t.GetProperties(bindingFlags)
                                 where !p.GetGetMethod(true).IsVirtual
                                 select t.Name + "." + p.Name);

    if (nonVirtualMembers.Any())
    {
        Assert.Fail("Non-virtual members found in domain type: \r\n" + string.Join("\r\n", nonVirtualMembers.ToArray()));
    }
}

Now when I add a new method or property to one of my entities, I’ll get a failing test when I inevitably forgot the ‘virtual’ keyword.  Yes, I should have some integration tests that catch this sort of thing as well, but I actually like this approach as it’s very specific about the convention its enforcing.