Lazy loading, Inheritance, and Persistence ignorance (part 1.5)

A few days ago, I talked about a problem, that using an ORM like NHibernate could bring, when there's inheritance and lazy loading around (please refer to this post). Unfortunately, the example in this post turned out to be somewhat unclear. So here's a short follow-up with another example, that hopefully underpins my arguments a bit more precisely.

The original example gave the impression that the problem occurs only when we would try to typecast around in some way or the other, which is in most cases problematic in itself. While this was absolutely not what I had in mind, I must admit that one could easily come to that suggestion from reading the code. Unfortunately, the described problem is not only significant for typecasting issues, but - far more general - for everything that somehow relies on type information.

So let's see an example for that. First, I implemented equality-comparison on the original Person class:

///

Book author is a single ('real') person.

/// Implements equality based on its .

public class Person: Author, IEquatable<Person>

{

    public override bool Equals(object obj)

    {

        return this.Equals(obj as Person);

    }

    public virtual bool Equals(Person other)

    {

        if (object.ReferenceEquals(null, other)) return false;

        if (object.ReferenceEquals(this, other)) return true;

        return this.Id == other.Id;

    }

}

 Quite trivial and intuitive, and 100% along the lines of standard.NET programming. Most developers have seen and written such code many times. To see the problem with that, look at the following test:

[Test]

public void AnAuthorShouldRemainEqualToItselfAfterSavingAndReloading

{

    Author goethe = new Person { Id = 1, Line = "Goethe" };

    Book book = new Book { Name = "Faust", Author = goethe };

    Assert.AreEqual(goethe, book.Author); // pass

    using (ISession session = this.sessionFactory.OpenSession)

    {

        // Persist the book and remove it from session cache (thus

        // effectively simulating two different sessions...).

        object id = session.Save(book);

        session.Evict(book);

        // Reload the book and compare its author to its original instance.

        Book savedAndReloadedBook = session.Get<Book>(id);

        Console.WriteLine("Just to see lazy loading happening...");

        Assert.AreEqual(goethe, savedAndReloadedBook.Author); // fail!

    }

}

The result for that test is:

EqualityTestResult

So one and the same equality comparison gives different results here, although we did nothing obviously wrong, and no polymorphic casting is done in any way! (And no, it has also nothing to do with the Id value...).

Related problems might occur for example, if one is trying to implement IComparable, and many other use case scenarios can be imagined, where the domain's behavior suddenly will be altered under the hood for no obvious reason! I consider that a big problem for the general acceptance of ORMs in software architecture...

This article is part of the GWB Archives. Original Author: Thomas Weller

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.