- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Refetch revisited
Here's some code to manage the fetching of an entity.
private IPrefetchPath2 GetPrefetchPathForPO()
{
IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.POEntity);
prefetchPath.Add(POEntity.PrefetchPathCreatedBy);
prefetchPath.Add(POEntity.PrefetchPathPODetail);
// etc.
return prefetchPath;
}
protected bool FetchPO(POEntity po)
{
return adapter.FetchEntity(po, GetPrefetchPathForPO());
}
public POEntity FetchPO(int PONumber)
{
// construct PO entity and call the other one
POEntity po = new POEntity(PONumber);
if (FetchPO(po))
return po;
else
return null;
}
Now I'm going to copy a PO object in memory and save it.
POEntity newPO = new POEntity();
newPO.CreatedByPersonID = user.PersonID;
// etc.
foreach (PODetailEntity detail in po.PODetail)
{
PODetailEntity newDetail = new PODetailEntity();
newDetail.POLineNumber = x++;
// etc.
newPO.SetRelatedEntity(newDetail, "PODetail");
}
Now save
adapter.SaveEntity(newPO);
Now refetch. This code works
return FetchPO(newPO.PONumber);
But this code will cause a problem. The only difference here is I'm ultimately passing this existing PO entity to adapter.FetchEntity() instead of creating a new one, but I didn't think that would make a difference (but maybe it does).
FetchPO(newPO);
return newPO;
When I trace into this I see that adapter.FetchEntity returns true. Then later I get an out of synch exception, though not when referring to fields in the header, but just the detail.
// gets called from routine filling my page with values, have already successfully read po.* values
private void SetTotalLabelText()
{
Decimal total = mgr.CalcTotalForPO(po);
TotalLabel.Text = total.ToString("c");
}
and here is where exception occurs, upon reference to fields in detail.
public Decimal CalcTotalForPO(POEntity po)
{
Decimal total = 0M;
foreach (PODetailEntity detail in po.PODetail)
total += (Decimal)((detail.Quantity * detail.Price) * (1.0 + detail.TaxRate));
return total;
}
I tried looking at the address of newPO to make sure the same variable is actually getting passed around, but &newPO in the command window doesn't tell me the address (uh - shouldn't it?). But ObjectID.ToString() keeps returning the same guid (is every object in .NET getting a GUID? Good God!).
After answering this question please reset my post counter or people are going to start wondering about someone with 200 posts still asking newbie questions
Please don't call SetRelatedEntity directly, use the properties.
If you specify 'true' with the SaveEntity(), does it then work? (i.e.: it will do the refetch for you).
Every new entity gets a new GUID. As you get the same GUID for both, it seems you're working with the same object, not a copy.
Otis wrote:
Please don't call SetRelatedEntity directly, use the properties.
Ah, thanks. I did this because I couldn't figure out how to associate the new detail objects with the header, which won't have a primary key until it's saved. I thought maybe the purpose of this function was to link them together so that LLBLGenPro will know to assign the result of SCOPE_IDENTITY to the child records. So what should I be doing instead? I tried creating a collection and assigning it to po.PODetail, but that property is read-only.
Also.....um.....if I shouldn't be calling that method should it be callable by me?
If you specify 'true' with the SaveEntity(), does it then work? (i.e.: it will do the refetch for you).
I know about that flag but I have all the prefetch paths, so I do it this way.
Every new entity gets a new GUID. As you get the same GUID for both, it seems you're working with the same object, not a copy.
Ok, that's your GUID! It was late when I was doing this and I started thinking it was a .NET thing, but of course I never see that on other .NET objects. I'm working too much.
Yes, it does seem the object is the same. (If you know how to check the address in the command window, please share). Being an old C/C++ guy, I instinctively don't want to throw away this perfectly good object and allocate a new one if the old one serves fine for the fetch. But the code is indicating that that may not work. Is that possible?
JimFoye wrote:
Otis wrote:
Please don't call SetRelatedEntity directly, use the properties.
Ah, thanks. I did this because I couldn't figure out how to associate the new detail objects with the header, which won't have a primary key until it's saved. I thought maybe the purpose of this function was to link them together so that LLBLGenPro will know to assign the result of SCOPE_IDENTITY to the child records. So what should I be doing instead?
Using the property, which corresponds to the field mapped onto the relation, in this case "PODetail".
I tried creating a collection and assigning it to po.PODetail, but that property is read-only. Also.....um.....if I shouldn't be calling that method should it be callable by me?
![]()
It's to be public because the prefetch path fetcher uses it too.
Every new entity gets a new GUID. As you get the same GUID for both, it seems you're working with the same object, not a copy.
Ok, that's your GUID! It was late when I was doing this and I started thinking it was a .NET thing, but of course I never see that on other .NET objects. I'm working too much.
![]()
Yes, it does seem the object is the same. (If you know how to check the address in the command window, please share). Being an old C/C++ guy, I instinctively don't want to throw away this perfectly good object and allocate a new one if the old one serves fine for the fetch. But the code is indicating that that may not work. Is that possible?
Refetching in an existing object is doable, though the already contained related entities in the existing object won't be cleared of course.
Refetching in an existing object is doable, though the already contained related entities in the existing object won't be cleared of course.
Ok, that may be my problem. Clearly it is safest here just to allocate a new object and fetch into it, and I shouldn't try to be clever and reuse the existing object. Do you agree?
This is the first application where I actually built a real service layer/BLL using adapter, and it's really been a huge learning experience. Every time I save data, I have to consciously ask myself, "Do I need to refetch this data?". If the answer is no, I don't want to refetch it (so as to save time) and I want to document it (so I don't look at it later and ask myself the same question again). If the answer is yes I need to make sure I refetch it (and cleanly so!) and also have to take into account that that little flag in SaveEntity() doesn't know about prefetch paths.
I am reminded of the old sketch in Monty Python where the highway robber keeps stealing from the rich and giving to the poor, so much so that eventually he has given the poor everything the rich had, and when it is pointed out to him that he is now stealing from the poor and giving to the rich, he gives the immortal line
"Blimy, this redistribution of wealth thing is more complicated than I thought it was!".
There's lots of useful information on this board about doing this stuff, but I suspect I'm not the only one who has wrestled with this. Is it time to make a sticky or add a section to the manual, "Guidelines for designing service layer using adapter"? I would be happy to post my own source code (but a bit sanitized) as an example, though I imagine someone out there has much better code to offer.
JimFoye wrote:
Refetching in an existing object is doable, though the already contained related entities in the existing object won't be cleared of course.
Ok, that may be my problem. Clearly it is safest here just to allocate a new object and fetch into it, and I shouldn't try to be clever and reuse the existing object. Do you agree?
I think it takes more code to clean up an object than to create a new one.
This is the first application where I actually built a real service layer/BLL using adapter, and it's really been a huge learning experience. Every time I save data, I have to consciously ask myself, "Do I need to refetch this data?". If the answer is no, I don't want to refetch it (so as to save time) and I want to document it (so I don't look at it later and ask myself the same question again). If the answer is yes I need to make sure I refetch it (and cleanly so!) and also have to take into account that that little flag in SaveEntity() doesn't know about prefetch paths.
Indeed . Document your tracks and decisions.
. The fun thing is though that next time, you'll probably do it differently because the app asks for a different approach. BUt that makes /keeps things interesting
I am reminded of the old sketch in Monty Python where the highway robber keeps stealing from the rich and giving to the poor, so much so that eventually he has given the poor everything the rich had, and when it is pointed out to him that he is now stealing from the poor and giving to the rich, he gives the immortal line
"Blimy, this redistribution of wealth thing is more complicated than I thought it was!".
There's lots of useful information on this board about doing this stuff, but I suspect I'm not the only one who has wrestled with this. Is it time to make a sticky or add a section to the manual, "Guidelines for designing service layer using adapter"? I would be happy to post my own source code (but a bit sanitized) as an example, though I imagine someone out there has much better code to offer.
The higher you get in a stack of layers in an application, the more you'll realize the code you've to write is specific to the application type/functionality of the application. What's more interesting is the reasoning why you decided to do things this way and not the other way, in such a way that it is useful to others to learn from it, as in: to grasp the reasoning paths, so they can use/apply that to their own thinking as well.
But that's very hard, as it's often getting overly vague or too detailed. Like for example the architecture forum here. Lots of great info posted there, though a lot of approaches are discussed and it's hard to say one is better than the other, specifically because the context they're used in differs a lot.