Ok, we’ve already seen one option for fetching data from a remote web service. In this post, we’ll see how to commit changes tracked by the DataContext component. Committing buffered changes means that you’ll need to set (at least) two DataContext instance properties: serviceUri and saveOperation. serviceUri identifies the URI of the web service that is responsible for persisting the changes; saveOperation is used for identifying the server side method which persists the changes that were buffered by the DataContext instance.
To illustrate committing, we’ll be updating the PeopleService we’ve introduced in a previous post. For now, we’ll start by writing some JavaScript code:
var items = [
{ name: "luis", address: "fx" },
{ name: "john", address: "fx" },
{ name: "peter", address: "fx" }
];
Sys.require([Sys.components.dataContext],
function () {
var ctx = Sys.create.dataContext({
serviceUri: "PeopleService.svc",
saveOperation: "SavePeople",saveHttpVerb: "POST"
});
ctx.trackData(items);
var aux = { name: "charles",address: "lx" };
ctx.insertEntity(aux);
ctx.saveChanges();
});
As you can see, we’re configuring three properties for our DataContext object:
- serviceUri: we’re using this to pass the URI to the web service that is going to be used for saving the changes;
- saveOperation: identifies the name of the method exposed by the web service that should be used for saving the changes;
- saveHttpVerb: we’re setting this property to specify the HTTP verb that should be used when the DataContext tries to commit the changes previously buffered.
Btw, notice that we’re using the trackData method for initiating data tracking. I’ve opted for doing this to show you that saving does not depend on fetching data from the same web service. You should also keep in mind that the saveChanges method might receive three parameters:
- the first, identifies the success callback function;
- you can also pass an error callback function (second parameter);
- finally, you can pass additional custom context that will be passed for the callback methods you’ve passed as first and second parameters.
Before showing the server side code, we need to know what is passed to the server side when someone calls the saveChanges method. The answer shouldn’t be too hard (at least, after thinking a little bit about it): we’ve already seen that the DataContext component saves all changes by adding entries to the internal _changelist field. And that is the information which gets send back to the server when the component tries to persist the buffered changes.
With this information, we can start updating the server side code. The first thing we need to do is create a new C# class which represents an instance of a change. In other words, we need to mimic the Sys.Data.ChangeOperation in the server side. Now, if I was building a reusable class, I’d use templates and I’d duplicate all the properties exposed by the ChangeOperation type.
However, since this is demo code and I know that the buffered operations will only involve insertions, editions and removals of Person objects (after all, we still haven’t talked about links), I’ve opted for only replicating the properties that are used in this scenario:
[DataContract]
public class PersonOperation
{
[DataMember(Name="item")]
public Person Person{ get; set; }
[DataMember(Name="action")]
public OperationType OperationType { get; set; }
}
public enum OperationType
{
Insert,
Update,
Remove
}
As you can see, we’ve added a new enum too. That was necessary because the client side ChangeOperation instance relies in using an enum for specifying the operation that is being performed (the only thing we need to ensure is that the enum entries have the same values as the ones specified in the client side).
The PersonOperation class mimics the Sys.Data.ChangeOperation “type” we have in the client side. As you can see, we’re using the DataMemberAttribute for mapping each of the server properties to the ones used in the client side. Oh, and just for completion, here’s the code for the Person class:
[DataContract]
public class Person {
[DataMember(Name="name")]
public String Name { get; set; }
[DataMember(Name="address")]
public String Address { get; set; }
}
Now, we’re still missing the web service’s save method. We’ve already established that it receives a collection of changes (ie, an array of Sys.Data.ChangeOperation objects). However, there’s still one important piece of information missing: we need to know the name of the parameter used for sending that information. In this case, the DataContext object uses the changeSet name. And now we’re read for taking a peek at the web service code:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(
RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class PeopleService {
static List<Person> _people = new List<Person>
{
new Person{ Name = "luis", Address = "fx"},
new Person{ Name = "john", Address = "lx"},
new Person{ Name = "peter", Address = "pt"}
};
private readonly
IDictionary<OperationType, Action<Person>> _executors;
public PeopleService()
{
_executors =
new Dictionary<OperationType, Action<Person>>
{
{OperationType.Insert, InsertItem},
{OperationType.Update, UpdateItem},
{OperationType.Remove, DeleteItem}
};
}
[OperationContract]
[WebGet]
public IEnumerable<Person> GetPeople(SortOrder sort){
var ordered = _people.OrderBy(
p => (sort == SortOrder.Name ? p.Name : p.Address) );
return ordered;
}
[OperationContract]
public void SavePeople(
IEnumerable<PersonOperation> changeSet)
{
foreach (var change in changeSet) {
_executors[change.OperationType](change.Person);
}
}
private void InsertItem(Person person)
{
//do something
}
private void UpdateItem(Person person)
{
//update person
}
private void DeleteItem(Person person)
{
//delete item
}
}
We rely on the OperationType property of each PersonOperation instance to decide if we’re inserting, updating or removing an item (I’ve opted for using a dictionary for eliminating the switch that you’ll see in most cases). It’s just that simple, really! The only thing to watch out for are the mappings: you need to ensure proper mapping between the client and side objects (and this includes method parameter names too!).
This has become a rather large post, so I guess this is it for now. There are still a couple of interesting observations that can be made about the server side save method, but we’ll leave it for a future post. Stay tuned for more!