Self-Servicing and Lazy-Loading

Posts   
 
    
EricM
User
Posts: 2
Joined: 28-Jan-2005
# Posted on: 29-Jan-2005 04:58:17   

I've been using LLBLGen Pro for a couple of months and have been thrilled at how much work it saves. However, I've come across an issue that I can't seem to figure out. It seems like it would be a pretty common issue but I haven't seen any other posts about it specifically so I assume I'm missing something.

I'm using the SelfServicing model and everything works like a dream except where I access a new related entity via the parent entity's property. For example, let's say I have 2 tables: A and B. Table A has the following columns: ID and Name, where ID is the PK. Table B has the following columns: ID and Phone, where ID is the PK and is a FK that references A.ID.

If my code is as follows:


AEntity a = new AEntity();
a.ID = 1;
a.Name = "John Doe";
a.Save(true);

a.B.Phone = "5551112222";
Console.WriteLine("Phone: {0}", a.B.Phone);

The resulting output is:


Phone: 

The value in a.B.Phone is an empty string even though I just set it in the line right before. I understand that this is probably due to the _bReturnsNewIfNotFound, _alwaysFetchB, and _alreadyFetchedB values in the AEntityBase class, but somehow this doesn't seem right. Shouldn't I be able to access a.B more than once before I save it?

Obviously, this is an oversimplified example, but it illustrates the issue. The problem is also apparent if I were to have multiple properties in BEntity and were to try to set multiple properties before saving a.B.

Am I missing something here? If not, what is the recommended approach for dealing with this? I'm using the latest version of LLBLGen Pro (1.0.2004.1) if that helps.

Thanks in advance for any help you can provide!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 29-Jan-2005 13:28:54   

EricM wrote:

I've been using LLBLGen Pro for a couple of months and have been thrilled at how much work it saves. However, I've come across an issue that I can't seem to figure out. It seems like it would be a pretty common issue but I haven't seen any other posts about it specifically so I assume I'm missing something.

I'm using the SelfServicing model and everything works like a dream except where I access a new related entity via the parent entity's property. For example, let's say I have 2 tables: A and B. Table A has the following columns: ID and Name, where ID is the PK. Table B has the following columns: ID and Phone, where ID is the PK and is a FK that references A.ID.

If my code is as follows:


AEntity a = new AEntity();
a.ID = 1;
a.Name = "John Doe";
a.Save(true);

a.B.Phone = "5551112222";
Console.WriteLine("Phone: {0}", a.B.Phone);

The resulting output is:


Phone: 

The value in a.B.Phone is an empty string even though I just set it in the line right before.

No, you haven't simple_smile The problem is this: when you do: a.B.Phone, the lazy loading code tries to load B for the a instance, as you're touching the property. Because B doesn't exist, it returns a new entity and doesn't mark that new entity as being 'loaded', as it didn't exist. So when you re-touch a.B, it will retry to load the entity, which again fails.

To work around this, do: a.B = new BEntity();

and now you can set the properties without problem, as a.B is indeed set to an existing value: a.B.Phone="5551112222";

It's kind of awkward, I admit, but the lazy loader code's first priority is to deliver related entities IF available. As you haven't set a.B to a value, the code assumes the related BEntity has to be read from the DB and will re-try until it has read one OR you've set a.B to a value. simple_smile

Obviously, this is an oversimplified example, but it illustrates the issue. The problem is also apparent if I were to have multiple properties in BEntity and were to try to set multiple properties before saving a.B.

If there is no related BEntity for a, then you can only save a.B if you set it explicitly to a new value, i.e. new BEntity(); as I did in the line which solves your problem simple_smile

That a.B returns a new entity is not helping, I admit, however that was done to avoid errors in value-read code in forms for example.

Frans Bouma | Lead developer LLBLGen Pro
EricM
User
Posts: 2
Joined: 28-Jan-2005
# Posted on: 30-Jan-2005 02:32:37   

Thanks for the quick response. I'm glad to know I'm not crazy. I guess I've just been looking at the "returns new if not found" feature the wrong way. Now that I know, I'll feel much more comfortable working around it. Thanks again for a GREAT product and awesome support!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 30-Jan-2005 09:56:50   

smile thanks Eric!

Frans Bouma | Lead developer LLBLGen Pro
johnsmith
User
Posts: 19
Joined: 14-Dec-2004
# Posted on: 08-Feb-2005 17:01:29   

Just stumbled on the same issue as Eric and was pleased to find this thread and set my mind at rest! Could I just check that the correct way to create a new related entity only if it hasn't been created previously, is as follows:

