views:

281

answers:

2

Howdy,

I have a middle tier containing several related objects and a data tier that is using a DataSet with several DataTables and relationships.

I want to call a Save method on one of my objects (a parent object) and have its private variable data transformed into a DataRow and added to a DataTable. Some of the private variable data are actually other objects (child object) that each need to have their own Save method called and their own variable data persisted.

How do I "lace" this together? What parts of a DataSet should be instantiated in the ParentObject and what needs to be passed to the ChildObjects so they can add themselves to the dataset?

Also, how do I wire the relationships together for 2 tables?

The examples I have seen for an Order OrderDetail relationship create the OrderRow and the OrderDetailRow then call OrderDetailRow.SetParentRow(OrderDetail)

I do not think this will work for me since my Order and OrderDetail (using their examples naming) are in separate classes and the examples have it all happening in a Big Honking Method.

Thank you, Keith

A: 

So, What I am doing right now is passing a reference to the DataSet and a reference to the DataRow of the parent into the Save method of the Child Object.

Here is a little code showing the concept of what I am doing.

// some random save event in a gui.cs//
public void HandleSaveButtonClick()
{ parentObject.Save(); }


// Save inside the parentObject.cs //
public void Save()
{    
    CustomDataSet dataSet = new CustomDataSet();

    ParentObjectTableAdapter parentTableAdapter = new ParentObjectTableAdapter();

    DataTable dt = dataSet.ParentTable;
    DataRow newParentDataRow = dt.NewRow();
    newParentDataRow["Property1"] = "Hello";
    newParentDataRow["Property2"] = "World";
    dt.Rows.Add(newParentDataRow);

    parentTableAdapter.Update(dataSet.ParentTable);

    //save children
    _child1.Save(dataSet, newParentDataRow)


    dataSet.AcceptChanges();
}

//Save inside child1.cs //
public void Save(CustomDataSet dataSet, DataRow parentRow)
{

    Child1TableAdapter childTableAdapter= new Child1TableAdapter();

    DataTable dt = dataSet.ChildTable;

    DataRow dr = dt.NewRow();
    dr.SetParentRow(parentRow);
    dr["CProp1"] = "Child Property 1";    
    dt.Rows.Add(dr);

    childTableAdapter.Update(dataSet.ChildTable);

}

Let me know how this looks. Is this a usable pattern or am I missing something critical?

Thank you,
Keith

Keith Sirmons
+2  A: 

I will not start another debate whether datasets are good or evil. If you continue to use them, here are something to consider:

  • You need to keep the original dataset and update that, in order to get correct inserts and updates.
  • You want your parents to know their children, but not the other way. Banish the ParentTable.
  • An Order and its OrderDetails is an aggregate (from Domain Driven Design) and should be considered as a whole. A call to order.Save() should save everything.

Well, that's the theory. How can we do that? One way is to create the following artifacts:

  • Order
  • OrderDetail
  • OrderRepository
  • OrderMap

The OrderMap is where you manage the Order to Dataset relationships. Internally, it could use a Hashtable or a Dictionary.

The OrderRepository is where you get your Orders from. The repository will get the dataset with all relations from somewhere, build the Order with all its OrderDetails, and store the Order/Dataset relationship in the OrderMap.

The OrderMap must be kept alive as long as the Order is alive. The Order contains all OrderDetails.

Pass the order to the repository and let it save it. The repository will get the dataset from the map, update the Order-table from the order and iterate all order-details to update the OrderDetail-table.

Retrieve and save:

var order = repository.GetOrder(id);
repository.Save(order);

Inside OrderRepository.GetOrder():

var ds = db.GetOrderAndDetailsBy(id);
var order = new Order();
UpdateOrder(ds, order);
UpdateOrderDetails(ds, order); // creates and updates OrderDetail, add it to order.
map.Register(ds, order);

Inside OrderRepository.Save():

var ds = map.GetDataSetFor(order);
UpdateFromOrder(ds, order);
foreach(var detail in order.Details) UpdateFromDetail(ds.OrderDetail, detail);

Some final notes:

  • You can implement the map as a singelton.
  • Let the map use weak references. Then any order should be garbage-collected when it should, and memory will be freed.
  • You need some way to associate an OrderDetail with its table-row
  • If you have the slightest possibility to upgrade to .NET 3.5, do it. Linq to Sql or Linq to Entity will remove some of your pain.
  • All of this is created out of thin air. I hope it's not too inaccurate.
Thomas Eyde