The DataContext component: Committing changes – take II

Published Mon, Nov 2 2009 15:22

In the previous post, we’ve seen how easy it is to commit the changes that are buffered by a DataContext instance. At the time, I said that there were still a couple of things related with the saveChanges method that needed further discussion…and that’s what we’ll do in this post.

To illustrate one scenario you might end up facing in the real world, lets assume that our person instances have a piece of information which can only be set in the server side. For instance, lets say that all created instances need to have a creation date that can only be set in the server side. Going back to our initial example, that means that each person instance in the initial items array needs to be updated with this extra info (keep in mind that items is supposed to have info about previously saved elements):

var items = [
    { name: "luis", address: "fx", date: new Date() },
    { name: "john", address: "fx", date: new Date() },
    { name: "peter", address: "fx", date: new Date() }
  ];

To make the code more real, we’ll be adding a DataView control which allows us to edit/add new items to this array. Take a look at the following image:

commit1

The code used for generating this page looks like this:

<head runat="server">
    <style type="text/css">
        .sys-template{
            display: none;
        }
    </style>
    <script type="text/javascript" 
      src="Scripts/MicrosoftAjax/start.debug.js">      
    </script>
    <script type="text/javascript">
        var items = [
          { name: "luis", address: "fx", date: new Date() },
          { name: "john", address: "fx", date: new Date() },
          { name: "peter", address: "fx", date: new Date() }
        ];
        var aux = { name: "charles", address: "lx" };
        var ctx = null;
        Sys.require([Sys.components.dataContext,
                     Sys.components.dataView],
            function () {
              Sys.Observer.makeObservable(items);
              ctx = Sys.create.dataContext({
                serviceUri: "PeopleService.svc",
                saveOperation: "SavePeople",
                saveHttpVerb: "POST"
              });
              ctx.trackData(items);
              $addHandler(
                      Sys.get("#add"),
                      "click",
                      function () {
                       var item = { name: "", address: "" };
                       ctx.insertEntity(item);
                       items.add(item);
                      });
              $addHandler(
                    Sys.get("#save"),
                    "click",
                    function () {
                      ctx.saveChanges();
                    });
            });
    </script>
</head>
<body
      xmlns:sys="BLOCKED SCRIPTSys"
      xmlns:dv="BLOCKED SCRIPTSys.UI.DataView">
    <div id="view"
         class="sys-template"
         sys:attach="dv"
         dv:data="{{items}}">
      <div>
        <input type="text" sys:value="{binding name}" />
        <input type="text" sys:value="{binding address}" />
        <span>{binding date}</span>
      </div>
    </div>
    <input type="button" value="add new" id="add" />
    <input type="button" value="save changes" id="save" />
</body>

As you can see, the items array is used in two places: it feeds the data shown by the DataView control and it is tracked by the DataContext component. We’ve transformed the items array into an observable array so that the DataView is able to automatically pick up insertions and deletions.

Even though this works, there’s still a small problem here: the date of each new person instance can only be set in the server side. The previous web service didn’t return anything from the server side and that is why you’ll always get null for the date property.

If you look at the code of the DataContext component, you’ll notice that it checks for values that are returned from the server for a save operation. Take a look at the _processResults method (edited for improved readability):

function Sys$Data$DataContext$_processResults(
dataContext, changes, results) {
if (results && results.length === changes.length) { dataContext._ignoreChange = true; try { for (var i = 0, l = results.length; i < l; i++) { var result = results[i],
change = changes[i],
item = change.item;
if (result && typeof(result) === "object") { dataContext._fixAfterSave(
change, item, result); } } }
finally { dataContext._ignoreChange = false; } } }

As you can see, if the web service method used for saving returns a collection, the component will try to merge the client information with the one returned from the server side. It’s imperative that the returned collection has the same size as the array of changes (_changelist property) that was sent to the server and that each returned item’s position matches the “correct” position in the changes array. With this info, we can easily solve our initial problem by updating the server side save method:

[OperationContract]
public IEnumerable<Person> SavePeople(
IEnumerable<PersonOperation> changeSet) { var changed = new List<Person>(); foreach (var change in changeSet) { change.Person.Date = DateTime.Now;//bad!!!demo only _executors[change.OperationType](change.Person); changed.Add(change.Person); } return changed; }

We’ve updated the web service save method so that it returns the collection of person instances it received from the client. Notice that in this case I’ve forced an update of the Date property and that info will be propagated back to the client and picked up by the internal _processResults method (you shouldn’t really do this for all items – after all, we only need to set the date for insertions). And that’s why you’ll get dates after you click the save changes button:

commit2

And I guess that’s all for now. Stay tuned for more on MS AJAX.

Filed under: ,

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above:  

Search

This Blog

Tags

Community

Archives

Syndication

Email Notifications

News




  • View Luis Abreu's profile on LinkedIn


    Follow me at Twitter

    My books

    Silverlight 4.0: Curso Completo

    ASP.NET 4.0: Curso Completo

    Portuguese LINQ book cover

    Portuguese ASP.NET 3.5 book cover

    Portuguese ASP.NET AJAX book cover

    Portuguese ASP.NET AJAX book cover