Related entity collection of derived class

Posts   
 
    
chrishilton avatar
Posts: 49
Joined: 26-Jun-2007
# Posted on: 26-Jun-2007 17:40:43   

I'm using LLBLGen Pro 2.0.0.0 Final with adaptor and .NET 2.0 connecting to a SQL Server 2005 dB.

I have created a set of derived classes that inherit from the entity classes created by the designer, for instance my ViewGroup table produces an ViewGroupEntity class and my derived class is called ViewGroup. In my derived classes I have added business logic code, additional methods and properties and overriden methods from the Entity classes.

The ViewGroup table is linked to the ViewEntry table with a one to many relationship so my ViewGroupEntity object contains an ViewEntry EntityCollection of related entities of type ViewEntryEntity.

At present using my derived class ViewGroup, when I fetch the related entities the class inherits the ViewEntry collection from the ViewGroupEntity class so I have a collection of ViewEntryEntity objects. My question is whether there is a way for my derived class of ViewGroup to have a ViewEntry EntityCollection containing my derived ViewEntry objects?

At present when iterating through the ViewEntry EntityCollection I am creating new ViewEntry objects from the ViewEntryEntity objects in order to access my additional methods etc. but this is proving cumbersome.

I am also keen to retain the ability to persist additions and amendments made to the ViewEntry EntityCollection to the database when saving the ViewGroup object.

Any help would be greatly appreciated as this is the first query that I've had that I haven't been able to find a definite answer to from a previous post of the forums.

Cheers, Chris

JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 27-Jun-2007 02:51:54   

Hi Chris,

If I understand what it is your asking then you should be able to resolve it using casting.

When you have a collection class 'colA' of objects from class 'A', then you derive 'B' from 'A', then 'B' objects can still be stored in the collection as you mention, but when you retrieve them (and assuming you know they are all of type 'B') then you can simply cast them back to the 'B' object. i.e. foreach (MyViewEntry mve in ViewEntryCollection) ... is valid so long as MyViewEntry is derived from ViewEntryEntity, or if you want to use a single item then obviously just use '(MyViewEntry)ViewEntryCollection[0]' (or use something like 'ViewEntryCollection(0) As MyViewEntry' in VB.Net).

Cheers, Jason

chrishilton avatar
Posts: 49
Joined: 26-Jun-2007
# Posted on: 27-Jun-2007 11:05:48   

JSobell wrote:

If I understand what it is your asking then you should be able to resolve it using casting.

When you have a collection class 'colA' of objects from class 'A', then you derive 'B' from 'A', then 'B' objects can still be stored in the collection as you mention, but when you retrieve them (and assuming you know they are all of type 'B') then you can simply cast them back to the 'B' object. i.e. foreach (MyViewEntry mve in ViewEntryCollection) ... is valid so long as MyViewEntry is derived from ViewEntryEntity, or if you want to use a single item then obviously just use '(MyViewEntry)ViewEntryCollection[0]' (or use something like 'ViewEntryCollection(0) As MyViewEntry' in VB.Net).

Thanks for your reply Jason but unfortunately a straight cast is not possible as it raises an InvalidCastException: Unable to cast object of type ViewEntryEntity to type ViewEntry. I have already resolved this by creating a cTor for my ViewEntry class which sets field values and related entities based on a ViewEntryEntity.

This solution is workable but doesn't help if, for instance, I want to use my ViewGroup.ViewEntry EntityCollection as the datasource of DataGridView when what I really want is a collection of derived ViewEntry objects rather than ViewEntryEntity objects.

So I'm back to my original question of whether there is a way for my derived class of ViewGroup to have a ViewEntry EntityCollection containing my derived ViewEntry objects?

Chris

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 28-Jun-2007 12:21:23   

What you're creating is a parallel hierarchy. (ascii art coming up simple_smile )

1) ViewGroupEntity ---1:n-- ViewEntryEntity ^ ^ | | 2) ViewGroup ----1:n----ViewEntry

1 is parallel to 2.

