In the first part of this series, I talked about some general concepts, and went into reading, updating, adding, deleting data using RIA Services. If you haven't read it yet, you might want to read up on that, and maybe read up on the introductory post as well.
In this part, I'll talk about an interesting option you have when updating your data: the change set.
As some people asked for it, I've uploaded the sourcecode of my demo project. This code will be updated with every new part in the series. To use the demo code, you'll need to change the connection string for the Entity Model to your own local copy of the AdventureWorks SL database - once that's done, you're ready to go! :-)
As mentioned in the previous part, persisting your changes to the underlying datastore boils down to calling SubmitChanges() on your (clientside) DomainContext. This will automatically make sure the correct server side methods (in the demo: InsertCustomer, UpdateCustomer or DeleteCustomer) are called for each of your entities.
Then what is a change set? A change set should be seen as a complete collection of all the changes you've made on your context, before having them submitted and thus persisted to your datastore. This principle makes sure server-side methods don't have to be called every time you make a change to your data: the changes will be tracked client-side, and will be submitted once you explicitly call SubmitChanges() to persist your change set.
To visualize this, I've added a few things to the demo application. To the right side of the datagrid, there's a grid which will track the changes. This grid contains some textblocks, bound to the number of modified, added, removed entities, and it contains a reject & submit button:
Now, to track the changes, I need to get the changeset from my DomainContext. I will do this in my datagrid's RowEditEnded-event (which gets fired right after I've edited a row), and this change set will be the datacontext of the grid used to track the changes:
As you can see, context.Entities.GetChanges() returns an EntityChangeSet. This object contains a list of changes, divided into different lists for AddedEntities, ModifiedEntities, ... (I've also added a way to delete items from the datagrid, have a look at the source code for that.)
So, now we've got a list of changes, and thanks to some databinding, these changes are automatically reflected in our UI. Now, we need to submit or reject this change set.
Rejecting changes is easy: just execute RejectChanges() on your context, and every change you've made that hasn't been submitted yet will be undone. To submit your changes, call the SubmitChanges()-method on your context:
There is, of course, more to it than just that. When you submit your changes, a few things will happen. This is what will be done on your DomainService after you've called that command (copy/paste from the RIA Services documentation):
1. Submit – the service entry point that receives the changeset and begins the changeset processing pipeline
2. AuthorizeChangeSet – Verifies Permission/Authorization for each operation. If any operations are not permitted, processing stops and an error is returned to the client
3. ValidateChangeSet – Validation for all operations in the changeset is run. If any operations fail validation, processing stops and errors are returned to the client
4. ExecuteChangeSet – The corresponding method for each operation in the changeset is invoked. This is the point in time when your business logic is run. If any operations failed, processing stops and errors are returned to the client
5. PersistChangeSet – At this point all the domain operations have been executed, and this method is called to finalize the changes.
6. ResolveChangeSet – If there were any optimistic concurrency errors, this method is called to resolve them.
All these methods are virtual, so they can be overridden as needed! As you can see, a few things can go wrong (eg, something isn't valid, the necessary permissions aren't available, ...), so it's important to include error handling. To do this, add an event handler to your context's Submitted-event:
By the way, this is not enough for correct, complete error handling. For example, in this demo it's quite easy to change the ID of a customer to something that's not valid anymore - best way to solve this is, of course, make sure it's not editable (an internal ID is NOT something you'd want to be editable), or not visible. But this does show that not all errors are caught: it isn't exactly a Conflict (used for concurrency) and it isn't exactly a a ValidationError (no validation exists on that field) either. So while this specific example isn't something that would occur in real-life situations, it does show that it's important to put validation on your data where needed AND to include some more general error handling in your code.
But as far as change sets are concerned: you're done! This is what change sets can mean for you - using them carefully will make sure you don't have unnecessary client-server-communication, and thanks to some databinding, you can easily track the changes in your UI.
Stay tuned for the next update, in a few days! :-)