More on Lazy Loading vs. Pre-loading in O/R mapping scenarios
Frans responded to my post about Lazy Loading. He disagrees with the comparision between lazy loading and paging, because...
The difference is in the fact that the first (paging) goes through the same channels for every fetch of new data while the second (lazy loading) first uses the normal channel to fetch data and after that uses functionality 'under the hood' to get the additional data.
This thus means that the lazy loading functionality bypasses the logic you would otherwise use to obtain data.
That is actually depending on how you define the logic for obtaining the data. Usually lazy loading would occur inside an aggerate root, so I don't believe that there is that much logic that is involved in getting the data. In both cases, it is the OR/M that is invoked to load the data from the DB, and hook up the correct assoications.
Most OR/M would provide you with a way to hook into this mechanism. In NHibernate, you can use IInterceptor.OnLoad to execute logic whenever an object is loaded, regardless of how it is loaded. I don't see this as a problem.
I am curious about the definition of logic to get the data, by the way. About the only thing that I can think of is security, where you may not be allowed to see the entire graph, but lazy loading may enable that, if you got the object model wrong. This is more an issue of proper design than anything else, in my opinion. An aggregate root should be the object you secure, and not its children.
No argument about here, although my approach would be the other way around. A good place to avoid lazy loading is in remoting scenarios, for the same reason you don't want to memory map a file on a network share and page it in.
Comments
There's been some healthy discussion....
Lazy loading is more triggered by traversing a graph than 'inside an aggregate root'. 'Inside an aggregate root' is also very fuzzy, as it doesn't mean anything really physical other than an abstract concept (I'll refer to the DDD yahoo group for an example of how confused a lot of people are about this)
It's precisely that which makes it use 2 paths. Sure, you can write your own plumbing code with an interceptor but isn't that mitigating the fact that you're using a framework to AVOID writing any plumbing? Why would anyone need to write plumbing to use a feature 'properly' ?
Frans,
As I said, I usually don't need to write additional code, and I am confused with regard to what you consider logic to load the data.
Can you expand on that?
I think the whole idea of "aggregate root" is pretty fuzzy, as Frans said, because different application components can have different uses for the same "root". For example, and Employee form may need Dependent information, while an Employee Employment Status report may not. Both need different Employee graphs.
Using the preloading technique, a programmer can predefine the graph and tie it directly to the component using it. This can be wasteful though, as sometimes, especially with forms, the 99% use case is that only 10% of this data is actually used. This leads to a need for some form of lazy-loading, where for example, EmployeeDependent information is loaded into the graph only when the Dependent screen is accessed on the Employee form. If you lazy-load inside the framework, you won't be able to say what Dependent information is actually needed by this component unless you do something strange like put the root Employee entity into "EmployeeForm" mode where the lazy loading will make decisions based on this mode. This is awkward, however, because now you are virtually tying GUI dependencies into your entity framework.
To avoid this awkwardness, it seems right to put the "lazy loading" code into the GUI itself. After all, the GUI should know exactly what data it needs, so this seems like the proper place. Now, this "lazy loading" code will end up taking the form of a preload (PrefetchPath for llblgen).
So, it seems to me that the best solution is to mix the preloading code with the lazyloading concept...best of both worlds and everyone is happy!
I am not sure that I understood what you meant.
That is what I do, lazy loading for ease of use, and preloading when I need performance, which turn out to be not so much, actually.
What I meant was that by putting the lazyloading logic into the entity framework, so that accessing Employee.Dependents will go and fill in this data from the database automatically, you are forced to decide inside the lazyloading code how much of the Dependent graph you want to fetch.
Before, I thought you were implying that the "what to fetch" decision would be based on definition of the aggregate roots, so there is a predefined Dependent graph which gets prefetched. I was just saying that you can't really predefine this graph without knowing the needs of the component which is using the data.
If it is ok to not predefine any graph at all, and lazyload exactly what is being asked for and nothing else, you obviously don't have this problem, but I think it is very rare where this approach makes sense when you look at the performance implications of so many round-trips to the database.
My point was just that lazyloading is good, but it shouldn't happen within the entity framework, it should happen outside, where the component which is using the data is defined. The only time where this isn't true is in the situation in the above paragraph, where you really can afford a roundtrip to the database for every new piece of data asked for.
That is exactly the reason why I like lazy loading, defining pre fetch path in advance is very rigid, in my opinion.
Its rigid in the sense that you do something like:
"Ok, I'm adding a one-to-many grid onto this form, so I need to add a few lines of code to the graph definition."
If you mess up the graph definition, the grid doesn't display right...that definitely is rigid.
I guess I'm so used to it that it doesn't seem like that big a deal. I would hate to see how many db connections it would take to fill up the contents of that grid using pure lazy-loading, though.
@Harald
I disagree with this.
The most common usage is with web scenarios, where it is used for request-response, so the pre-load vs. lazy load is usually in the context of a request.
By performance in lazy load scenarios, I am mostly referring to SELECT N+1 issues.
Comment preview