The problem you run into is solved with the 'derived entity classes for adapter'. These classes are prefixed by default with 'My', but you can alter the templates to remove 'My' and simply opt to drop the 'Entity' suffix instead. The templates aren't that big. Could you see if these templates offer the flexibility for you? (they generate the derived classes which are pretty small and to which you can add a partial class with your own code per entity).

These classes let you use the hierarchy in parallel, so ViewGroup.ViewEntries does have ViewEntry instances and not ViewEntryEntity instances.

Personally, I'd go for partial classes to the generated entity classes to add your BL code instead.

Frans Bouma | Lead developer LLBLGen Pro
chrishilton avatar
Posts: 49
Joined: 26-Jun-2007
# Posted on: 28-Jun-2007 13:14:40   

Thanks for your reply Otis and I agree that the partial classes may well be the best approach. The main reason for seeking an alternative was the fact that I then loose the ability to override the properties in the generated entity classes.

Can I just check that I am correctly following your suggested use of 'derived entity classes for adapter':

I have generated my project with the selected preset of SD.Presets.Adaptor.TwoClasses2005 which creates me an EntitySubClasses directory containing the derived classes eg. MyViewGroupEntity. However the ViewEntry EntityCollection of MyViewGroupEntity still contains ViewEntryEntity objects so have I missed something out?

Cheers, Chris

chrishilton avatar
Posts: 49
Joined: 26-Jun-2007
# Posted on: 29-Jun-2007 14:04:04   

Any chance I can ask another question related to changing my solution structure from derived classes to partial classes?

At present my derived classes are part of a separate ExtendedObjects project that references both the BusinessObjects and BusinessObjectsDBSpecific generated projects. In each of my derived classes I have Save, Fetch and Delete methods. In the case of the Delete method this allows me to specify which related entities are deleted (and the correct order) when deleting the entity eg:


        /// <summary>
        /// Deletes the entity and its related entities from the database
        /// </summary>
        /// <returns>
        /// true if the delete was succesful, false otherwise
        /// </returns>
        public bool Delete()
        {
            using (DataAccessAdapter adapter = new DataAccessAdapter(Config.MainConnectionString()))
            {
                // delete related entities
                {
                    RelationPredicateBucket bucket = new RelationPredicateBucket(
                        ViewEntryFields.ViewGroupId == this.RecId);
                    adapter.DeleteEntitiesDirectly("ViewEntryEntity", bucket);
                }
                // delete entity
                return adapter.DeleteEntity(this);
            }
        }

To replicate these methods in a partial Entity class the BusinessObjects project will have to reference the BusinessObjectsDBSpecific project resulting, it would seem, in the BusinessObjects project no longer being "DatabaseGeneric". As such these methods don't seem to sit as well in the ViewGroupEntity class as they did in the dervied ViewGroup class.

Am I right to be concerned about adding the BusinessObjectsDBSpecific reference to the BusinessObjects project? Can anyone recommend an alternative approach if this one is flawed?

Cheers, Chris

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 29-Jun-2007 15:17:53   

Am I right to be concerned about adding the BusinessObjectsDBSpecific reference to the BusinessObjects project? Can anyone recommend an alternative approach if this one is flawed?

IMHO, your concern is valid. Cascading deletes can be implemented in the database, but in general, scenarioes like these are better to be implemented in the BL (Manager Classes for instance).

LukeO
User
Posts: 58
Joined: 23-Jul-2007
# Posted on: 02-Aug-2007 19:40:09   

Another question about this. And it concerns EntityCollections.

I have exactly the same situation as described in the ASCII ch-art below.

Here is some of the auto-code generated stuff

        ''' <summary>Gets the EntityCollection with the related entities of type 'PlanOption' which are related to this entity via a relation of type '1:n'.
        ''' If the EntityCollection hasn't been fetched yet, the collection returned will be empty.</summary>
        <TypeContainedAttribute(GetType(PlanOption))> _
        Public Overrides ReadOnly Property [PlanOptions]() As EntityCollection(Of PlanOptionEntity)
            Get
                Dim toReturn As EntityCollection(Of PlanOptionEntity) = MyBase.PlanOptions
                LoadPlanOptionsData(toReturn)
                toReturn.EntityFactoryToUse = New PlanOptionFactory()
                Return toReturn
            End Get
        End Property

