Iterate through inherited objects

Posts   
 
    
eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 01-Feb-2006 13:04:13   

Dears at the forum,

I am using Adapter with subclasses, 1.1 DotNet, VS 2003 and date displaed in the about box is Novermber 30th, 2005 (the moving image is pretty freaky!!!)

I defined a View QryCustomerAddress that collects the various addresses of a customer. The view has a discrimintaor column that has a value that determines the type of the address (shipping, billing, etc.). I defined the view to be an entity and created a relationship with the Customer entity. I defined a number of subentites using the different values of the discriminator. The code generated was wonderful: - Super class QryCustomerAddress as super class for all addresses - different classes for the different address-types - Prefetchpathes are already defined for all entities envolved

What I am unable to do is interate through the collection. I keep on getting an "invalid Cast exception". A code example

        
For Each _address As AWD.EntityClasses.MyQryCustomerAdressEntity In _customer.QryCustomerAdress
            Debug.WriteLine("Address found")
        Next

I get the same error when I try any of the subclasses, like billing of shipping address. The collection has no members ofthe super class, only subtypes.

Thank you very much for your help

Best regards,

Or

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 01-Feb-2006 14:51:30   

Please check the following thread: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=5053

Also please state the complete exception text, stack trace and your LLBLGen Pro RuntimeLibrary version.

Thanks.

eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 01-Feb-2006 15:28:29   

Dear Walaa,

I just downloaded the lastest componentes for LBLGen, regenerated and recompiled. The error I get is [System.InvalidCastException]. There is no way I can see to iteriate through the collection using the For Each construct. Again, I am using the "One target (view/table) per entity hierarchy" with a super Address Entity/Class and a number of subclasses that are defined using the value of a descriminator. What I am trying to do is the follwoing: - I fetch a collection with all addresses of a customer - iterate through the collection while getting all instances of a certain type i.e. all delivery-addresses in a collection that might contain other types of addresses.

I have checked the thread you mentioned and I couldn't find a hint on my problem disappointed

Best regards

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Feb-2006 15:42:17   

Please paste the complete exception and the complete stack trace, otherwise we can't help you, as that's essential information in this kind of situations.

Frans Bouma | Lead developer LLBLGen Pro
Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 01-Feb-2006 15:53:47   

You didn't answer my question:

Please state the complete exception text, stack trace and your LLBLGen Pro RuntimeLibrary version

Also please check this other thread: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=5062

We have a policy to make you read 10 threads before giving you the answer simple_smile

eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 01-Feb-2006 16:34:20   

Dear Otis,

Here some few details which might be helpful:

  • The addresses fetched are all correctly typed (according to the discriminator, so a billing-address has the type BillingAddressEntity), but are of the super class types (BillingAddressEntity instead of MyBillingAddressEntity)

  • In the prefetchpath construction I added an entityfactory for the main subclass (MyCustomerAddressEntity), yet the classes returned are still of the super class type (MainAddressEntity, BillingAddressEntity, etc.).

  • The entityCollection displays as EntityFactoryToUse is the SubClass Factory MyQryCustomerAddressEntityFactory

Back to the main problem: The ability to iterate through a collection contains a number of different types (in my case billing, shipping, main, etc.), using For Each. I am asking myself if this is possible at all!

for each billingaddress in addresses
DoSomething
next
for each Shippingaddress in addresses
DoSomething
next

Is this possible at all without obtainging a special enumerator that points to the classes needed?

I hope that this might help clarify the issue!

Best regards

eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 01-Feb-2006 17:20:59   

One more thing (actually quite important): The exception I get has very little information, except for InvalidCastException, without stating who and where!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-Feb-2006 12:01:13   

The collection _customer.QryCustomerAdress can have different types. As the relation is between Customer and Address (the supertype/root of the hierarchy), the collection, when fetched, can have instances of Address and any of the subtypes of Address.

so if you have Address and teh subtypes: BillingAddress and ShippingAddress, these are siblings in the hierarchy. If you traverse the collection _customer.QryCustomerAdress, you can't assume the instances in that collection are all BillingAddresses as it can be one address is a shippingaddress. As these are siblings, you can't cast from one to the other. This means that if you do:


