Need to modifiy the GetList method int my ReadOnlyList

Need to modifiy the GetList method int my ReadOnlyList

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


RangerGuy posted on Thursday, July 20, 2006

By Default the ReadOnlyList Template has this factory method defined

public static ReadOnlyList GetReadOnlyList(string filter)

{

   return DataPortal.Fetch<ReadOnlyList>(new Criteria(filter));

}

 

I have a readonlylist that is a child of another object so I do not need to send it to the DataPortal for population it just needs to be passed a SafeDataReader and it's parent object for looping criteria. So I want to change it like this

public static ReadOnlyList GetReadOnlyList(ParentObject myparent, SafeDataReader dr)

{

   return DataPortal_Fetch(myparent,dr);

}

OR

Can I just change the GetReadOnlyList method to be like this

public static ReadOnlyList GetReadOnlyList(ParentObject myparent, SafeDataReader dr)

{

   return fetch(myparent,dr);

}

would this have any adverse effects. I'm am fairly new to CSLA 2.0 so please excuse my inexperienced questions :)

ajj3085 replied on Thursday, July 20, 2006

You should never be exposing SafeDataReader in your public methods and properties.  Your static method should provide some way for the UI to specify criteria, and then in your DataPortal_Fetch methods you can use the safe data reader there, but only to load up your business objects with data.

RangerGuy replied on Thursday, July 20, 2006

So I have ReadOnly Parent List that contains ReadOnly Parent Objects. Each of these Parent Objects will have a ReadOnlyList of Children. Like so

|-ParentList

|--Parent

|---Childlist

|----Child

The data for the Child list is contained in the same DataReaderObject as parent result set so we are not returning multiple resultsets. What would be the best method to populate the childlist/objects without having to hit the database again for the info.

xal replied on Thursday, July 20, 2006

RangerGuy, your second option is ok, but you should not expose the method as public. You'd want to make it friend in scope so that the UI layer won't know about it.

Also, watch it with the "myparent".. Do you really need a parent reference? are you storing it as a reference in your child objects or collection?

If you are, then make sure you mark the field that will hold it as [NotUndoable()] and [NonSerialized()].

Andrés

RangerGuy replied on Thursday, July 20, 2006

OK so I almost got it LOL!

This is the method that populates a readonly parent object. Except I can not figure out how to create an empty list to manually add the child items since they do not need to go to the dataportal

internal ParentInfo(SafeDataReader dr)

{

_parentid = dr.GetInt32("ParentID");

_parentname = dr.GetString("ParentIName");

while(dr.Read() && (_parentid == dr.GetInt32("ParentID")))

{

ChildInfo childinfo = new ChildInfo (dr.GetInt32("ChildID"),dr.GetString("ChildName"));

_childlist.Add(childinfo ); //childlist is a member of Parent info

}

}

RangerGuy replied on Thursday, July 20, 2006

Well this doesn't work either because I can't set the child list to ReadOnly = false from the parent object :(

Anybody have an Idea of how to do this. My readonly list of children doesn't need the dataportal or data access for these objects just needs to access a datareader passed from the parent to populate itself

ajj3085 replied on Thursday, July 20, 2006

Modify your code as below:


internal ParentInfo(SafeDataReader dr)

{

_parentid = dr.GetInt32("ParentID");

_parentname = dr.GetString("ParentIName");

_childlist = ChildListClass.GetList( dr );

}

}


In your ChildListClass, have this code:

internal static ChildListClass( SafeDataReader dr ) {

ChildListClass result;

result = new ChildListClass();

result.LoadList( dr );

return result;

}

private void LoadList( SafeDataReader dr ) {

IsReadOnly =false;

while(dr.Read() && (_parentid == dr.GetInt32("ParentID")))

{

_childlist.Add( ChildInfo.GetChild( dr ) ); //childlist is a member of Parent info

}

IsReadOnly = true;

}


Finally, in your ChildInfo class:

internal static ChildInfo GetChild( SafeDataReader dr ) {

ChildInfo result;

result = new ChildInfo();

result.LoadSelf( dr );

return result;

}

private void LoadSelf( SafeDataReader dr ) {

       myId = dr.GetInt32( "my_id" );

       myName = dr.GetString( "myName" );

}



The idea is that the parent doesn't know how to load the list; it just gets the data and hands it off.  The ChildList class knows it has children of type ChildInfo, but doesn't know anything at all about the details (like what properties it has).  The child knows how to load itself given a reader.  The list knows there can be many children; the parent simply knows how to get the data, it doesn't know what to do with it.