This snippet is in my Plan class which derives from PlanEntity.

So this works:

                For Each aplanoption As PlanOptionEntity In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

But obviously this gives me a cast exception

                For Each aplanoption As PlanOption In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

Note that PlanOption derives from PlanOptionEntity.

My goal here is to allow my derived classes to have collections of derived classes. What am I doing wrong?

Thanks, -Luke

Otis wrote:

What you're creating is a parallel hierarchy. (ascii art coming up simple_smile )

1) ViewGroupEntity ---1:n-- ViewEntryEntity ^ ^ | | 2) ViewGroup ----1:n----ViewEntry

1 is parallel to 2.

The problem you run into is solved with the 'derived entity classes for adapter'. These classes are prefixed by default with 'My', but you can alter the templates to remove 'My' and simply opt to drop the 'Entity' suffix instead. The templates aren't that big. Could you see if these templates offer the flexibility for you? (they generate the derived classes which are pretty small and to which you can add a partial class with your own code per entity).

These classes let you use the hierarchy in parallel, so ViewGroup.ViewEntries does have ViewEntry instances and not ViewEntryEntity instances.

Personally, I'd go for partial classes to the generated entity classes to add your BL code instead.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 03-Aug-2007 09:36:27   

Would you please create a new thread as described here: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7722

Anyway, as far as I know, you can't cast from a base type to a sub type.

pat
User
Posts: 215
Joined: 02-Mar-2006
# Posted on: 07-Aug-2007 00:00:25   

LukeO wrote:

But obviously this gives me a cast exception

                For Each aplanoption As PlanOption In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

Note that PlanOption derives from PlanOptionEntity.

My goal here is to allow my derived classes to have collections of derived classes.

I have exactly the same question and I am not sure why a new thread should be started as this thread seems to explain the problem very well (apart from a few question about not directly related topics).

        ''' <summary>Gets the EntityCollection with the related entities of type 'PlanOption' which are related to this entity via a relation of type '1:n'.
        ''' If the EntityCollection hasn't been fetched yet, the collection returned will be empty.</summary>
        <TypeContainedAttribute(GetType(PlanOption))> _
        Public Overrides ReadOnly Property [PlanOptions]() As EntityCollection(Of PlanOptionEntity)
            Get
                Dim toReturn As EntityCollection(Of PlanOptionEntity) = MyBase.PlanOptions
                LoadPlanOptionsData(toReturn)
                toReturn.EntityFactoryToUse = New PlanOptionFactory()
                Return toReturn
            End Get
        End Property

Please... is there no way that the PlanOptions property could return a collection of PlanOption objects instead of a collection of PlanOptionEntities? The way it is at the moment makes the "Adapter two class" scenario quite confusing. I started putting my business logic into the derived class PlanOptions (or MyPlanOptions) but then when I try to access PlanOptions through a related entity collection it is not possible. This leads to confusing code as some logic ends up in PlanOptionsEntity and some in PlanOptions.

Thank you, Patrick

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 07-Aug-2007 10:24:28   

pat wrote:

LukeO wrote:

But obviously this gives me a cast exception

                For Each aplanoption As PlanOption In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

Note that PlanOption derives from PlanOptionEntity.

My goal here is to allow my derived classes to have collections of derived classes.

I have exactly the same question and I am not sure why a new thread should be started as this thread seems to explain the problem very well (apart from a few question about not directly related topics).

If we ask you to start a new thread, please do so. If people ask random questions in the same thread even YOU think they're related (but they might not be), it's impossible to follow where a NEW problem occurs and which information is relevant for the problem: should we re-read all messages in the thread or just start with the first one of a new person in the thread?

