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:
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.