Data for Silverlight
Commit Changes to the Server
The Sample Application > Implement the Client Side > Commit Changes to the Server

We are almost done now. The only piece still missing is the code that submits the changes made by the user back to the server, so they can be applied to the database.

The first step is to modify the page constructor once again and connect the event handler for the "Commit Changes" button:

C#
Copy Code
// Load data, hook up event handlers
public Page()
{
  InitializeComponent();
  LoadData();

  _gridCategories.SelectionChanged += _gridCategories_SelectionChanged;
  _btnAdd.Click += _btnAdd_Click;
  _btnRemove.Click += _btnRemove_Click;
  _btnCommit.Click += _btnCommit_Click;
}

And here is the implementation for the event handler:

C#
Copy Code
// Commit changes to server
private void _btnCommit_Click(object sender, RoutedEventArgs e)
{
  SaveData();
}

And here is the implementation for the SaveData method, which does the real work:

C#
Copy Code
// Save data back into the database
void SaveData()
{
  if (_ds != null)
  {
    // get changes of each type
    byte[] dtAdded = GetChanges(DataRowState.Added);
    byte[] dtModified = GetChanges(DataRowState.Modified);
    byte[] dtDeleted = GetChanges(DataRowState.Deleted);
 
    // Invoke service
    var svc = GetDataService();
    svc.UpdateDataCompleted += svc_UpdateDataCompleted;
    svc.UpdateDataAsync(dtAdded, dtModified, dtDeleted);
  }
}
void svc_UpdateDataCompleted(object sender, UpdateDataCompletedEventArgs e)
{
  if (!string.IsNullOrEmpty(e.Result))
  {
    throw new Exception("Error updating data on the server: " + e.Result);
  }
  _tbStatus.Text = "Changes accepted by server.";
  _ds.AcceptChanges();
}

The method starts by calling the GetChanges method to build three byte arrays. Each one represents a DataSet with the rows that have been added, modified, or deleted since the data was downloaded from the server. Then the method invokes the Web service we implemented earlier and listens for the result. If any errors were detected while updating the server, we throw an exception (real applications would deal with the error in a more elegant way).

The only piece still missing is the GetChanges method. Here it is:

C#
Copy Code
byte[] GetChanges(DataRowState state)
{
    DataSet ds = _ds.GetChanges(state);
    if (ds != null)
    {
        MemoryStream ms = new MemoryStream();
        ds.WriteXml(ms);
        return ms.ToArray();
    }
    return null;
}

The method uses the DataSet.GetChanges method to obtain a new DataSet object containing only the rows that have the DataRowState specified by the caller. This is the same method available on the ADO.NET DataSet class.

The method then serializes the DataSet containing the changes into a MemoryStream, and returns the stream contents as a byte array.

Try running the application now. Make some changes, then click the "Commit Changes" button to send the changes to the server. If you stop the application and start it again, you should see that the changes were indeed persisted to the database.

Note: The update may fail depending on the changes you make. For example, if you delete one of the existing categories, all products that belong to that category will also be removed from the DataSet on the client. When you try to apply these changes to the server, the transaction will likely fail because the database may contain tables that still refer to the products you are trying to delete (the Orders table in this case). To test the delete/commit action, try creating a category, committing the changes, then deleting the new category and committing again. This will succeed because the new category won't have any products associated with it in the database.

A real application would have to deal with this type of problem more intelligently. For example, we could have loaded the Orders table as well, inspect the DataSet to detect whether the user is trying to delete an item that he should not, and issue a warning. That is left as an exercise for the reader.

See Also