CSLA 4.2 Polymorphism

CSLA 4.2 Polymorphism

Old forum URL: forums.lhotka.net/forums/t/10976.aspx


Russ posted on Monday, December 12, 2011

Hi everyone,

 

I am using CSLA 4.2.1, bxf, MVVM, SL4 and have created a LineItem editable child object that contains a child list that is lazy loaded. The class signature is:

 

public class ChildItemROL : ReadOnlyListBase<ChildItemROL, ChildItem1ROC> { }

 

This works as expected. The problem is that the type of LineItem determines the type of child list to be loaded. Here is an example of a few Child Item read only child classes:

 

public class ChildItem1ROC : ReadOnlyBase<ChildItem1ROC> { }

public class ChildItem2ROC : ReadOnlyBase<ChildItem2ROC> { }

public class ChildItem3ROC : ReadOnlyBase<ChildItem3ROC> { }

 

I tried to implement polymorphism using the following blog from Rocky:

 

http://www.lhotka.net/Article.aspx?id=b8515cd9-7f8e-43df-9efd-cd958dfdc21a

 

I have spent a week tinkering with this and have not been able to get it to work.  I first tried changing the read only list signature to:

 

public class ChildItemROL : ReadOnlyListBase<ChildItemROL, Csla.Core.IReadOnlyObject> { }

 

This will compile and appears to load the correct child data by it is unable to pass it across the data portal. I get the following error:

 

Cannot serialize collections not of type IMobileObject

 

So I created my own empty interface that implements the IReadOnlyObject and IMobileObject interfaces:

 

public interface IChildItemROC : IReadOnlyObject, IMobileObject { }

 

Then I changed my read only list signature to:

 

public class ChildItemROL : ReadOnlyListBase<ChildItemROL, IChildItemROC>

 

and the child item classes to:

 

public class ChildItem1ROC : ReadOnlyBase<ChildItem1ROC>, IChildItemROC { }

public class ChildItem2ROC : ReadOnlyBase<ChildItem2ROC>, IChildItemROC { }

public class ChildItem3ROC : ReadOnlyBase<ChildItem3ROC>, IChildItemROC { }

 

It now runs without any error and appears to load the correct data however there is something missing that is causing the binding to not be notified that the data has completed loading.

 

Am I on the right track here or have I ventured completely off the path?

 

Thanks

Russ

Russ replied on Monday, December 12, 2011

Still struggling with this one. Rocky’s “Creating a collection with polymorphic child objects“ blog states that “This interface must derive from Csla.Core.IEditableBusinessObject.” So I have replaced the ReadOnly classes with Business classes as follows:

 

public interface IChildItemEC : IEditableBusinessObject, IMobileObject { }

 

public class ChildItemECL : BusinessListBase<ChildItemECL, IChildItemEC>

 

public class ChildItem1EC : BusinessBase<ChildItem1EC>, IChildItemEC { }

public class ChildItem2EC : BusinessBase<ChildItem2EC>, IChildItemEC { }

public class ChildItem3EC : BusinessBase<ChildItem3EC>, IChildItemEC { }

 

I get exactly the same results; the UI is not updated after the data has been loaded.

 

Does anyone know how to create a collection with polymorphic child objects using CSLA 4.2?

 

Thanks again

Russ

 

 

 

 

JonnyBee replied on Monday, December 12, 2011

Hi Russ,

In your lazy loading of the property - you MUST make sure that OnPropertyChanged (or PropertyHasChanged) is called to notify the UI of a new value to the property.

PropertyHasChanged will:

or just OnPropertyChanged to notify UI.

Russ replied on Monday, December 12, 2011

Thanks for the reply Jonny.

 

I’m convinced this is not a Lazy Loading, MVVM, bxf or DataBinding issue because I can simply change the read only list signature from:

 

public class ChildItemROL : ReadOnlyListBase<ChildItemROL, IChildItemROC>

 

to:

 

public class ChildItemROL : ReadOnlyListBase<ChildItemROL, ChildItem1ROC>

 

and it works perfectly!  Problem is that the child list is now limited to only child items of type ChildItem1ROC.

 

To reproduce the problem simply change any read only list that you have by replacing the “C” part of the ReadOnlyListBase<T, C> with Csla.Core.IReadOnlyObject.

 

I do not understand why an interface is required to implement the IMobileObject interface since all the ChildItem classes are created as subclasses of ReadOnlyBase.

 

Thanks

Russ

JonnyBee replied on Monday, December 12, 2011

Did you define the common properties in IChildItemROC?

  public interface IChildItemROC : IReadOnlyObjectIMobileObject
  {
    string Name { get; }
    string Address { get; }
  }
 
  [Serializable]
  public class ChildItemROC<T> : Csla.ReadOnlyBase<T>, IChildItemROC where T:ChildItemROC<T>
  {
    // define common properties 
    public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      private set { LoadProperty(NameProperty, value); }
    }
 
    public static readonly PropertyInfo<string> AddressProperty = RegisterProperty<string>(c => c.Address);
    public string Address
    {
      get { return GetProperty(AddressProperty); }
      private set { LoadProperty(AddressProperty, value); }
    }
  }
 
  [Serializable]
  public class ChildItemROC1 : ChildItemROC<ChildItemROC1>
  {
    public static readonly PropertyInfo<string> StateProperty = RegisterProperty<string>(c => c.State);
    public string State
    {
      get { return GetProperty(StateProperty); }
      private set { LoadProperty(StateProperty, value); }
    }
  }
 
  [Serializable]
  public class ChildItemROC2 : ChildItemROC<ChildItemROC2>
  {
    public static readonly PropertyInfo<string> CountryProperty = RegisterProperty<string>(c => c.Country);
    public string Country
    {
      get { return GetProperty(CountryProperty); }
      private set { LoadProperty(CountryProperty, value); }
    }
  }
 
  [Serializable]
  public class ChildItemList : ReadOnlyListBase<ChildItemListIChildItemROC>
  {
 
  }

