Polymorphic Lists in Csla 4.0.1

Polymorphic Lists in Csla 4.0.1

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


comp1mp posted on Wednesday, June 12, 2013

Hi,

I have followed the guidelines suggested by Rocky for implementing a polymorphic Csla List.

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

 

What is not described in above article is how to implement adding different types to the list.

Factory methods of list child objects should be internal, so I am assuming that one needs to expose public methods for adding on the list itself.

MyList.AddConcreteType1(ConcreteType1 params)

MyList.AddConcreteType2(ConcreteType2 params)

 

Am I missing something,? Is there a cleaner and slicker way to do this?

Thanks!

 

 

JonnyBee replied on Wednesday, June 12, 2013

Hi.

My preference is to create public factory methods on the child objects.

The list should IMO not have any knowledge of the possible child types.
IE: You should be able to add new child types without modification to the list object.

comp1mp replied on Wednesday, June 12, 2013

Hi Jonny

That was my initial thought, but then I started thinking how I have never implemented a child object with a public factory. The CSLA4 templates themselves have all children implementing internal factories.

Is potential misuse by a client developer an issue with a public factory on a child? It just seems that allowing creation of a child instance outside of its parent is inviting problems.

 

JonnyBee replied on Wednesday, June 12, 2013

Hi,

When you do lazy loading you need a child object with a "root" public factory method and make sure to call MarkAsChild. This will ensure that other developers cannot call Save on the object.

 Some code must know which type of child to add. My preference is that the list should not require changes in order to accept a new child type. 

comp1mp replied on Wednesday, June 12, 2013

Ahh I see. I have never used lazy loaded properties before. I just assumed that a child object's factory must be internal.

Nice to see protection from client misuse is built in.

Thanks for your help!

tiago replied on Wednesday, June 12, 2013

JonnyBee

When you do lazy loading you need a child object with a "root" public factory method and make sure to call MarkAsChild. This will ensure that other developers cannot call Save on the object.

 

Hi Jonny,

As far as I understand, the factory method will be called by the property getter on the parent object. So why public factory method?

tiago replied on Wednesday, June 12, 2013

BTW this a very interesting thread!

I was just re-evaluating the "custom fields" question and a list of polymorphic objects is a good starting point.

The requisite goes like this:

Different kind of documents need different custom fields that may be defined by configuration.

As the custom fields need to be defined by configuration, this must be handled at runtime. All the application can do is make available different types of custom field: text, date, money, integer, etc.

comp1mp replied on Thursday, June 13, 2013

This is a first for me in these forums. I don't know if the proper thing was to mark it as unanswered again. Let me know if I should have submitted a second question.

Tiago brings up a a good point. 

I referenced the Expert C# Business Objects 2008, and it seems to support what Tiago is saying. In discussing lazy loaded properties, Rocky describes 2 scenarios. In both scenarios...

"The factory method (internal scope) is called."  (referring to the lazy loaded property.)

This is on page 151.

 

tiago replied on Thursday, June 13, 2013

Concerning the polymorphic list I think Jonny is write: the list shouldn't know about the item's type. So you should add items to the collection like this:

Add(item);

So your items must be already made when you add them to the list. For creating and fetching the items, you should use the factory methods. I think you shouldn't make the factory methods public. I prefer to have a public creator object that is responsible for creating and adding items to the list.

After creating and saving your items and now you need to fetch them in order to show/edit them. Again the list shouldn't know about the item's type. Again a getter object should be use but I believe this time it doesn't need to be public.

As I said before, very interesting thread Big Smile

comp1mp replied on Thursday, June 13, 2013

Hi Tiago,

I agree with you and Jonny about the list not knowing anything about the objects it contains.

In my particular case, the client application dictates which type of object is going to be added to the list. As you point out, this means i must have reference to the child object in order to call Add. 

I don't see any difference between making a factory public, and the Creator object as you described. The bottom line is, as a client developer I can now create reference to a child object without needing a list.

Is this bad? I don't know.  I guess if a client developer was confused and trying to use it for anything else other than adding items to a list it could be an issue.

