20
Jun
09

How the DataContext can change your data and your life (well, sort of, but not really)…

In the previous post I explained how the DataContext can be used for read-only scenarios, both in a standalone fashion and in conjunction with a DataView. I explained how the additional level of abstraction over an underlying service proxy can be beneficial, but ultimately alluded to the fact that the DataContext’s true power lies in its unit of work behavior that can be achieved pretty trivially. In this post I want to dig into how the change tracking and data persistence functionality of the DataContext works.

Let’s pick up where we left off in the previous post. We had an ASMX service already in place for returning customer data. Using ASP.NET AJAX 4 we could create a DataContext that pointed at the service and associate it with a DataView to dynamically display all customers returned from the service. One thing that we didn’t mention is the DataContext’s saveOperation property. We could modify our previous example like so…

var dataContext = $create(Sys.Data.DataContext, { 
                               serviceUri: "Customers.asmx", 
                               saveOperation: "SaveCustomers" 
                          });       
 
var customersTemplate = $create(Sys.UI.DataView, { 
                            autoFetch: true, 
                            dataProvider: dataContext, 
                            fetchOperation: "GetCustomers", 
                            fetchParameters: { count: 10 } 
                        }, 
                        null, 
                        null, 
                        $get("customers-template"));

Notice that nothing has changed from the perspective of the DataView declaration. The only difference between the above sample and the read-only scenario we created in the previous post is the existence of the saveOperation property on the DataContext. Here we’re telling the DataContext that there is a method called “SaveCustomers” on our ASMX service that is responsible for handling the persistence of changes. When we call the saveChanges method on the DataContext, it will trigger a request to the save operation method, passing it a change set that represents all of the changes made to the local data since the last time it was saved.

Two questions arise here that need to be addressed before moving on:

  1. How is it that changes are happening to the data coming back from the DataContext?
  2. How is it that the DataContext can know which exact changes have happened when trying to persist them back to the server?

Remember in the previous post that I mentioned that the DataContext was basically a class that can connect to a server-side resource, as long as that resource serves JSON data? That technically isn’t true, since the DataContext could work just fine with services that return XML or other payload formats. The reason I limited the DataContext’s abilities is because the only type of data that it can provide change tracking on is JSON data, and since change tracking is the key feature of the DataContext, it should almost be viewed as a limitation.

The main value of services that return JSON data is that once that data is returned to the JavaScript client it can immediately begin using it since that data is represented by instances of objects, as opposed to XML that would need to be parsed. To answer the first question above, since the DataContext is retrieving JSON data from services, and JSON data is simply object instances in memory, changes can happen to the data as easily as modifying property values on the returned objects. But, if your using a DataContext simply to associate it with a DataView (or some other control), you’re not necessarily handling the returned data yourself, but rather, the DataView is. Let’s take a brief detour into client templates…

In all of the samples that I’ve been using thus far I’ve instantiated a DataView around an element called “customers-template”. What exactly is “customers-template”? It is an instance of a client template (another new feature in ASP.NET AJAX 4) that is responsible for displaying instances of customers. The customers client template, in our example, looks like this…

<table cellspacing="0">
    <thead>
        <tr>
            <th>Title</th>
            <th>First Name</th>
            <th>Middle Name</th>
            <th>Last Name</th>
            <th>Suffix</th>
        </tr>
    </thead>
    <tbody id="customers-template" class="sys-template">
        <tr>
            <td><input type="text" value="{binding Title}" /></td>
            <td><input type="text" value="{binding FirstName}" /></td>
            <td><input type="text" value="{binding MiddleName}" /></td>
            <td><input type="text" value="{binding LastName}" /></td>
            <td><input type="text" value="{binding Suffix}" /></td>
        </tr>
    </tbody>
</table>

What we have here is a template that represents a tabular display of customers, including the full name, title, and suffix. The part that represents the actual client template is the <tbody> element. Notice that the ID of the <tbody> corresponds to the ID of the element that is represented by the DataView control we created. What the DataView will do is ask its corresponding DataContext for data (which will call its respective service), instantiate an instance of its associated client template, and create an instance of the template for every instance of data that is retrieved from the DataContext. Hence, in our example, we will get a table row for every customer.

In order to actually interpolate data into the client template you use what are called bindings. ASP.NET AJAX’s bindings look very similar to those in WPF/SL, and are represented by the contents of the value attributes of the <input> elements in the example above. Bindings are a fairly complex topic that I won’t get into it this post, but for the sake of our sample you can think of bindings as being able to synchronize two property’s values together. In this case, we’re synchronizing the value property of the textboxes to individual properties of the customer objects (remember that we’re getting back JSON data, which includes properties for all of the customer’s assets).

If we were to run this sample as is right now, we’d get something like this (I have some basic CSS in place as well)…

image

As expected, we have a table with a row for each customer, and the customer’s name, title, and suffix are bound to individual textboxes. This is great for read-only scenarios so far, but since we’re using textboxes for this display, it would be great to be able to edit the customer data and push it back to the server. Because we used bindings to attach the textbox values to customer properties, when you modify the value of any of the textboxes in this table you’re also modifying the value of its respective customer object. This is a very subtle but very powerful feature of client templates and bindings. This also answers our question of how you can go about changing your data when using it in conjunction with a DataContext and DataView.

Let’s move on to the second question of how it is the DataContext can know about the changes. The act of binding data to controls is by no means anything new. Binding a customer to a row, and then each of its properties to textboxes is a pretty basic act. But, what we really need, is for each of the individual customer instances to be able to report back to their “parent” DataContext when they’ve changed. If I go up into the form above and enter “Sr.” into the Suffix textbox for Orlando Gee, I need that customer instance that represents Orlando Gee to notify the DataContext that he has changed. That way, whenever I call the DataContext’s saveChanges method, it would already know what changes have happened and can send them to the service.