I wouldn't be surprised that using Csla.Core.IReadOnlyObject is causing problems -  you must define the common behavior in the Interface in order to expose these to consumer of your list.

Could you provide a small sample download for investigation?

 

 

Russ replied on Monday, December 12, 2011

Hi Jonny,

 

I added the common property “Name” as in your example to see if it would affect anything and now the childItem Datagrid is displaying a single column for “Name”. The DataGrid AutoGenerateColumns is set to true but the DataGrid is not generating any of the specific childitem properties (State, Country in your example).

 

Maybe this is a weird Silverlight DataGrid binding thing. I have a SL4 UserControl that displays a DataGrid where the ItemsSource is the LineItems read only list. When a user clicks on a row in this DataGrid the RowDetails area is displayed and another DataGrid is displayed.  Here is the XAML:

 

<sdk:DataGrid.RowDetailsTemplate>

  <DataTemplate>

    <sdk:DataGrid x:Name="childListDataGrid" Margin="10" AutoGenerateColumns="True"

            ItemsSource="{Binding Path=ChildList}">

    </sdk:DataGrid>

  </DataTemplate>

</sdk:DataGrid.RowDetailsTemplate>

 

This triggers the lazy loading of the ChildList property. It appears the AutoGenerateColumns=”True” mechanism which uses reflection to create a column for each property of the bound object is interrogating the bound object before it has returned the collection and therefore only contains the common property “Name”.

 

I ruled out the lazy loading getter code by commenting it out and moving the ChildList load logic to the Child_Fetch on the ListItem. I observed the very same DataGrid column binding behavior.

 

I was hoping to get this AutoGenerateColumn mechanism to work. Otherwise I will have to create a DataTemplateSelector for Silverlight and then have to predefine DataTemplates for all the ChildItem types. A lot of seemingly unnecessary XAML that will need to be maintained. L

 

I’m not sure how to easily create a small sample for you as this problem is part of a larger project. Maybe I can start with a copy of one of the Sample CSLA Silverlight applications and modify it. Can you recommend which sample to start with?

 

Thanks

Russ

JonnyBee replied on Tuesday, December 13, 2011

Hi Russ,

This is how polymorfism works with general interfaces and base classes.

Your code does not guarantee that there is only one type of child in the list!!!

Any object that inplements the IChildItemROC can be added to the list and the ony safe data fields to display is those defined in the interface.
It would be perectly legal (code wise) to add objects of type ChildItemROC1 , ChildItemROC2 and ChildItemROC3 to the same instance of the list.

Russ replied on Tuesday, December 13, 2011

Hi Jonny,

 

I came to the same conclusion as you after sleeping on it.

 

The use of the interface allows my ChildList to contain a variety of ChildItemROC1, ChildItemROC2 etc. objects in the same collection. This is not what I require because all the items in my child list are always the same for any given LineItem.

 

My code is working correctly and is lazy loading the required list for each LineItem. In order to correctly display a DataGrid I believe I will need to set AutoGenerateColumns to false to prevent the creation of the columns when the DataGrid is layed out and then listen for the Model.ChildList property changed event to trigger the creation of the correct DataGrid columns for the first row in the returned ChildList.  I’ve looked into this a bit and it does not look pretty!

 

Thanks again for all the help

Russ.

Troncho replied on Wednesday, May 23, 2012

Hi, Jonny, hi everyone.

Back to CSLA 3.8, using this example you are providing.

When I use a BusinessListBase<,> usually, the list gets populated by receiving from the header BusinessBase<> object an array of DAL line objects that were loaded from Linq to Sql. The list passes each line object to the static method included on the line object to get initialized and marked as child. This is done by calling for example:

static ChildObject Get[..]ChildObject(..dalObject obj)

{
	return DataPortal.FetchChild<ChildObject>(obj);
}

Now, this is a static method call, which cannot go to the line item Interface. JOnny, how can we solve this issue?

Thanks,

JonnyBee replied on Wednesday, May 23, 2012

I'd create a static <Child>Creator class that would inspect the DAL object and create the proper child type.

So something like this:

    public class ChildCreator
    {
        public static IChild Fetch(dalItem dalObject)
        {
            
            if (dalObject.X == 1
                return DataPortal.FetchChild<ChildType1>(dalObject);
            if (dalObject.X == 2
                return DataPortal.FetchChild<ChildType2>(dalObject);
            
            throw new ArgumentException("Unknown child type");
        }
    }

and the list like:

  [Serializable]
  public class ChildBusinessList :
    BusinessListBase<ChildBusinessList, IChild>
  {
    public static ChildBusinessList GetEditableRootList(int id)
    {
      return DataPortal.Fetch<ChildBusinessList>(id);
    }

    private void DataPortal_Fetch(int criteria)
    {
      RaiseListChangedEvents = false;

     // Retrive dalList objects by Linq 2 Sql
     
     // load child objects 
     foreach (var item in dalList) {
         Add(ChildCreator.Fetch(item));
     }

      RaiseListChangedEvents = true;
    }
  }

Troncho replied on Wednesday, May 23, 2012

Now there's an elegant solution! Your accurate and detailed help is very appreciated Big Smile

Once again: THANK YOU VERY MUCH!

Troncho

Copyright (c) Marimer LLC