LA.NET [EN]

Oct 30

Getting started with the DataContext object

Posted in Javascript MS AJAX      Comments Off on Getting started with the DataContext object

Yesterday, I’ve started looking at the DataContext object. I haven’t still tested all its features (to do that, I still need to take a look at ADO.NET Data Services and study the AdoNetDataContext type), but I’ve already picked up some ideas about it.

I guess it’s fair to say that you can think of the DataContext as a buffer which keeps track of the changes  you do along the time. Besides tracking basic changes, it does also support several more advanced concepts like links (which are just “fancy relations” between different objects – I think that most of this stuff is there to support interactions with the ADO.NET Data Services).

Since this is just an intro post, we’ll only be looking at its basic features. Creating a new DataContext instance can be easily accomplished through the Sys.create object helper:

<script type="text/javascript">
  var items = [
    { name: "luis", address: "fx" },
    { name: "john", address: "fx" },
    { name: "peter", address: "fx" }
  ];
  var _ctx = null;
  Sys.require([Sys.components.dataContext])
  Sys.onReady(function () {
    _ctx = Sys.create.dataContext();
});
</script>

Once again, we rely on the script loader for getting the necessary JavaScript files and populating the helper objects with new methods (ex.: Sys.create.dataContext). The previous DataContext instance is “empty”. However,I’m positive that in most scenarios you’re really interested in populating it with some data that will be tracked over time. The DataContext lets you do that through trackData method:

_ctx.trackData(items);

If you’ve looked at the DataContext’s prototype object,then you might think that calling trackData ends up initializing the items property. However, that will only happen if the DataContext object has been previously configured with identity related methods (we’ll be talking about this feature in future posts). Since we’re not using these advanced features, we’ll end up getting the data saved in the _lastResults property:

datacontext1

Now that the DataContext has been “initialized” with data, we can use it to, say, add a new item to that internal array:

_ctx.insertEntity({ name: "charles", address: "lx" });

You won’t be surprised by knowing that the previous line ends up “adding” a new item to the list of objects tracked by the _ctx DataContext instance. However, the new item won’t be added to the _lastResults array; instead, it gets added to the private _inserts property:

datacontext2

I’ve expanded the _lastResults, _items and _inserts fields of the _ctx instance. As you can see, _lastResults remains unchanged. The inserted item is appended to the _inserts array and you’ll also get a new entry on the _changeslist property. Even though I’m mentioning the _lastResults and _inserts fields, the truth is that you shouldn’t use them in your code (they’re considered private details and should only be managed by the DataContext instance). You can, however, get a reference to the _changeslist field through the get_changes public property:

var changes = _ctx.get_changes();

The local variable changes is just a collection of Sys.Data.ChangeOperation instances, which expose the following properties:

  • action: value from the Sys.Data.ChangeOperation enumeration (insert, update or remove) which identifies the current entry;
  • item: reference to the item of the operation (when adding, editing or removing items from the data context, this property references the object which was used in the operation);
  • linkSource: used for identifying the source of a link operation;
  • linkSourceField: identifies the field of the linkSource object used in the link operation;
  • linkTarget: identifies the target object used in a link operation.

The last three properties displayed in the previous list are only used for link operations (which we’ll talk about in future posts).

Since changes is an array, we can print all the changes by using a simple for loop. Notice that this will only show changes which haven’t been “persisted” by the DataContext instance (more about this topic at the end of this post):

for (var i = 0; i < changes.length; i++) {
  alert(changes[i].action + "-" +
                changes[i].item.name + "-" +
                changes[i].linkSource + "-" +
                changes[i].linkTarget + "-" +
                changes[i].linkSourceField);
}

To wrap up the data context’s insertEntity method topic, I’d like to add two things:

  • currently, the method expects two parameters: the first, is a reference to the entity that you’re adding to the current context; the second, is supposed to be a string which identifies the entity set to which this element belongs (this will be used when the DataContext instance has been customized to support identities – since we’re not using those features, we didn’t pass anything for this second parameter);
  • all the insertion operations need to go through the insertEntity method… and this is also true for observable arrays (in other words, if you’re thinking that you can add new items to an observable array which has been associated with a DataContext through the use the “observable array methods” and still have those changes recorded by the DataContext, then forget it).

Now that we’ve seen how insertion works, it’s time to see how we can remove items from the DataContext instance. In this case, we must use the removeEntity method:

_ctx.removeEntity(items[0]);

Removing an item ends up adding a new ChangeOperation to the internal _changelist field. If you run the previous for loop, you’ll see that it has two changes: one for the insertion and another one for the removal of the item. And yes, the private _deletes array will end up with one entry (which references the element that has been removed).

Now, lets make things a little more interesting…Take a look at the following snippet:

var aux = { name: "charles", address: "lx" };
_ctx.insertEntity(aux);
_ctx.removeEntity(aux);

What do you expect to happen now? If you’re thinking that you’ll end up with _insert and _delete empty arrays and with no changes (ie, with an empty _changeslist array), then you’re absolutely right. That happens because you’ve inserted and removed the same item without “committing” the insertion. The next image shows these results:

datacontext3

From the three basic operations, we’re only missing one: editing an item. To understand editing, we need to go back and talk a little more about tracking data. Besides initializing the _lastResults field, the trackData ends up hooking all the array items’ property changed events so that the DataContext instance is notified of any change made to any of the objects that it’s tracking.

In practice, this means that you’ll need to use the observable methods to change an item that is being tracked by a DataContext instance. For instance, the next snippet changes the address of the first item that is being tracked:

Sys.Observer.setValue(items[0], "address", "pt");

Which produces the following internal changes in the DataContext instance:

datacontext4

Btw, do notice that in editing scenarios, the item ends up being modified (notice the _lastResult’s first item address). As you might expect, the edit operation ended up leading to new entries in the private _changelist and _edits fields too.

Notice that if you’re updating an item which was added after the DataContext has started tracking data, then that edit operation will be performed, but it won’t be registered in the _changeslist collection (because there’s still a pending insert operation which, when performed in the back office, ends up doing everything  with a single insertion instruction).

And I guess this covers the basic operations related with inserting, editing and removing items that are being tracked by a DataContext object. In the next posts, we’ll keep looking at this object. Stay tuned for more in MS AJAX.