About your Creator object. Does it return reference to the child object as I perceived? Or do you have a method where you pass the list in and it creates the object locally and adds it?

 

 

tiago replied on Thursday, June 13, 2013

Matthew Copeland

About your Creator object. Does it return reference to the child object as I perceived? Or do you have a method where you pass the list in and it creates the object locally and adds it?

 

Hi Matthew

I changed the generated code as little as I could. On the collection class I changed only one line

private void Fetch(SafeDataReader dr)
{
    var rlce = RaiseListChangedEvents;
    RaiseListChangedEvents = false;
    while (dr.Read())
    {
        //Add(DataPortal.FetchChild<ICustomField>(dr));
        Add(DocCustomFieldCreator.CreateCustomField(dr));
    }
    RaiseListChangedEvents = rlce;
}

The static CreateCustomField method loads a DTO with DB data and according to the CustomFieldTypeName returns by calling the relevant internal static factory method, passing the DTO as parameter.

switch (docCustomFieldDto.CustomFieldTypeName)
{
    case "Int32":
        return DocCustomFieldInteger.LoadDocCustomFieldInteger(docCustomFieldDto);
    case "Date":
        return DocCustomFieldSmartDate.LoadDocCustomFieldSmartDate(docCustomFieldDto);
    default:
        return DocCustomFieldString.LoadDocCustomFieldString(docCustomFieldDto);
}

In this case I'm populating the collection and I don't need to expose anything at all.

 

comp1mp replied on Thursday, June 13, 2013

I did not provide an adequate background in my initial question.

This project does not have a DAL layer or utilize object factories. We are tightly coupled to a database technology. It is too small of a project and has time constraints which do not allow that level of implementation.

I have an abbreviated version of what I have done but would like to post the code in the manner that you have. How did you accomplish that? I tried using the Word paste function, but it did not work.  

comp1mp replied on Thursday, June 13, 2013

Here is the code. I am using a screen shot, if there is a better way to add formatted code please inform me :).

Given the fact we have no DAL or object factories...

My question now - is the following a poor implementation of a polymorphic list just because it is so tightly coupled? If so are their any options other than object factory / DAL?

(There is a typo in the second Add of fetch pseudo code it should read Add(ripCut) not Add(setup))

tiago replied on Sunday, June 16, 2013

Hi Matthew,

Your Child_Fetch knows about two different sets of properties. You have two "select" each one followed by a loop adding distinct child types. From what I see, I imagine that on

"setup" and "ripCut" must be nethods that populate each child type, element by element and return a collection of children. So "Add" should be "AddRange".

I prefer the approach of having a single select, followed by a single loop that calls the loader (or getter) object. This object handles the child type difference and returns a ready made child, thus making the collection completely unaware of the item details

When you need to change some item detail or add a new item type, you don't change the collection's code but just the getter object code.

Since we started this thread I implemented a polymorphic collection of custom properties as explained above. In my case I also needed to handle the creating part, as collection items are created on root object save. I must say it all works quite well except I only created a few item types. Now I need to create the others and for each new item type, I know I only have to add an entry in the switch statement. Quite easy to maintain...

comp1mp replied on Sunday, June 16, 2013

Hi Tiago,

Tiago Freitas Leal

Your Child_Fetch knows about two different sets of properties. You have two "select" each one followed by a loop adding distinct child types. From what I see, I imagine that on

"setup" and "ripCut" must be nethods that populate each child type, element by element and return a collection of children. So "Add" should be "AddRange".

Actually the pseudo code should have been more specific. I am calling internal factory method on the type being retrieved from the database,  passing the datareader with each call. It just returns a single object. It builds the collection because it is contained in the while.

while (dr.Read()) Add(Setup.GetEditableChild(dr) )

Tiago Freitas Leal
I prefer the approach of having a single select, followed by a single loop that calls the loader (or getter) object. This object handles the child type difference and returns a ready made child, thus making the collection completely unaware of the item details

