In the last article, we discussed how to perform data modifications against a data service using the WebDataContext class. While that represents a great solution for most scenarios, the need for an AJAX story still exists. You may recall from part 6 that the WebDataContext class has a counterpart in the ASP.NET AJAX library, namely Sys.Data.DataService. Reason would lend us to believe that the same functionality exposed by the WebDataContext class, in terms of data modification, would be available and similarly exposed on the DataService class, and reason would be right.
In order to use the ADO.NET Data Services client library, you have to perform some minor setup. Please refer to part 6, if you haven’t done so already, and ensure that you can query your data service using the JavaScript DataService object. The following article is meant to be an extension of the previous two articles, and therefore won’t restate anything previously explained.
Before moving on, we need to mention a few differences between WebDataContext and DataService. The biggest difference between the two is that DataService does not act as a unit of work. What does that mean? It means that DataService doesn’t “cache” a series of data modification operation, and submit them all as a single unit. When you work with the WebDataContext class, and you add/update/delete entity objects, none of that takes affect until you call SaveChanges, at which point all changes made are submitted at once. When working with the JavaScript DataService object, all data modification requests are made immediately. In most cases, this behavior is what you would want anyways, but it’s important to expect this behavior, otherwise one might be confused when transitioning from using WebDataContext to DataService.
The second difference between the two environments is that when working with the DataService object in JavaScript, you don’t have a generated (or hand-written) client-side model. This means that we don’t have a Carrier, CellPhone, or Manufacturer class. This isn’t an issue though since DataService works purely with JSON objects. This means that in order to create instances of entity objects, all you have to know is their required properties. For instance, the Carrier model class only has two properties: Id, and Name. Id is a service generated identity property, so in order to create a new carrier, I’d only need to be concerned with the Name property.
var carrier = { Name: "Lost In Tangent Mobile" };
That’s all that it takes to create a new carrier instance. The same logic would be applied to create more complex entity instances. Without further ado, let’s jump right into how to perform each individual data modification operation.
Note: Throughout the rest of the article, the variable “service” simply represents a Sys.Data.DataService object that points at the data service.
Inserting
Using our DataService instance, how can we insert the carrier instance created above?
service.insert(carrier, "Carriers", onSuccess, onFailure, context, true);
The insert method takes six parameters: the object you want to insert, the name of the entity set to insert into, success callback, failure callback, arbitrary context data, and a boolean that determines whether you want the DataService to pass a refreshed entity object back to the success callback. Only the first two parameters are required. This will immediately submit the insert request to the data service, and then call the respective callback upon success of failure. Pretty easy stuff.
The success and failure callback methods have the exact same signature as the success/failure callback methods used with the DataService’s query method as seen in part 6. The operation parameter will always equal “insert”.
Now you may be wondering what happened to the Id property. We didn’t specify it when we created the carrier JSON object above, but we know that the data service was going to generate one for us. In the previous article we saw that the WebDataContext class would actually refresh any properties on entity instances it modified when it submitted changes (as long as its MergeOption was set correctly). That way any generated identity values would be present after the call to SaveChanges. DataService offers the same behavior. After you call its insert method, it will not only refresh, but create any properties that were omitted but affected as a result of the insertion (i.e. the Id property of our carrier instance). This means that if I modified our success callback like so:
function onSuccess(result, context, operation) { alert(result.Id); }
I would get an alert message stating 7, or whatever the generated Id would be. As long as we create a valid JSON object, with the proper property names, the DataService will take care of the rest and fill in the gaps
Now, this behavior is dependent upon the sixth parameter of the insert method being set to true, or being omitted (because the default value is true). If I modified the call to insert like the follow:
service.insert(carrier, "Carriers", onSuccess, onFailure, null, false);
The above onSuccess method would result in an error, because we told the DataService not to pass the entity object to the success callback. This provides the equivalent of the MergeOption property of the WebDataContext class.
One more point of interest is that the insert method returns the instance of Sys.Net.WebRequest that is used internally by the DataService to make the actual service request. For the most part, you probably won’t need to use the object manually, but it’s important to know it’s there, in case you do. Also note that the insert method is making an HTTP POST request to the data service, not an HTTP GET request, like the query method uses.
Updating
Once you have an entity instance, whether you’ve queried for it, or reconstructed it, you can modify its property values. If you want to persist those changes back to the data service, all you have to do is call the DataService’s update method.
service.update(carrier, "Carriers(2)", onSuccess, onFailure, context, true);
The update method takes the exact same six parameters as the insert method. There are five differences though:
- The second parameter isn’t the name of the entity set, but rather the URI of the exact resource you’re updating. This is why I’m specifying “Carriers(2)”, because that is the canonical URI of the carrier I’m updating.
- The resource URI isn’t required, which means you could call the update method, passing only the entity object you wish to update.
- The sixth parameter is false by default. This means that the DataService won’t pass a refreshed entity instance to your success callback, unless you specifically tell it to.
- The update method is creating an HTTP PUT request.
- The operation parameter passed to the success/failure callbacks will always equal “update”.
Deleting
To insert data, you call the DataService’s insert method. To update data, you call the DataService’s update method. That must mean if I want to delete data I can call the DataService’s delete method! There’s only one problem with that: delete is a reserved keyword in JavaScript, and therefore can’t be used as a method name. Does this mean that the ADO.NET team decided to omit the ability to delete data when working with a data service using the DataService class in JavaScript? Well that would have been ridiculous. Fortunately for us they weren’t too partial to naming the delete method “delete”, and just went ahead and called it “remove”.
service.remove(carrier, "Carriers(2)", onSuccess, onFailure, context);
The remove method takes five parameters, all of which are optional. Does that mean I can just call remove with absolutely no parameters, and the DataService be smart enough to know what data I actually wanted to delete? That would be pretty sweet wouldn’t it?
// Perform rediculous magic data deletion :D service.remove();
Yeah, not so much. The two callbacks, and the context data are completely optional, but the first two parameters are exclusively optional. There are three possible scenarios for calling the remove method:
- Provide the instance of the entity object you want to delete and omit the resource URI.
- Provide the resource URI and omit the entity object instance.
- Provide the entity object instance and resource URI, which would be overkill, but perfectly acceptable.
If I already had a reference to the carrier I wanted to delete I could use the following:
service.remove(carrier, null, onSuccess);
If I didn’t have a reference to the carrier I wanted to delete, but I knew its resource URI, I could use the following:
service.remove(null, "Carriers(2)", onSuccess);
The awesome thing about the above approach is that you don’t have to make an extra service call to retrieve the entity object you want to delete, you can simply target it by URI, deleting it in one request. This is one thing that the DataService class does better than its WebDataContext counterpart. Using the later always requires a reference to the entity object instance you want to delete.
As you may have guessed, just like insert and update, the remove method returns the Sys.Net.WebRequest object used internally to make the HTTP DELETE request. Also note, the operation parameter passed to the success/failure callbacks will always equal “remove”.
Being able to perform data modification operations against a data service using the ASP.NET AJAX is extremely useful, but having a unit of work is essential in a lot of cases, and the above DataService method’s don’t satisfy that need. Luckily the DataService class does in fact support unit of work behavior for performing a series of data modification operations as a group. In the next article we will discuss action sequences.
0 Responses to “ADO.NET Data Services Part 8: Data Modification - AJAX”
Leave a Reply