a.BReturnsNewIfNotFound = false;
if (a.B==null) a.B = new BEntity();

Or am I missing a better way?

Thanks, J.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 08-Feb-2005 17:09:37   

You can also test: if(a.B.IsNew) { a.B=new Bentity(); }

Frans Bouma | Lead developer LLBLGen Pro
johnsmith
User
Posts: 19
Joined: 14-Dec-2004
# Posted on: 08-Feb-2005 17:19:20   

But B.IsNew will continue to return true after I've changed B's properties, i.e. I'd lose SomeValue in the following code...

if(a.B.IsNew)
{
a.B=new Bentity();
} 
a.B.SomeProperty = SomeValue;
if(a.B.IsNew)
{
a.B=new Bentity();
}
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 08-Feb-2005 19:24:17   

johnsmith wrote:

But B.IsNew will continue to return true after I've changed B's properties, i.e. I'd lose SomeValue in the following code...

if(a.B.IsNew)
{
a.B=new Bentity();
} 
a.B.SomeProperty = SomeValue;
if(a.B.IsNew)
{
a.B=new Bentity();
}

Yes, if you test again on isnew, then you'll get the actions you describe above. However after you've done a.B = new BEntity();, a.B.Foo will not re-read a new entity from the db. So you should do the test once, then fill in the properties.

Frans Bouma | Lead developer LLBLGen Pro
johnsmith
User
Posts: 19
Joined: 14-Dec-2004
# Posted on: 09-Feb-2005 10:31:56   

Appreciated. I’m just thinking about a situation where you don’t know whether or not a sub-entity has been initialised and populated yet, say if you’re building up some entity structure in an ASP.NET session over multiple pages before saving, then you have to use the null test.

Thanks for your advice, Cheers, J.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 09-Feb-2005 11:15:40   

Looking back, I think returning null instead of a new entity would have been a better choice. The sad thing is I can't change that now because too much code relies on the fact that a new entity is returned already (i.e. if I would change it, a lot of code would crash all over)

Frans Bouma | Lead developer LLBLGen Pro
johnsmith
User
Posts: 19
Joined: 14-Dec-2004
# Posted on: 09-Feb-2005 16:57:20   

I'm not so sure. It's a tricky decision. I think if you'd opted for returning null by default you'd probably find just as many occasions when you wished you'd implemented the return new option!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 09-Feb-2005 17:38:35   

johnsmith wrote:

I'm not so sure. It's a tricky decision. I think if you'd opted for returning null by default you'd probably find just as many occasions when you wished you'd implemented the return new option!

simple_smile Well that's what I thought beforehand so I implemented it this way.

I fetch the entity every time, and don't keep it around because in a recursive save it can be saved which is perhaps not what people want.

Frans Bouma | Lead developer LLBLGen Pro
KPat
User
Posts: 15
Joined: 26-Mar-2004
# Posted on: 21-Feb-2005 18:29:45   

Couldn't this situation be resolved by adding some bit flag to the entities to determine whether or not the related entity has already been initialized? For example,

 FirstEntity first = new FirstEntity() ;
first.SecondEntity.Property = "somevalue" ; <-- FirstEntity sets internal flag to prevent SecondEntity from being reset in the future
Console.WriteLine( "Property: {0}", first.SecondEntity.Property ) <-- Now displays proper value.

That wouldn't seem to break existing code while eliminating the awkwardness in the existing model.

-K

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 21:10:18   

KPat wrote:

Couldn't this situation be resolved by adding some bit flag to the entities to determine whether or not the related entity has already been initialized? For example,

 FirstEntity first = new FirstEntity() ;
first.SecondEntity.Property = "somevalue" ; <-- FirstEntity sets internal flag to prevent SecondEntity from being reset in the future
Console.WriteLine( "Property: {0}", first.SecondEntity.Property ) <-- Now displays proper value.

That wouldn't seem to break existing code while eliminating the awkwardness in the existing model. -K

Yes it will break existing code simple_smile . Say I use your proposal. if the related entity comes available, I can't refetch it, as it is never read, because lazy loading is already switched off. That's also why it uses a flag to check if lazy loading has been used already, but this is not set if the entity isn't found so a future read from the property will retrigger the lazy loading code and will read it propery from the db.

You can also write your code as this:


FirstEntity first = new FirstEntity() ;
first.SecondEntity = new SecondEntity();
first.SecondEntity.Property = "somevalue" ; 
Console.WriteLine( "Property: {0}", first.SecondEntity.Property ) 

To me this is more logical, as you define the new entity yourself, as it should be, because it's not there yet.

Frans Bouma | Lead developer LLBLGen Pro