lazy loading collections

Posts   
 
    
Sokon1
User
Posts: 97
Joined: 17-Jul-2006
# Posted on: 17-Jul-2006 18:02:13   

Hi guys,

Using V2.0.0.0 und the self servicing scenario with C#:

If I want to load a single Entity on demand, I just access it like:

string x = myParentEnt.x;

If I want to load an entity collection, i have to write something like that:

OrderCollection orders = customer.Orders; Or: OrderCollection orders = customer.GetMultiOrders(false);

All of that I understand and it works fine, but what about that: OrderEntity ent = (OrderEntity)customer.Orders[0];

I this case, the Orders collection is not loaded and i get an "wrong index" error (of course). I would have to write:

OrderCollection orders = customer.Orders; OrderEntity ent = (OrderEntity)orders[0];

Am I right? Do I really have to write these two lines just to get one entity? Or did I miss something? I think the one line approach is much easier to write, isn't it?

Thanks for your answers!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 17-Jul-2006 18:13:49   

I can't reproduce it.


[Test]
public void WrongLazyLoadingTriggerTest()
{
    CustomerEntity customer = new CustomerEntity("CHOPS");
    OrderEntity order = customer.Orders[0]; // should trigger lazy loading first. 
}

works fine. I also see 2 queries being executed: one for customer and one for its orders... As lazy loading is implemented in the generated code, could you set a breakpoint into the property for the child collection and see what code is executed?

Frans Bouma | Lead developer LLBLGen Pro
Sokon1
User
Posts: 97
Joined: 17-Jul-2006
# Posted on: 18-Jul-2006 08:31:21   

Otis, thanks for your answer.

If I access the Property, GetMulti_EntityName_(false) is executed. But the _alreadyFetched-Property is always TRUE when the method "GetMulti[i]EntityName[/i](bool forceFetch, IEntityFactory entityFactoryToUse, IPredicateExpression filter)" later on.

Since I now know it should be possible, here's the complete Code. Perhaps something else is wrong. (bsKapital, bsPerson1 and bsPerson2 are BindingSources)


VorgabeKapital.FetchUsingPK(3);
bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

Using this, I get the correct results:

            
IPrefetchPath prefetchPath = new PrefetchPath((int)Data.EntityType.VorgabePersonEntity);
prefetchPath.Add(VorgabeKapitalEntity.PrefetchPathVorgabePerson);

VorgabeKapital.FetchUsingPK(3, prefetchPath);
bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

This works, too:


VorgabeKapital.FetchUsingPK(3); 
VorgabeKapital.GetMultiVorgabePerson(true);

bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

But that's not lazy loading, is it? Do I have to do something to enable lazy loading? I didn't find anything in the documentation, but perhaps I simply missed it.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 18-Jul-2006 10:33:30   

Sokon1 wrote:

Otis, thanks for your answer.

If I access the Property, GetMulti_EntityName_(false) is executed. But the _alreadyFetched-Property is always TRUE when the method "GetMulti[i]EntityName[/i](bool forceFetch, IEntityFactory entityFactoryToUse, IPredicateExpression filter)" later on.

Then the fetch already took place. Be aware that it is tricky to debug: if you have open a watch in the debugger which reads the collection property, it will trigger lazy loading and load the collection.

Since I now know it should be possible, here's the complete Code. Perhaps something else is wrong. (bsKapital, bsPerson1 and bsPerson2 are BindingSources)


VorgabeKapital.FetchUsingPK(3);
bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

It's a little tricky. You set VorgabeKapital as the datasource for bsKapital, which could have a DataMember set to VorgabePerson and a grid could be bound to that too. IF that's the case, at this line: bsKapital.DataSource = this.VorgabeKapital; the VorgabePerson collection is fetched first.

at this line: bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0]; VorgabePerson is fetched for the first time, if bsKapital isn't bound to a grid and datamember isn't set to VorgabePerson.

and when you access the property again on this line: bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1]; The collection is already fetched so the same collection is returned. If there was just 1 entity, this gives an exception.

Using this, I get the correct results:

            
IPrefetchPath prefetchPath = new PrefetchPath((int)Data.EntityType.VorgabePersonEntity);
prefetchPath.Add(VorgabeKapitalEntity.PrefetchPathVorgabePerson);

VorgabeKapital.FetchUsingPK(3, prefetchPath);
bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

As you fetch the data up front, lazy loading isn't triggered here at all, as the data is already fetched simple_smile

This works, too:


VorgabeKapital.FetchUsingPK(3); 
VorgabeKapital.GetMultiVorgabePerson(true);

bsKapital.DataSource = this.VorgabeKapital;
bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

At this line you fetch the data: VorgabeKapital.GetMultiVorgabePerson(true);

so any code accessing the property won't trigger lazy loading, as the data is already fetched.

But that's not lazy loading, is it? Do I have to do something to enable lazy loading? I didn't find anything in the documentation, but perhaps I simply missed it.

Lazy loading is automatic, however it of course doesn't fetch the data over and over again: if it's fetched once, it won't fetch it again. You can force it to refetch the data by passing true to the getmulti method.

Frans Bouma | Lead developer LLBLGen Pro
Sokon1
User
Posts: 97
Joined: 17-Jul-2006
# Posted on: 18-Jul-2006 11:39:00   

Hell yes! wink The Debugger! That was the right hint. Thanks!

So for those with the same problem: here is what I found out:


* VorgabeKapital.FetchUsingPK(3); 
  bsKapital.DataSource = this.VorgabeKapital;
  bsPerson1.DataSource = this.VorgabeKapital.VorgabePerson[0];
  bsPerson2.DataSource = this.VorgabeKapital.VorgabePerson[1];

If there is a breakpoint at the line marked by the '*' and there is a watch on 'this.VorgabeKapital.VorgabePerson[0];' The lazy loading tries to fetch the collection of a entity, which is not fetched yet. So the watch shows an exception. The problem is: By trying to fetch the collection the _alreadyFetched-Property is set to true (couldn't this be changed by you LLBLGen-developers?). If you access the VorgabePerson collection later, the collection won't be fetched again and - BANG

If you remove the breakpoint, everything is fine.

Thanks again for your help, Otis!