If you were using WPF or WinForms or Silverlight, you would have to make your Customer class implement an interface called INotifyPropertyChanged. The whole purpose of this interface is to make objects capable of notifying some interested party when it has changed. This approach works alright but it is also painful and is frankly a gleaming example of the constraints of statically typed languages. JavaScript is a dynamic language and as such we shouldn’t be held down by such limitations. But the need still exists to have our customer objects report their changes back to the DataContext. How will that happen?

Attention: this entire paragraph is very key…whenever the DataContext is used to retrieve data from a service, it will automatically inject every JSON instance with the necessary logic so that it can report any changes back to the DataContext. The above example would flow something like this…

  1. The DataView asks its associated DataContext for data
  2. The DataContext requests its associated service for the data, and once retrieved injects the objects with change tracking behavior
  3. The DataView then creates an instance of its associated client template for every instance of data it received from its DataContext and binds the data instance to the client template instance
  4. If you modify data through the client template UI (i.e. a textbox), since the JSON object instance is bound to the UI, and the JSON objects contain change tracking logic, those changes will trigger a notification to the DataContext letting it know all of the changes
  5. When you call DataContext.saveChanges, it already knows about every change that has happened (by virtue of the binding notifications), and can simply send that change set to the service
  6. The DataContext will then call the service method specified in the “saveOperation” property, passing the change set

What this means is that you don’t have to worry about change tracking or data synchronization at all,  which is great because those are two of the most painful parts of developing data-driven applications (in my opinion). On the service-side you simply need to create the corresponding method to receive the save operation. For our ASMX service it could look like this…

[WebMethod] 
public void SaveCustomers(List<Change<Customer>>> changeSet) 
{ 
    // Save the customer data 
}

The Change class is a simple container class that wraps the data instance that was changed and the change that occured (e.g. Insert, Update, Delete). Within that service method we can now take the appropriate actions to persist the changes based on whatever our data store is.

While ASMX and WCF provide a great option for general-purpose services, if what you need is a service for the sole purpose of retrieving and persisting data then using either ASMX or WCF is a roundabout way to go. ADO.NET Data Services should be seen as the defacto option for data-centric services since it does most of the heavy lifting for you. In fact, if you’re using a DataContext with a DataView and a Data Service (that’s a lot of data stuff) then you don’t have to do anything extra in order to persist your changes. Data Services automatically includes the logic to receive a change set and push it back to your data store, which alleviates the need to create a save operation.

So what is the moral of the story? The DataContext class can be used for read-only service interaction, but truly shines when you need automatic change tracking and unit of work behavior. You can use the base DataContext class when working with an RPC-style service (i.e. ASMX/WCF), but in most cases, if you’re developing a data-centric application, using the AdoNetDataContext along with an ADO.NET Data Service is going to provide you with a much easier solution. The DataContext works beautifully with a DataView and allows you to create dynamic data-driven UI, that when used with live bindings, can provide a really compelling user experience.

There is though still an outstanding question here: what if you’re using ASP.NET MVC and you want to leverage controllers to handle all incoming requests? There are plenty of scenarios where you don’t want to have to create an ASMX/WCF/Data service just to provide data to an AJAX client, when your controllers are perfectly good for that (in fact you could argue that in a “true” MVC application, the whole point of the controller role is to intercept user interaction, so using another service type is slightly awkward). Remember in the last post how I mentioned that the DataContext was also a great foundation for creating more specific context’s for additional scenarios? In the next post I’ll show how we created a DataContext that provides full change tracking and persistence when using ASP.NET MVC.


6 Responses to “How the DataContext can change your data and your life (well, sort of, but not really)…”


  1. 1 Chris Bower Jun 22nd, 2009 at 9:35 am

    A great post. its the Sys.Observer class that provides most of the functionality to the DataContext object. I have been using the Sys.Observer to add changes from other sources back to the DataContext. Do you think this is necessary? or should you be able to create an application that updates everything through bindings.

  2. 2 Jonathan Carter Jun 23rd, 2009 at 2:42 am

    @Chris

    Yep, the Sys.Observer is indeed behind the automatic change-tracking. I opted not to get into that detail just yet in the posts :) Using the Sys.Observer to make changes back to the DataContext is in fact neccessary.

    -JC

  3. 3 Mohamed Meligy Sep 16th, 2009 at 4:57 pm

    This is VERY interesting. I really find it hard to believe it can work without defects :D
    I have some questions about this though:

    1- How does this work for “Remove” operations, let’s say you have this list and you want to have “Remove” button on each row, how would that work?. The same question can be asked about “Add New” and both would be solved if there`re such methods on client side, along with some “DataKey” notation.

    2- What if I want to use this for a SINGLE item, say a details view for one item instead of list and in-place editing. Would that be treated as a lit containing one item, or there is a another way to do it. I think i know how to load data for single object, but how about load/save?

    3- What does the “Change” class give you exactly, how does the API look like? (Is there API documentation somewhere?)

  1. 1 ASP.NET MVC Archived Blog Posts, Page 1 Pingback on Jun 22nd, 2009 at 4:57 am
  2. 2 Announcing the Microsoft AJAX CDN - ScottGu's Blog Pingback on Sep 16th, 2009 at 6:46 am
  3. 3 BusinessRx Reading List : Announcing the Microsoft AJAX CDN Pingback on Sep 16th, 2009 at 6:59 am

Leave a Reply




June 2009
S M T W T F S
« Apr    
 123456
78910111213
14151617181920
21222324252627
282930  

Categories