…but it does impose limitations on your domain model.
Most of these limitations, however, like the need for public/ internal/ protected members to be virtual, and the requirement for a default constructor to exist with at least protected accessibility, are not that hard to adhere to and usually don’t interfere with what you would do if there were no rules at all.
One of the limitations, however, can be pretty significant – Ayende describes the problem here, using the term “ghost objects”.
But, as I am about to show, this significance only arises if you follow a certain style of coding, which you should usually avoid!
Short explanation of the problem
When NHibernate lazy-loads an entity from the db (i.e. when you call session.Load<TEntity>(id) or when an entity in your session references something through a lazy-loaded association), it does so by providing an instance of a runtime-generated type, which acts as a proxy.
The first time you access something on the proxy, it gets “hydrated”, which is just a fancy way of saying that the data will be loaded from the database.
This would be fine and dandy, if it weren’t for the fact that the proxy is a runtime-generated subclass of your entity, which – in cases where inheritance is involved – will be a sibling to the other derived classes. Consider the simple inheritance hierarchy on the sketch to the right which in code could be something like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public abstract class LegalEntity { public virtual Guid Id { get; set; } } public class Person : LegalEntity { public virtual string FirstNames { get; set; } public virtual string LastName { get; set; } } public class Company : LegalEntity { public virtual string CompanyName { get; set; } } |
– and then NHibernate will generate something along the lines of this (fake :)) class signature:
1 2 3 4 |
public class LegalEntityProxy1234AndSomeMoreStuff : LegalEntity { // ... secret stuff to access db in here } |
See the problem? Here’s the problem:
1 2 3 |
var legalEntity = session.Load<LegalEntity>(someKnownId); Assert.IsTrue(legalEntity is Person || legalEntity is Company); //< AssertionException! will never be Person or Company |
This means that this kind of runtime type checking will fail in those circumstances where the entity is a lazy-loaded reference of the supertype, and the following will FAIL:
1 2 3 4 5 6 7 8 9 10 11 12 |
var legalEntity = session.Load<LegalEntity>(someKnownId); if (legalEntity is Person) { var person = (Person) legalEntity; return person.FirstNames + " " + person.LastName; } else { // we know it's a company then, right? WRONG! var company = (Company) legalEntity; //< InvalidCastException! return company.CompanyName; } |
One possible solution
The other day, Ayende blogged about a recent addition to NHibernate, namely lazy-loaded properties. This allows an entity to be partially hydrated, intercepting calls to certain properties to lazy-load the relevant fields on demand.
This feature is great when storing LOBs alongside the other fields on an entity, but it also laid the ground for his most recent addition, which is the ability to lazy-load an association by setting lazy="no-proxy" on it.
This way, NHibernate will not build a proxy, but instead it will intercept the property getter and load the entity at that point in time, thus being able to return the exact (sub)type of the loaded entity.
Now this seems to solve our problems, but let’s zoom out a bit … why did we have a problem in the first place? Our problem was actually that we failed to write object-oriented code, but instead we wrote a brittle piece of code that would fail at runtime whenever someone added a new subtype, thus violating the Liskov substitution principle. Moreover it just feels wrong to implement business logic that reflects on types!
What to do then?
Well, how about making your code polymorphic? The logic above could be easily rewritten as:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public abstract class LegalEntity { public virtual Guid Id { get; set; } public abstract string Name { get; } } public class Person : LegalEntity { public virtual string FirstNames { get; set; } public virtual string LastName { get; set; } public override string Name { get { return FirstNames + " " + LastName; } } } public class Company : LegalEntity { public virtual string CompanyName { get; set; } public override string Name { get { return CompanyName; } } } |
– which moves the logic of yielding name as a oneliner into the class hierarchy, allowing us to always get a name from a LegalEntity.
What if I really really need a concrete instance?
Then you should use the nifty visitor pattern to extract what you need. In the example above, I would need to add the following additions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public interface ILegalEntityVisitor { void Visit(Person person); void Visit(Company company); } public abstract class LegalEntity { // ... public abstract void Accept(ILegalEntityVisitor visitor); } public class Person : LegalEntity { // ... public override void Accept(ILegalEntityVisitor visitor) { visitor.Visit(this); } } public class Company : LegalEntity { // ... public override void Accept(ILegalEntityVisitor visitor) { visitor.Visit(this); } } |
This way, we’re taking advantage of the fact that each subclass knows its own concrete instance, thus allowing it to pass itself to the visitor we passed in.
This is the preferred solution when the logic you’re writing doesn’t belong inside the actual entitiy class, like e.g. when you want to convert the entity to an editable view object, because this will make your code break at compile time if someone adds a new specialization, thus requiring each piece of logic to handle that specialization as well.
Oh, and if you’re a Java guy, you might be missing the ability to create an inline anonymous visitor within the scope of the current method, but that can be easily emulated by a generic visitor, like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class LegalEntityVisitor : ILegalEntityVisitor { Action<Person> handlePerson; Action<Company> handleCompany; public LegalEntityVisitor(Action<Person> handlePerson, Action<Company> handleCompany) { this.handlePerson = handlePerson; this.handleCompany = handleCompany; } public void Visit(Person person) { handlePerson(person); } public void Visit(Company company) { handleCompany(company); } } |
– which would allow you to write inline typesafe code like this:
1 2 3 4 5 |
double risk = CalculateInitialRisk(); // multiply some number depending on type of legal entity legalEntity.Accept(new LegalEntityVisitor(person => risk *= GetRiskExperienceForPeople(), company => risk *= GetRiskExperienceForCompany(company)); |
Only thing missing now is the ability to return a value in one line depending on the subclass. Well, the generic visitor can be used for that as well by adding the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class LegalEntityVisitor : ILegalEntityVisitor { // ... public static TResult Func<TResult>(LegalEntity legalEntity, Func<Person, TResult> handlePerson, Func<Company, TResult> handleCompany) { TResult result = default(TResult); legalEntity.Accept(new LegalEntityVisitor(p => result = handlePerson(p), c => result = handleCompany(c))); return result; } } |
– allowing you to write code like this:
1 2 3 4 |
public string GetReportTypeCodeFor(LegalEntity legalEntity) { return LegalEntityVisitor.Func(legalEntity, p => "P00000", c => "C" + GetReportingCode(c)); } |
– and still have the benefit of compile-time safety that all specializations have been handled.
When to reflect on types?
IMO you should only reflect on types in business logic when it’s a shortcut that doesn’t break the semantics of your code. What do I mean by that? Well, e.g. the implementation of the extension method System.Linq.Enumerable.Count<T>() looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
public int Count<T>(this IEnumerale<T> items) { if (items is ICollection<T>) { return ((ICollection<T>)items).Count; } var count = 0; // iterate and count manually return count; } |
This way, providing the number of items is accelerated for certain implementations of IEnumerable<T> because the information is already there, and for other types there’s no way to avoid manually counting.
Conclusion
I don’t think I will be using the new lazy="no-proxy" feature, because if I need it, I think it is a sign that my design has a bad smell to it, and I should either go for polymorphism or using a visitor.
“Our problem was actually that we failed to write object-oriented code, but instead we wrote a brittle piece of code ”
You saved my bacon with this one!
How can something so obvious be forgotten. Sometimes when you are mucking around at the ORM level you forget the benefits of the “O” when you focus too much on the “R”.
Thanks
Glad I could help!
Oh yeah, and we should never forget the benefits of the “O” 🙂