Child objects not saving?

Child objects not saving?

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


MikeB posted on Sunday, May 17, 2009

I have an object with a child list. I call Save() on the root level object and use the FieldManager to call update on the child objects. It all seems to be working but in the end, the changes are not persisted.

I have stepped through the code to ensure that my child objects have the most recent values and they do.... I use Transactional attribute with TransactionScope as they type. Do I have to call Commit or is this done by the CSLA framework?

 

Any clues?

Mike B

Here is the Child_Update code:

[RunLocal()]

[Transactional(TransactionalTypes.TransactionScope)]

private void Child_Update(object parent)

{

   using(ObjectContextManager<InformEntities> manager

      = ObjectContextManager<InformEntities>.GetManager("InformEntities", true))

   {

      InformModel.TimesheetActivity activity = new InformModel.TimesheetActivity();

      ObjectToEntity(activity);

      activity.EntityKey = new System.Data.EntityKey("InformEntities.TimesheetActivities", "Id", activity.Id)

      manager.Context.Attach(activity);

      manager.SaveChanges();

   }

}

 

RockfordLhotka replied on Sunday, May 17, 2009

You should read Chapter 18 (and 17) of Expert 2008 Business Objects to get an understanding of the data access models supported by CSLA .NET.

At a high level:

  1. The RunLocal attribute is only valid on root object methods - it is ignored on Child_XYZ methods
  2. The Transactional attribute is only valid on root object methods - it is ignored on Child_XYZ methods
  3. Your root object's DataPortal_XYZ method must trigger saving the child by calling either the data portal for each child object, or the field manager's helper method to update all child objects

 

MikeB replied on Sunday, May 17, 2009

Thanks for the reply Rocky:

So, would 1 and / or 2 stopped my object from saving. You stating that the RunLocal and Tansactional attributes are ignored at the child level tells me no. This leaves the DataPortal_XYZ method. I do call UpdateChildren(this) from the parent method, and my Child_Update(...) does get called....I get no exceptions, no errors, but also, no persistence.

I have read chapter 18 of your book and I understand the data access methods which should be used. Well I thought I did anyway. The RunLocal and Transactional attributes were remnants left behind before I implemented Child_Update (Originally I had everything as a root level object). The classes were all genereated from a template using MyGeneration...

Any idea why this will not save? Here is my parent DataPortal_Update(...) function. I am using Entity Framework for data access.... Anything apparently wrong with this? Remember, the Child_Update() is getting called..... 

 

[RunLocal()]

[Transactional(TransactionalTypes.TransactionScope)]

protected override void DataPortal_Update()

{

using (ObjectContextManager<InformEntities> manager

= ObjectContextManager<InformEntities>.GetManager("InformEntities", true))

{

InformModel.Department newObj = new InformModel.Department();

ObjectToEntity(newObj);

newObj.EntityKey = new System.Data.EntityKey("InformEntities.Departments", "DepartmentId", newObj.DepartmentId);

manager.ObjectContext.Attach(newObj);

manager.ObjectContext.SaveChanges();

FieldManager.UpdateChildren(this);

}

}

 

ajj3085 replied on Monday, May 18, 2009

I don't know EF, but maybe the SaveChanges (if it's equivolent to SubmitChange on the l2s DataContext) should be after the FieldManger call.

MikeB replied on Monday, May 18, 2009

Yeah, I think your right. Unfortunately, I became really overwhelmed with my generated classes trying to change root objects to child objects etc... so I deleted them all and started handcoding them from scratch to get a better feel for the framework and how it works.... 

My only problem now is I cannot figure out the indtended way to create child objects..... Even with the book, I find it difficult to follow ProjectTracker project.

If I have a Order object with an OrderItem collection. Am I supposed to create a AddNewOrderItem(....) method in the Order object where I pass all the arguments? Or do I just ask the Order for a new Blank Order Item, fill out the details and pass back to the Order for saving?

Thanks for the reply....

Mike B 

RockfordLhotka replied on Monday, May 18, 2009

I tend to create my classes following a consistent structure, and this was a reason I created the child data portal concept – to increase the consistency.

 

So I tend to create a factory on every class (public for a root, internal for a child) and have that factory call the data portal.

 

Then I implement the real work in the DataPortal_XYZ or Child_XYZ method (or in a factory object).

 

Typically a child object is contained in a collection, and collections have Add() and AddNew() methods. When possible, I’ll support AddNew() by overriding AddNewCore():

 

protected override object AddNewCore()

{

  var item = ChildType.NewChild();

  Add(item);

  return item;

}

 

If that’s not realistic because some values are necessary to create a new child, then I won’t take that approach, and instead will do one of two things

 

1.       Have a public static factory method on the child class so anyone can create a child (and then use Add() to add it to the collection)

2.       Have an overload for Add() on the collection that takes the required parameters, calls the internal factory method on the child class to create the child, and then calls Add() to add the item to the collection

 

At this point though, we’ve hit a point where style and choice enters the picture. It doesn’t really matter to CSLA how you choose to do this (there are variants beyond these two) as long as you ultimately have a child object and Add() it to the collection.

 

Rocky

 

MikeB replied on Monday, May 18, 2009

Thanks for the reply Rocky.... Makes perfect sence, I figured it was really up to the "implementer" but I thought maybe there were prefered methods. 

Mike B

Copyright (c) Marimer LLC