for each BillingAddress address in _customer.QryCustomerAdress
   ....
next

this will crash if _customer.QryCustomerAdress contains a ShippingAddress, as the for each loop will cast the instance to BillingAddress below the surface, at least try to do so, which of course fails.

So what you should do is:


for each AddressEntity address in _customer.QryCustomerAdress
    ' check here the discriminator field in address and based on that cast. 
next

Frans Bouma | Lead developer LLBLGen Pro
eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 06-Feb-2006 09:19:06   

Dear Otis,

thank for your reply, it helped clear why For Each will not work in this manner. I am using the Extended Entity Template. Each entity has a two representations, whereby one inherits from the other (MyXXXEntity and Entity). The View-As-Entity abides by this design rule and is generated in two classes. The classes retrieved by the query, however, are all of the super type und not of the inherited.

In _customer.QryCustomerAdress, for example, _customer is of the type MyCustomerEntity. Members of the collection QryCustomerAdress don't belong to the extended types, but but rather to the super types. I tried to override this behavior by defining a class factory in the query, or in the collection, all this didn't work. I still get address classess that are of the super type. What am I doing wrong??

Thank you for your helpt!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 10-Feb-2006 10:12:37   

Sorry for the late reply.

If you use the derived entity classes for adapter, please download the latest version of these templates, as the were updated recently to fix what you run into (I think the problem you ran into is the one that's fixed, but I'm not entirely sure it's the same thing)

Frans Bouma | Lead developer LLBLGen Pro
eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 13-Feb-2006 11:42:04   

Dear Otis,

thank you for the reply! I will download the template and test as soon as I can!

Greetings

eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 14-Feb-2006 11:51:00   

Dear Otis,

I downloaded the latest template set and the the latest "extended entity generator" template. I installed both and tried to generate. I get the following errors:

Task ActionProceduresClassGenerator Could not find template 'SD_ActionProceduresAdapterTemplate'. It is not defined in the template set config file.

Task RetrievalProceduresClassGenerator Could not find template 'SD_RetrievalProceduresAdapterTemplate'. It is not defined in the template set config file.

Task VisualStudio.NetProjectFileCreator Could not find template 'SD_VsNet2002DBSpecificAdapterTemplate'. It is not defined in the template set config file

Task EntityClassGenerator Could not find template 'SD_DerivedEntityAdapterTemplate'. It is not defined in the template set config file.

Task DerivedEntityFactoryClassesGenerator Could not find template 'SD_DerivedEntityFactoriesAdapterTemplate'. It is not defined in the template set config file.

Task VisualStudio.NetProjectFileCreator Could not find template 'SD_VsNet2002AdapterTemplate'. It is not defined in the template set config file

Could it be that certain templates need to be registered in a configuration file? Any Help would be really appreciated! Unfortunately, I installed without making a backup of the older installation :-(

Best regards

eugene
User
Posts: 85
Joined: 05-Oct-2004
# Posted on: 14-Feb-2006 12:11:37   

Dear Otis, please forget about my latest posting. Is was a clear case of "they have eyes but they just don't see": The template set used was a wrong one (C# template set for sqlserverce and cf.net). After I installed the new templates, the selected template set was somehow (maybe by my mouse) reset to this template. I changed the template set and generated and every thing is ok. Oh right, and the classes generated are of course of the right type, i.e. of the subclass and no longer of the super class type!

Many thanks and best regards

Eugene

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Feb-2006 12:20:39   

smile

Frans Bouma | Lead developer LLBLGen Pro
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 22-Oct-2006 12:20:44   

Otis wrote:

The collection _customer.QryCustomerAdress can have different types. As the relation is between Customer and Address (the supertype/root of the hierarchy), the collection, when fetched, can have instances of Address and any of the subtypes of Address.

so if you have Address and teh subtypes: BillingAddress and ShippingAddress, these are siblings in the hierarchy. If you traverse the collection _customer.QryCustomerAdress, you can't assume the instances in that collection are all BillingAddresses as it can be one address is a shippingaddress. As these are siblings, you can't cast from one to the other. This means that if you do:


for each BillingAddress address in _customer.QryCustomerAdress
   ....
next

this will crash if _customer.QryCustomerAdress contains a ShippingAddress, as the for each loop will cast the instance to BillingAddress below the surface, at least try to do so, which of course fails.

So what you should do is:


for each AddressEntity address in _customer.QryCustomerAdress
    ' check here the discriminator field in address and based on that cast. 
next

I'm having a similar issue, but I'm not seeing how you would cast from the supertype to the subtype. How can you cast from Address to BillingAddress without getting a cast error?

In my case, I'm fetching a collection of CalendarItems, each of which could be an EventEntity or a TaskEntity. In this specific code, I only care about the first EventEntity (there should always be 0 or 1) and my return value must be typed as EventEntity.


        private EventEntity _GetEvent(EventEntity.EventTypes type)
        {
            foreach (CalendarItemEntity ci in this.CalendarItems)
            {
                if (ci.EntityType == (byte)EntityTypes.Event)
                {
                    // Checking the descriminator gets me here, but how do I return the EventEntity?
                    // These lines both error (as expected, since you can't cast to a more specific type, right?):
                    // EventEntity evt = (EventEntity)ci;
                    // EventEntity evt = ci as EventEntity;

                    if (evt.EventType == type)
                    {
                        return evt;
                    }
                }
            }
            return null;
        }

Maybe it's too late at night and I'm just too sleepy to figure it out? confused

Chester
Support Team
Posts: 223
Joined: 15-Jul-2005
# Posted on: 22-Oct-2006 20:55:36   

The problem is that this...

foreach (CalendarItemEntity ci in this.CalendarItems)

...gets only the supertype entity. If it was retrieving the subtype initially typed as the supertype (which is of course possible in inheritance scenarios) then you could subsequently do the cast to the subtype, since the object would indeed actually be an instance of the subtype. Since it isn't, you have to get a new instance of the subtype instead of doing a cast.

So instead of this...

EventEntity evt = (EventEntity)ci;

...you'd do this:

EventEntity evt = new EventEntity(ci.ItemID);
bmoeskau
User
Posts: 54
Joined: 15-Jun-2005
# Posted on: 23-Oct-2006 05:41:40   

Chester wrote:

The problem is that this...

foreach (CalendarItemEntity ci in this.CalendarItems)

...gets only the supertype entity. If it was retrieving the subtype initially typed as the supertype (which is of course possible in inheritance scenarios) then you could subsequently do the cast to the subtype, since the object would indeed actually be an instance of the subtype. Since it isn't, you have to get a new instance of the subtype instead of doing a cast.

So instead of this...

EventEntity evt = (EventEntity)ci;

...you'd do this:

EventEntity evt = new EventEntity(ci.ItemID);

But then I would need to refetch each individual EventEntity each time I did this since I need it to be loaded correctly with data.

Maybe our basic approach is flawed. The issue is that we only have one physical table (CalendarItem) and Event and Task are both mapped as subtypes of this same table. We used to have separate tables, but it got down to where they only had the FK to CalendarItem, so we just consolidated to a single table (the objects still have different custom properties and methods though). But now since there are no separate relations to Event or Task tables, we can't specifically prefetch those types, only CalendarItems. Maybe we should go back to physical tables for Event and Task (or is there another way to accomplish fetching the subtypes by specific type without having the physical tables/relations)?

Thanks for your help.

[Edit]: I lied. I double-checked our inheritance hierarchy, and CalendarItem, Event and Task are actually mapped as siblings currently, which would explain my casting issue. This is because we have a base table (BaseItem) and when we executed the command to construct our "target per entity hierarchies" automatically it made everything subtypes of BaseItem. We did this back before we fully understood the inheritance model, and I think it now needs some adjustment. I'm still having issues getting this fixed, but I'll start a different post if I cannot get it resolved since it's no longer a casting issue. simple_smile

Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 24-Oct-2006 17:54:16   

You can probably use EntityType filters.

in your prefetchs in the first place if you can afford to filter on retrieval (use the custom filter property of your prefetch elements), or at runtime otherwise, using the FindMatches method for instance