Binding inherited collection / inherited entity to a GridView

Posts   
 
    
nyathancha
User
Posts: 17
Joined: 19-Jan-2007
# Posted on: 19-Jan-2007 08:49:52   

Hi,

I am having some trouble binding a CollectionClass to GridView where the collection contains entities that are derived from the Generated entities. To give you an example, I've written this toy application using the northwind database. Lets say, I want to add some more properties/fields to the CustomerEntity. I create a class that inhertis from the CustomerEntity and adds some functionality.


    public class DecoratedCustomer : ORMTester.NorthWind.EntityClasses.CustomersEntity
    {

        public DecoratedCustomer(string customerID)
            : base(customerID)
        { 
        }

        public string LuckyNumber
        {
            get { 
        return "13"; 
        /* Custom functionality here. Quite possibly based on any data in the particular customer instance */ 
        }
        
        }


    }


I create a custom collection (based on the CustomersCollection class) that can give me these decorated classes


    public class AggregatedCustomerCollection : ORMTester.NorthWind.CollectionClasses.CustomersCollection
    {


        public new System.Collections.IEnumerator GetEnumerator()
        {
            foreach (ORMTester.NorthWind.EntityClasses.CustomersEntity currentCustomer in base.Items)
            {
                DecoratedCustomer wrappedUpCustomer = new DecoratedCustomer(currentCustomer.CustomerId);
                yield return wrappedUpCustomer;
            }
        }

    }


Here, I am overwriting the enumerator of the customerscollection class to return the DecoratedCustomer instead of the base customer.

And to bind it to the GridView, I am doing the following :



    ORMTester.DBLayer.AggregatedCustomerCollection allCustomers = new ORMTester.DBLayer.AggregatedCustomerCollection();
    allCustomers.GetMulti(null);
    
    TestGridView.DataSource = allCustomers;
    TestGridView.DataBind();


When the bound columns only access the fields in the base customer collection, this works fine. But as soon as I try to access the custom field ("LuckyNumber"), I get an error saying that LuckyNumber is not present



    <asp:BoundField DataField="LuckyNumber" HeaderText="LuckyNumber" SortExpression="LuckyNumber" />


This seems to indicate that the AggregateCustomerCollection's enumerator is still returning the base Customer class (or casting whats returned into the base customer class) rather than returning the decorated customer class.

Is there some other interface I should implement or field/type I should set/override to return the decorated customer class? What am I missing/overlooking here?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 20-Jan-2007 12:04:58   

Do you get a call to your GetEnumerator? (in the debugger) ?

Frans Bouma | Lead developer LLBLGen Pro
nyathancha
User
Posts: 17
Joined: 19-Jan-2007
# Posted on: 22-Jan-2007 00:52:56   

No, it never gets to that stage. It throws an exception at the DataBind() method saying "field or property with the name LuckyNumber was not found in the data source"

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 22-Jan-2007 08:11:45   

Which runtimeLibrary version are you using?

nyathancha
User
Posts: 17
Joined: 19-Jan-2007
# Posted on: 22-Jan-2007 08:58:09   

Designer version: 2.0.0.0 Runtime libraries version: 2.0.0.0

obviously using .Net 2.0 as well.

I see that the generic entity collection base is passed the CustomersEntity class


public partial class CustomersCollection : EntityCollectionBase<CustomersEntity>

This probably is why the GridView is seeing it as a collection of CustomersEntity (and checking the properties of the CustomersEntity class) rather than as a collection of DecoratedCustomers.

My "AggregatedCustomerCollection" is already inheriting from CustomersCollection, how do I override the type thats passed to the Underlying CollectionBase? I don't think there really is a way and I can't inherit from EntityCollectionBase again with the <DecoratedCustomers> as the type parameter.

The only thing I can think of doing is creating a new collection called DecoratedCusomersCollection that inherits from EntityCollectionBase and pass it the DecoratedCustomer as the type parameter. But this means that I will lose all the functionality built into the CustomersCollection (and will have to re implement).

Maybe this is more of .Net question, but is there someway I specify that the type of the collection is "DecoratedCustomer" class instead of "Customer" (because in the new GetEnumerator, I am returning "DecoratedCustomer"s not "Customers")

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 22-Jan-2007 12:11:26   