We have this forum now for a couple of years and one thread per question-user combi works best. So if we ask you to start a new thread with YOUR question, please start that new thread. We didn't post our guidelines for nothing: if we don't have enough information or the whole context is becoming too vague because of all info from the people in the same thread confuses the matter, we have to ask questions about YOUR particular context and that takes time, so we can help you less efficient.

If you want that, then please keep on posting in other people's threads.

        ''' <summary>Gets the EntityCollection with the related entities of type 'PlanOption' which are related to this entity via a relation of type '1:n'.
        ''' If the EntityCollection hasn't been fetched yet, the collection returned will be empty.</summary>
        <TypeContainedAttribute(GetType(PlanOption))> _
        Public Overrides ReadOnly Property [PlanOptions]() As EntityCollection(Of PlanOptionEntity)
            Get
                Dim toReturn As EntityCollection(Of PlanOptionEntity) = MyBase.PlanOptions
                LoadPlanOptionsData(toReturn)
                toReturn.EntityFactoryToUse = New PlanOptionFactory()
                Return toReturn
            End Get
        End Property

Please... is there no way that the PlanOptions property could return a collection of PlanOption objects instead of a collection of PlanOptionEntities? The way it is at the moment makes the "Adapter two class" scenario quite confusing. I started putting my business logic into the derived class PlanOptions (or MyPlanOptions) but then when I try to access PlanOptions through a related entity collection it is not possible. This leads to confusing code as some logic ends up in PlanOptionsEntity and some in PlanOptions.

You work on the same project or is your hierarchy slightly different? You now see what's the problem with posting in other peoples threads: I now have to re-read all posts in this thread to try to gather the information about somebody elses hierarchy, project etc. while you could have provided that information yourself so it matches YOUR situation perfectly. I now have to ASSUME the information provided by others not related to your project is the same as your project.

I can tell you, that's a frustrating, time wasting experience simply because it's unclear if the info I gathered is the same for your situation. That's why we ask you to provide AS MUCH information about YOUR situation as possible with REAL code snippets. If you want us to help you, please... help us to help you. We don't have your code in front of us, nor do we have the project model etc. in front of us. If we have to start guessing, we will likely make mistakes and that's not in your interest.

Frans Bouma | Lead developer LLBLGen Pro
pat
User
Posts: 215
Joined: 02-Mar-2006
# Posted on: 07-Aug-2007 19:36:19   

Otis wrote:

If we ask you to start a new thread, please do so. If people ask random questions in the same thread even YOU think they're related (but they might not be), it's impossible to follow where a NEW problem occurs and which information is relevant for the problem: should we re-read all messages in the thread or just start with the first one of a new person in the thread?

Ok point taken - sorry. Starting a new thread with a precise question does make more sense. And looking back over this thread does make it look a bit confusing flushed

Otis wrote:

We have this forum now for a couple of years and one thread per question-user combi works best.

Makes sense.

I guess I felt frustrated because the same question I had for a while was asked twice but there was no answer to it:

chrishilton wrote:

I have generated my project with the selected preset of SD.Presets.Adaptor.TwoClasses2005 which creates me an EntitySubClasses directory containing the derived classes eg. MyViewGroupEntity. H**owever the ViewEntry EntityCollection of MyViewGroupEntity still contains ViewEntryEntity objects **so have I missed something out? Cheers, Chris

LukeO wrote:

So this works:

                For Each aplanoption As PlanOptionEntity In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

But obviously this gives me a cast exception

                For Each aplanoption As PlanOption In yPlan.PlanOptions
                    MsgBox(aplanoption.Funds.Name & " " & aplanoption.Funds.RIAFunds(0).AssetClass.Name)
                Next

Note that PlanOption derives from PlanOptionEntity. **My goal here is to allow my derived classes to have collections of derived classes. **What am I doing wrong?

But I must admit that it got confusing since quite a few different topics were mentioned.

I actually just found the solution to my question and posted it in a new thread http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=10800

Thanks Patrick

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 07-Aug-2007 21:48:52   

Glad things are sorted out now, Patrick smile and thanks for posting the solution for others who might run into this same problem simple_smile

Frans Bouma | Lead developer LLBLGen Pro