1. Problem
You need to have read-write access to data published via OData.
2. Solution
Take advantage of WCF Data
Services enhancements such as DataServiceCollection available in
Silverlight 4 to implement CRUD operations.
3. How It Works
WCF Data Services support entity tracking on the client so that you can submit a query to the service in order to populate a DataServiceCollection that is databound to a UI control such as a DataGrid. You used this technique in Recipe 1 to load read-only data. In this recipe, you do the same but there's a twist: now you can insert, update, and delete edits.
The code is covered in detail in
the next subsection but here are the key points on how the code works.
In this recipe, you edit order details for the Northwind database, first
loading all of the Customers, the related Orders, and finally the
related Order Detail line items. The code allows the user to do the
following:
Delete the selected Order Details line item
Update the Quantity for an existing Order Details line item
Add an Order Details line item to an existing Order
You created a Service Reference to the Northwind Data Service in Recipe 2. The client-side proxy provides the NorthwindEntities type that inherits from System.Data.Services.Client.DataServiceContext. You create an instance of that class called NorhwindContext, running all queries and operations through the DataServiceContext instance. This is required in order to keep things synchronized across entities as you make edits.
Once you have the data context
established, you go through the process of querying data and enabling
edits. The code is very simple once you see the pattern. Essentially,
you identify the object to be deleted, updated, or created and call
methods on NorthwindContext to make it happen. The operation occurs locally in the DataServiceCollection instance of interest (i.e. the Order_Details entity) and then the changes are applied on the server via NorthwindContext.
4. The Code
Take advantage of the Northwind Data Service you created in Recipe 2 and add a service reference to make it available in Recipe 9-3's Silverlight application. In UserControl_Loaded you create a DataServiceContext instance named NorthwindContext as before. You add one directive to have NorthwindContext apply changes as a batch by updating SaveChangesDefaultOptions since the insert operation affects multiple tables, as shown here:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
NorthwindContext = new NorthwindEntities(
new Uri("DataService/NorthwindDataService.svc", UriKind.Relative));
NorthwindContext.SaveChangesDefaultOptions = SaveChangesOptions.Batch;
}
Next, you have a series of similar operations as in Recipe 1 and 2 where you load up three DataGrid
instances with Customer data, Customer Order data, and finally the
corresponding Customer Order Details data where you implement simple
CRUD operations. (We don't go through the data loading code so as to not
be redundant.) Essentially, when the user clicks the Load Northwind
Customer Data button, it populates the Customers DataServiceCollection<Customer> instance that is databound to the top left DataGrid. The Load Customer Orders button loads the Orders that correspond to the selected Customer into the DataGrid on the upper right. The same process is implemented to load up the corresponding Order_Details in the DataGrid on the lower right. At this point, you have the data that you want to perform edits on in the Order_Details data. Next, add a UI for making edits with buttons that correspond to deleting, updating, and creating an Order_Details entity, as shown in figure 9-7.
To load the data, simply
clicked the Load Northwind Customer Data button, followed-by the Load
Customer Orders button, and finally the Load Customer Order Details
button. You can select a particular record before clicking the next step
if desired to test it out but you must click the buttons in that order
for the simple UI to function.
The Delete Selected Line Item button does what it suggests: it deletes the selected Order_Details record in the Customer Order Details DataGrid. Here is the event handler for the button:
private void DeleteDetailsItemBtn_Click(object sender, RoutedEventArgs e)
{
if (CustomerOrderDetailsDataGrid.SelectedItem != null)
{
NorthwindContext.DeleteObject(
(Order_Detail)CustomerOrderDetailsDataGrid.SelectedItem);
NorthwindContext.BeginSaveChanges(
SaveChangesOptions.Batch, ChangesSaved, NorthwindContext);
}
}
Since you have the Order_Details object to be deleted already on the client as part of the CustomerOrderDetails DataServiceCollection<Order_Detail> collection, you can simply pass in the DataGrid SelectedItem to DeleteObject and all of the change tracking magic on the client takes care of the rest. However, to push the changes to the server, call BeginSaveChanges on the NorthwindContext object which knows where the WCF Data Service lives and how to apply the changes.
The code to perform the update operation is a little bit more complicated but not much more. Here is the code:
private void UpdateDetailsItemBtn_Click(object sender, RoutedEventArgs e)
{
((Order_Detail)CustomerOrderDetailsDataGrid.SelectedItem).Quantity =
Convert.ToInt16(EditQuantity.Text);
NorthwindContext.UpdateObject(
(Order_Detail)CustomerOrderDetailsDataGrid.SelectedItem);
NorthwindContext.BeginSaveChanges(
SaveChangesOptions.Batch, ChangesSaved, NorthwindContext);
}
The code is similar except you call the NorthwindContext.UpdateObject method and pass in the modified object to complete the changes on the client and to mark the edited Order_Details entity as modified. You edit the selected Order_Details entity by updating the Quantity value to match what is entered in the EditQuantity TextBox.
The code to insert a new Order_Details entity as a child to the selected Customer Order is shown here:
private void InsertlineItemBtn_Click(object sender, RoutedEventArgs e)
{
Order_Detail od = new Order_Detail();
Order SelectedOrder = (Order)CustomerOrdersDataGrid.SelectedItem ;
Product SelectedProduct = (Product)ProductsDataGrid.SelectedItem ;
od.Order = SelectedOrder;
od.OrderID = SelectedOrder.OrderID;
od.Product = SelectedProduct;
od.ProductID = SelectedProduct.ProductID;
od.Quantity = Convert.ToInt16(InsertProdQuantity.Text);
od.UnitPrice = (Decimal)SelectedProduct.UnitPrice;
NorthwindContext.AddToOrder_Details(od);
NorthwindContext.BeginSaveChanges(ChangesSaved, NorthwindContext);
}
You have a small DataGrid on the left that lists all of the possible Products that you can add as a new line item to the selected Order. Otherwise, the code to create the new Order_Details entity is very straightforward. When the Service Reference client proxy is created, it automatically generates a method called AddToOrder_Details on the NorthwindContext variable where you pass in the new Order_Details entity to add the line item to the Order. As before, client-side changes are persisted to the service via the BeginSaveChanges method, which is very different when compared to the ADO.NET Entity Framework
The last item to mention is the ChangesSaved method that is called after changes are persisted by NorthwindContext shown here:
private void ChangesSaved(Object state)
{
EditsMessage.Text = "Selected Line Item Modified + " + DateTime.Now.ToString();
}
You simply update a string with a time stamp, but instead of passing in the NorthwindContext
variable as the third parameter in the call below that is required to
persist CRUD operations to the server, you could pass in a custom
structure that has properties to identify more detail such as the type
of operation, the name of the entity modified, etc., in order to provide
a more robust status:
NorthwindContext.BeginSaveChanges(ChangesSaved, NorthwindContext);