nyathancha wrote:

Designer version: 2.0.0.0 Runtime libraries version: 2.0.0.0

obviously using .Net 2.0 as well.

I see that the generic entity collection base is passed the CustomersEntity class


public partial class CustomersCollection : EntityCollectionBase<CustomersEntity>

This probably is why the GridView is seeing it as a collection of CustomersEntity (and checking the properties of the CustomersEntity class) rather than as a collection of DecoratedCustomers.

My "AggregatedCustomerCollection" is already inheriting from CustomersCollection, how do I override the type thats passed to the Underlying CollectionBase? I don't think there really is a way and I can't inherit from EntityCollectionBase again with the <DecoratedCustomers> as the type parameter.

That's indeed a problem, which I've struggled with a long time as well. The main problem is that C# and VB.NET don't support covariance, so List<string> isn't a subtype of List<object> (and thus you can't cast List<string> to List<object>)

I'd either do: - add the property to a partial class file of the CustomerEntity or - derive a class from CustomerEntity and add the property to that class.

The first is the easiest. If you go for the second option, you have to pass in a factory to the collection which produces your derived customer entity classes instead.

For selfservicing I had to keep the typed collections for backwards compatibility. I don't like the typed name myself, but there was simply no other option, it would otherwise have broken too many existing projects and upgrading would have been a nightmare.

Frans Bouma | Lead developer LLBLGen Pro
nyathancha
User
Posts: 17
Joined: 19-Jan-2007
# Posted on: 24-Jan-2007 06:45:28   

Problem with partial classes is that it requires me to put my "Extension" code in the same project as the one generated by LLBLGen (since partial classes can't span projects). I would prefer to leave that class untouched if i can.

I ended up using composition instead of inheritance.


    public class DecoratedCustomer  
    {
    ORMTester.NorthWind.EntityClasses.CustomersEntity CurrentCustomer;

        public DecoratedCustomer(ORMTester.NorthWind.EntityClasses.CustomersEntity InputCustomer)
        {
        CurrentCustomer = InputCustomer
        }

    public ORMTester.NorthWind.EntityClasses.CustomersEntity CustomerEntity
    {
    get {return CurrentCustomer;}
    }

        public string LuckyNumber
        {
            get {
        return "13";
        /* Custom functionality here. Quite possibly based on any data in the particular customer instance */
        }
        
        }

    }


and for the collection




    public class AggregatedCustomerCollection
    {
    ORMTester.NorthWind.CollectionClasses.CustomersCollection CurrentCollection;

    public AggregatedCustomerCollection(ORMTester.NorthWind.CollectionClasses.CustomersCollection InputCollection)
    {
    CurrentCollection = InputCollection;
    }

        public new System.Collections.IEnumerator GetEnumerator()
        {
            foreach (ORMTester.NorthWind.EntityClasses.CustomersEntity currentCustomer in CurrentCollection)
            {
                DecoratedCustomer wrappedUpCustomer = new DecoratedCustomer(currentCustomer);
                yield return wrappedUpCustomer;
            }
        }

    }



and when binding it to grid view



    ORMTester.NorthWind.CollectionClasses.CustomersCollection CurrentCollection;
        CurrentCollection.GetMulti(null);

        ORMTester.DBLayer.AggregatedCustomerCollection allCustomers = new ORMTester.DBLayer.AggregatedCustomerCollection(CurrentCollection);

    
        TestGridView.DataSource = allCustomers;
        TestGridView.DataBind();


I don't particularly like this solution as I am creating a new type. And I can't use the SD.LLBLGen.Pro.ORMSupportClasses.LLBLGenProDataSource object and sort of stuffs up the heirarchy tree a bit.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 24-Jan-2007 07:57:21   

That's another good solution, but I would have gone to the partial classes option. It's very easy and it won't affect the generated code, even after re-generation, your partial class will remain untouched.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 24-Jan-2007 11:37:52   

I'd also go for the partial class option, and store the extensions in a different folder in the project, similar to what I've done to the HnD sourcecode where I had to extend a typedlist class.

Frans Bouma | Lead developer LLBLGen Pro