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?
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.
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.
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
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.
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);
}
> 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
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