HTH

Andy

RockfordLhotka replied on Thursday, July 20, 2006

What you are trying to do is, unfortunately, relatively difficult. This is due to the way data comes from the database.

You want a query that returns two result sets - the first is the list of parent data and the second is the list of child data for all parents.

Since this data comes sequentially, you are forced to load all the parent objects in one pass, then go back through them a second time so they can load their individual child collections.

Certainly this can be done, but it does require going through the parent list twice - once to load, and once to load children. (pseudocode follows)

First load the parent list:
While dr.Read()
  Add(Parent.GetParent(dr))
Loop

Then load the children:
dr.NextResult()
dr.Read()
Loop While this.GetParentById(dr.GetInt32("Id")).LoadChildren(dr)

This loops through all the parents, loading the child data of each one from the datareader. GetParentById() should be obvious - it just finds the matching parent based on the id value. The interesting bits are in LoadChildren...

Parent.LoadChildren looks kind of like this:
internal bool LoadChildren(DataReader dr)
    return _children.GetChildren(_id, dr)

The Children.GetChildren method is mostly a standard child loading loop - with the exception that it needs to watch for the Id column to stop matching the parent's _id value and return at that point (remember that this means the datareader is then pointing to either EOD or the next parent's data, and the EOD (or not) needs to be returned up the call chain

Children.GetChildren looks like this:
internal static GetChildren(int parendId, datareader dr)
  bool moreData
  do
    Add(Child.GetChild(dr))
    moreData = dr.Read()
  while moreData and dr.GetInt32("Id")==_id
  return moreData


Note that I haven't tested this - it is just pseudo-code - so there could be unforseen issues. But the basic concept should be sound.

Brian Criswell replied on Thursday, July 20, 2006

Or you could try my SafeDataRowReader.  You retrieve a single DataSet in your root object or collection and then add DataRelations to link the DataTables together:
ds.Tables["Table1"].ChildRelations.Add(
    "Table1.Prop1_Table2.Prop1",
    ds.Tables["Table1"].Columns["Prop1"],
    ds.Tables["Table2"].Columns["Prop1"],
    false);

You then instantiate a new SafeDataRowReader with the first table.  Each level of child data requires a new SafeDataReader.
using(SafeDataRowReader dr = new SafeDataRowReader(ds.Tables[0]))
{
    // Root object pattern
    // load data using dr.Get...();
    // load child data
    using (SafeDataRowReader dr = dr.GetData(0))
    {
       // first child object / collection
       _child1 = Child1.GetChild1(dr);
       // next child object / collection
       dr.NextResult();
       _child2 = Child2.GetChild2(dr);
    }

    // Root collection pattern
    using (SafeDataRowReader dr = dr.GetData(0))
    {
       while (dr.Read())
       {
          this.Add(Child1.GetChild1(dr));
       }
    }
}

Children can then load grandchild data using dr.GetData(0).  In the Child.Fetch(dr), you load your child data and then use:
using(SafeDataRowReader childDr = (SafeDataRowReader)dr.GetData(0))
{
       // first child object / collection
       _child1 = Child1.GetChild1(dr);
       // next child object / collection
       dr.NextResult();
       _child2 = Child2.GetChild2(dr);
}

So you load then next level of children using dr.GetData(0) and move within a level (one child to the next) using dr.NextResult().  There, I think I remembered everything.

RangerGuy replied on Friday, July 21, 2006

Thanks everybody for the ideas :) This project is turning out to be more complex than expected LOL!

I'm trying them out right now :)

RangerGuy replied on Friday, July 21, 2006

Just wanted to thank everybody for the tips. We got it by doing the following    

bool HasMoreChildren = false;

using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))

{

IsReadOnly = false;

HasMoreChildren = dr.Read();

while (HasMoreChildren)

{

ParentInfo info = new ParentInfo(dr,ref HasMoreChildren);

this.Add(info);

}

 IsReadOnly = true;

 

}

Our Method in the child list object

internal ChildList(int currentChildID, SafeDataReader dr, ref bool HasMoreChildren)

{

 

IsReadOnly = false;

 do

{

ChildInfo childInfo =  new ChildInfo (dr.GetInt32("ChildID"), dr.GetString("ChildName"));

Add(childinfo);

HasMoreChildren = dr.Read();

while (HasMoreChildren && (currentChildID== dr.GetInt32("ChildID")));

IsReadOnly = true;

}

 

 

 

 

Copyright (c) Marimer LLC