Always be lazy


Scott Bellware has an interesting post about Lazy Load in ORMs.

IMHO, I prefer eager load of all scalar values and load on demand (lazy load) for all complex objects, even when the cardinality is M..1 and 1..1.  And once something is loaded, it stays loaded until until the user explicitly resets the navigation relationship.  In other words, navigations cause load on first access and never again.  This is how we have implemented Jasper – with two exceptions:

1.  Jasper allows objects to be added to collections which have not been populated.

2.  When a query is being built up on a collection (i.e. Northwind.Orders.Where().OrderDetails().OrderBy() ) query execution is delayed and the intermediary collections are not materialized.

Obviously, exception #2 is about the only way to support Linq and makes complete sense.  Exception #1 is a bit more interesting in that the only reason for it is to allow inserting on objects for top level collections – in other words we didn’t want to force the developer to load Northwind.Orders to add a single order.  I personally don’t think that is an important enough scenario to merit the exception, but the Jasper design team convinced me otherwise.

To be honest, I don’t like Span (the ability to front load related objects) and think the N+1 query problem solves a demoware issue not a real life problem.  In addition, Span introduces a bunch of complexity into the ORM framework which does not justify the benefit.

For Jasper, we wanted a single solution which was the most obvious and easiest to explain – which is consistent with our design principles.   As a result, there will be apps where Jasper may not be the right solution – but that is that tax when using a prescriptive framework.

Not having Span can lead to situations for the in memory object graph is inconsistent with the database in strange and complex ways.  However, I would argue that this is really a problem with the app logic that the framework is exposing.  Span support just hides the problem and doesn’t fix it unless the app developer is smart enough about using Span to manage their in-memory object graph – and that seems tricky at best for non-trivial apps.

That all said, we are open to adding Span support in Jasper depending on feedback.


Comments (7)

  1. Udi Dahan says:

    I love the title!

    About your exceptions:

    Adding objects to non-initialized collections. +1

    I have a post describing what I do to get around these limitations on "transparent persistence":

    http://udidahan.weblogs.us/2007/06/04/performant-and-explicit-domain-models/

    On the issue of spans – it is a big deal in terms of performance. I also have a post up on where it is architecturally best to define these spans and how they connect to both the Domain Model and the Service Layer. Take a look and tell me what you think:

    http://udidahan.weblogs.us/2007/04/23/fetching-strategy-design/

  2. Hey Andy!

    I heard – but don’t know if it’s true – that Jasper is built on EF.  Since EF doesn’t do lazy loading, did you guys build a lazy loading layer on top of EF (if Jasper is indeed built on EF)?

  3. aconrad says:

    Udi,

    I understand the high level perf scenario span solves, but I don’t buy that it really helps out in scenarios where it wouldn’t be preferable to cache the data.   In other words, to me – Span solves the N+1 query problem, but how often is this really a problem in the real world?

  4. aconrad says:

    Mr. Bellware,

    Jasper is built on top of the EF.  For Jasper, we ended putting in our own loading mechanism – but that is because we wanted to simplify the model and not because EF does support lazy load.  

    Where did you get the impression that EF doesn’t support lazy load?  

  5. Andrew,

    I’m not quite sure how caching helps with the scenario of having to run business rules on a graph of objects as a part of performing some business action (update/insert/delete) on that graph – all in a single transaction.

    For instance, say we have a business rule that says "preferred" customers get a 10% discount on orders. Preferred customers are customers who have ordered more than 100K of products – shipping costs don’t count. Now we receive an order from a customer, does he get the dicount?

    In order to do this business action, we need to load the customer, his orders, and their orderlines in the same transaction. We really wouldn’t want to use stale-cached data – resulting in possibly giving a discount when we shouldn’t have (in the case where a customer was preferred but then cancelled an order in the cache-timeout period).

    I consider this very much a real world problem that happens fairly often.

    Does that make sense?

  6. Andy,

    I’m pretty sure that I’ve heard it said at the EF presentations given by MS folks that I’ve attended that EF doesn’t do lazy loading.  Could be a faulty memory though…

  7. aconrad says:

    EF does lazy loading – in fact, it is the default.   (The team is currently adding Span support).