I am not able to have a single select. Each type that implements ITask is different, with different data, stored in different tables. I cannot produce a same shaped datareader containing the data for all of the different types.

I think the mismatch may be that your solution deals with value types while I am talking about complex object types.

Do you have a single table with a CustomPropertyType column and then a column for each value type to contain the actual data?

If not, how are you "shaping" your datareader?

comp1mp replied on Tuesday, June 18, 2013

Based upon the response to this thread, I am left to believe that the story for working with polymorphic CSLA lists is incomplete.

Currently, there is not a clean method for adding polymorphic complex objects to the list, other than exposing a public factory on a child object. While this works, it breaks CSLA encapsulation in my opinion.

It may be the inherent use of generics in CSLA business objects makes a clean solution impossible.

tiago replied on Tuesday, June 18, 2013

Matthew Copeland

Currently, there is not a clean method for adding polymorphic complex objects to the list, other than exposing a public factory on a child object. While this works, it breaks CSLA encapsulation in my opinion.

Hi Matthew,

How come you need a public factory method? Why isn't internal enough?

comp1mp replied on Friday, June 21, 2013

Hi Tiago,

Sorry for delayed response. Things got busy at work :).

Tiago Freitas Leal

Hi Matthew,

How come you need a public factory method? Why isn't internal enough?

The user chooses what types are added to the list, so the ability to add any new type of ITask must be exposed to the UI developer. Instead of making the factory for all types of ITask public, I have exposed a method from the list (ie AddSetup, AddRipcut etc.) We thus keep the child factories internal.

We are using SQLite so we do not have the luxury of abstracting the database call into a single stored procedure returning multiple result sets with a jobid as the parameter. We have to execute separate SQL statements for each type.

 This list semantically is used for managing types of ITask, and after some time to contemplate, I am comfortable with tight coupling and the list containing all of the top level management logic as illustrated by my sample code. In my opinion, it is the natural place to make maintenance changes whenever new types are added or current types are changed. Ultimately, we could factor out the code into a helper class as described by you and Jonny. But this code would still reside in the business dll and require recompilation while adding one more non-intuitive type to be managed during maintenance. Perhaps coupling is not so evil in this case?

Thank you both for your thoughtful responses.

 

tiago replied on Tuesday, June 18, 2013

Matthew Copeland

I am not able to have a single select. Each type that implements ITask is different, with different data, stored in different tables. I cannot produce a same shaped datareader containing the data for all of the different types.

I think the mismatch may be that your solution deals with value types while I am talking about complex object types.

Do you have a single table with a CustomPropertyType column and then a column for each value type to contain the actual data?

If not, how are you "shaping" your datareader?

 

Hi Matthew,

Currently I'm using a single varchar column to store all kinds of values: int32, string, SmartDate, etc That's a choice I've made based on the principle "make the simplest thing that could work".

This choice can be changed so there is one database column for each data type. The fetch issue can be solved in two ways.

1) single result set that includes all columns - simpler but inefficient (since there are always meaningless columns)

2) each child type is populated by a different result set  efficient but more complex

While option 1) needs small code changes only, option 2) needs a serious refactoring. To be more specific,

while (dr.Read())
{
    //Add(DataPortal.FetchChild<ICustomField>(dr));
    Add(DocCustomFieldHelper.CustomFieldGetter(dr));
}

needs to be refactored to something that moves to the next result set, until you are out of result sets. Note that the core

Add(DocCustomFieldHelper.CustomFieldGetter(dr));

doesn't need to be changed, in order to keep the parent collection agostic about its child/item types (and respect the "separation of concerns" principle).

All the dirty work of populatiing each child/item type is done by the method

internal static ICustomField CustomFieldGetter(SafeDataReader dr)

and that method needs big changes in order to accomodate for child types that might have big or not so big differences. Using a DTO might be usefull or it can prove useless. On the later case, the switch statement should pass the SafeDateReader instance to the factory method. These are implementation details, so I won't discuss them further.

EDIT

Note option 2) can be used for very different child/item types, including child that fecth data from different database tables, whatsoever.

Copyright (c) Marimer LLC