Parent Child Grandchild Issue.

Parent Child Grandchild Issue.

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


gwilliams posted on Thursday, April 30, 2009

I have a parent object that has a collection of children. At times some of these children will be an existing item and some may be new. What would be the best way to build this?

 

Example.

I have a trip that has trip specific properties with a collection of stops. Each stop has a child object of destination properties and the destination with an address child.

 

A trip can be either new or existing

A stop can be either new or existing

A stop can have either a new or existing or both destination and child address.

 

So a stop is the parent of a destination and a destination is a parent of address.

 

I need to be able to crate a new trip with new stops and those stops may have an existing destination/address or may have a new destination/address OR a combination of both.

 

Example:

StopListing has 2 stops

1 new stop with new destination/address

2. new stop with existing destination/address (pulled form db)

 

If the trip object (which is the overall parent) was created as new then it will try and do an insert for all child objects; blowing up on the second stop record when it tries to insert a duplicate destination/address record.

 

I would like the stop record to have the destination / address object within it.

 

Any ideas?

RockfordLhotka replied on Thursday, April 30, 2009

gwilliams:

If the trip object (which is the overall parent) was created as new then it will try and do an insert for all child objects; blowing up on the second stop record when it tries to insert a duplicate destination/address record.

That should not be true.

Every individual object is responsible for its own IsNew property, and the data portal (in 3.6 with the child data portal) will route the ChildUpdate() call to Child_Insert(), Child_Update() or Child_DeleteSelf() as appropriate based on the IsNew and IsDeleted property values of the child.

In other words, it is totally acceptable to add an existing child to a new root - assuming you have some scheme by which you are retrieving the existing child.

And you can do that by having the child class include a factory method that invokes the data portal - temporarily treating this as a root object, but only for the Fetch operation. The only trick to doing this, is that your DataPortal_Fetch() method must make sure to call MarkAsChild() because that won't happen automatically.

gwilliams replied on Friday, May 01, 2009

I had tried, however when saving the object it still tries to run the child as an insert not update or nothing if the child has not changed.

Object hierachie

 

Trip

-- StopListing

-------Stop

            Facility

              -- Address

 

From my parent object (Stop)

 

private static PropertyInfo<Facility> FacilityProperty = RegisterProperty<Facility>(new PropertyInfo<Facility>("Facility"));

    public Facility Facility

    {

      get

      {

        if (!(FieldManager.FieldExists(FacilityProperty)))

          LoadProperty(FacilityProperty, Facility.NewFacility());

        return GetProperty(FacilityProperty);

      }

    }

 

How I load the existing data.

Facility fc = _stop.Facility.LoadExistingFacility(new Guid("98446a25-8546-4911-a9d9-263cf2545820"));

 

 

Facility load:

 

public Facility LoadExistingFacility(Guid ID)

    {

      return DataPortal.Fetch<Facility>(ID);

    }

 

private void DataPortal_Fetch(Guid ID)

    {

      MarkAsChild();

      using (FacilityDAL dal = new FacilityDAL())

      {

        using (SafeDataReader data = dal.GetFacilityInfo(ID))

        {

          data.Read();

          LoadProperty<Guid>(IdProperty, data.GetGuid("Facility_ID"));

          LoadProperty<Guid>(ParentfacilityIdProperty, data.GetGuid("ParentFacility_ID"));

       --load other properties—

        }

       }

      }

 

At this point fc. and its child address is pop populated.

Debugging is showing that the IsNew = false.

 

Trip.Save() is then called and Trip data is inserted, Stop data is inserted, and the Facility data is hitting the insert not the update Or nothing if the object is not dirty.

xAvailx replied on Friday, May 01, 2009

Check if after your fetch your child items that are trying to be inserted are marked as new.

They shouldn't be after getting fetched, that is probably why are they going in the insert when saving.

RockfordLhotka replied on Friday, May 01, 2009

The thing is, you want to load a pre-existing child into a new root. That is not a directly supported scenario – which is to say that you’ll have to do just a little extra work yourself because CSLA won’t do it for you.

 

public class MyChild
{

  internal static MyChild NewChild()

  {

    return DataPortal.CreateChild<MyChild>();

  }

 

  internal static MyChild GetChild(int id)

  {

    return DataPortal.Fetch<MyChild>(new SingleCriteria<MyChild, int>(id));

  }

 

  private void Child_Create()

  {

    // set defaults or whatever

  }

 

  private void DataPortal_Fetch(SingleCriteria(MyChild, int> criteria)

  {

    MarkAsChild();

    // get data and load object’s fields

  }
}

 

Notice how this is a “hybrid” of sorts, in that there’s the normal child create (and insert/update/delete that I didn’t show). And there’s a root fetch implementation, with the one twist that MarkAsChild() is called explicitly because otherwise the data portal will continue to treat this like a root object, and you don’t want that.

 

Rocky

 

 

gwilliams replied on Friday, May 01, 2009

After reading chapter 9 again I found the error in my implementation. I was expecting the parent object to be smart enough to know what I wanted regarding the child (new or existing)

 

A slight modification to the parent and the implementation of the Dataportal_Fetch in the child yielded the proper results.

 

Parent:

public Facility Facility

    {

      get

      {

        if (!(FieldManager.FieldExists(FacilityProperty)))

          if (this.IsNewFacility)

          {

            LoadProperty(FacilityProperty, Facility.NewFacility());

          }

          else

          {

            LoadProperty(FacilityProperty, Facility.LoadExistingFacility(this.FacilityId));

          }

        return GetProperty(FacilityProperty);

      }

    }

 

I will always know before adding a new stop if i have an existing facility/address child. all i need to do is set the IsNewFacility flag and make sure i have the stop.facilityID property set.

 

the facility is selected from an external list. if a facility does not exist then uses can create a new one.

 

This is the first real implemenation of CSLA that i have done. Trying to show the powers to be that this is the best framework to use for this project. This app will have a windows, web, system to system (telephony), and possibly mobile interfaces.

trying to make sure i don't hack my way threw the creation of the objects.

 

thanks for all the help.

gwilliams replied on Wednesday, May 06, 2009

One last question on this matter… would it be bad design practices to have one object that can be either a child or a root?

 

So my factory methods would be as follows:

 

public static Facility NewFacilityBase()

{

return DataPortal.Create<Facility>();

}

 

internal static Facility NewFacility()

{

return DataPortal.CreateChild<Facility>();

}

 

 

 

public static Facility GetFacilityBase(Guid id)

{

return DataPortal.Fetch<Facility>(new SingleCriteria<FacilityBase, Guid>(id));

}

 

 

internal static Facility GetFacility(SafeDataReader dr)

{

return DataPortal.FetchChild<Facility>(dr);

}

RockfordLhotka replied on Wednesday, May 06, 2009

> would it be bad design practices to have one object that can be either a child or a root?

 

This is called a Switchable object and is a supported stereotype.

 

However, usually a switchable object is an indication of a flawed object model, so be careful.

 

Switchable objects are only acceptable if their responsibility is exactly the same regardless of whether they are acting as a root or child. In other words, as long as you have no code like

 

if (IsChild)

  // do child stuff

else

  //do root stuff

 

Then you are in pretty good shape. Obviously the data access code will be somewhat different – but that’s more in the DAL.

 

The key is to ensure that your business logic is not different – then switchable is fine.

 

Rocky

 

gwilliams replied on Thursday, May 07, 2009

Thanks, this object does meet the "Switchable" object definition. With this being the first production implementation use the CSLA frame work i am possibly being over cautious with the implementation. I don't want to bastardize the implementation.

Copyright (c) Marimer LLC