Now, turns up the comparatively complex database binding
sample. According to what we have discussed above, we find out the typical
scenarios on using DataSource; and also, in Sample 1,
we used an in-memory data source to simulate the web service calling with state.
In practice, however, this is not recommended. In this section, we are to
create a sample much closer to the enterprise development environment.
Introduction
1. Create the sample
Launch Visual Studio 2005 and then create a sample website
named DatabaseBinding using the "ASP.NET AJAX
CTP-Enabled Web Site" template, as well as choose Visual C# as the
built-in language. Next, modify page Default.aspx as illustrated in Figure 4.
Figure 4: The design-time sample web page
2. Behind the scene
Here are the HTML elements definitions in page Default.aspx.
Listing 6
<!--------------------------------------------------->
<div id="detailsView"></div>
<!--------------------------------------------------->
<input type="button" id="previousButton" value="Previous" title="Go to
previous row" style="width: 67px; height: 30px;" />
<span id="rowIndexLabel"></span>
<input id="nextButton" type="button" value="Next" title="Go to next row"
style="width: 67px; height: 30px;" />
<input type="button"
id="addButton" value="Add" title="Create a new row" style="width: 67px; height: 30px;" />
<input type="button"
id="delButton" value="Delete" title="Delete the current row" style="width: 67px; height: 30px;" />
<input type="button"
id="saveButton" value="Save" title="Save all pending changes" style="width: 67px; height: 30px;" />
<input type="button"
id="refreshButton" value="Refresh" title="Discard pending changes and get the latest data from the server"
style="width: 73px; height: 30px" />
<!--------------------------------------------------->
<div style="visibility:hidden;display:none" >
<div id="detailsTemplate" class="ListWindow">
Name: <input id="nameField" size="30" /><br />
Address:<br />
<textarea id="addressField" style="width: 428px; height: 130px" rows="4"
cols="4"></textarea><br />
</div>
<div id="emptyTemplate">
Loading Data...
</div>
</div>
According to the layout in Figure 4 and the code above, we
first define two navigator buttons—previousButton and nextButton, which
are used to navigate between the records of the client-side (NOT the server
side!) data source of control ItemView. Next, we define two buttons (addButton, delButton) to modify the
records in the database. The next two buttons, saveButton
and refreshButton, are directly corresponding with the save and load methods of the MS AJAX
client-side control DataSource. After that, a group of divs tags are used to describe
MS AJAX control ItemView, with each div tag in response to each template of
control ItemView. You can compare the layout and mappings for ItemView here with
those for ListView in Sample 1.
Create a Web Service connected with the database
1. Create a sample database—DataBind.mdf
By right click the project and select "Add new Item…"
and then choose the template "SQL Database," you can easily create an
empty database—here we name it DataBind.mdf.
Then, we add to it only one table named Employees with
three fields—Id
(int, primary key), Name (nvarchar(50), not empty), and
Address (nvarchar(50), not empty). And also, we have
created four simple stored procedures: DeleteRecord, GetAllRecords, InsertRecord, UpdateRecord, which are associated with the typical CRUD database
operation. Here we will not talk more about this trivial since our interests do
not lie in here.
2. Create a class—Employee
This class is very much like class Employees
in Sample 1, which acts as the OOP packing with its attribute DataObjectField
decorated member variables mapped to the fields defined in table Employees.
3. Two Helper classes—SqlHelper (borrowed
from Microsoft) and SqlTaskProvider
To make things easier and reusable, we have set up two
helper classes: one is SqlHelper borrowed from
Microsoft sample—TaskList, the other is
SqlTaskProvider. Since these contents are far from our attention, we do not
explain them either, and you can refer to the source code for details.
Now with the groundwork done, let us create a DataService
(derived from Web Service) connected with the database.
2. Create a DataService to connect with
database
Here is the key code snippet of file MyDataService.asmx.
Listing 7
//……(omitted)
using System.ComponentModel;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Data;
using System.Web.Script.Services;
using Microsoft.Web.Preview.Services;
using Demos.Employee; //defines class SqlTaskProvider
[WebService(Namespace = "http://tempuri.org/")][WebServiceBinding(ConformsTo =
WsiProfiles.BasicProfile1_1)][ScriptService]
public class MyDataService: DataService
{
[WebMethod][DataObjectMethod(DataObjectMethodType.Delete)]
public void DeleteRecord(Employee emp)
{
if (emp.Name == null)
{
throw new AccessViolationException();
}
new SqlTaskProvider().DeleteRecord(emp);
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Select)]
public List < Employee > GetAllRecords()
{
return new SqlTaskProvider().GetAllRecords();
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Insert)]
public void InsertRecord(Employee emp)
{
if (emp.Name == null)
{
throw new AccessViolationException();
}
new SqlTaskProvider().InsertRecord(emp);
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Update)]
public void UpdateRecord(Employee emp)
{
if (emp.Name == null)
{
throw new AccessViolationException();
}
new SqlTaskProvider().UpdateRecord(emp);
}
}
With a little study, you can make clear the calling
relations as shown in Figure 5.
Figure 5
Next, let us see how the data bindings are accomplished on
the client side. Note here we still use the xml-script mode instead of the manual
JavaScript programming.
Declarative programming on the client side
As with Sample 1, we first set up the mapping relationships between
the HTML elements defined above and the templates of control ItemView.
Listing 8
<components>
<dataSource id="EmployeeDataSource" serviceURL="MyDataService.asmx" >
</dataSource>
<itemView id="detailsView">
<bindings>
<binding dataContext="EmployeeDataSource" dataPath="data" property="data" />
<binding dataContext="EmployeeDataSource" dataPath="isReady"
<p class=Code-ASPXCxSpMiddle>property="element" propertyKey="enabled"/>
</bindings>
<itemTemplate>
<template layoutElement="detailsTemplate">
<textBox id="nameField">
<bindings>
<binding dataPath="Name" property="text" direction="InOut"/>
</bindings>
</textBox>
<textBox id="addressField">
<bindings>
<binding dataPath="Address" property="text" direction="InOut"/>
</bindings>
</textBox>
</template>
</itemTemplate>
<emptyTemplate>
<template layoutElement="emptyTemplate" />
</emptyTemplate>
</itemView>
Here, there are some points to be noticed. First, control
ItemView is typically used for showing one record with MS AJAX client-side data
binding solution, while control ListView is used for many records in some
range. Second, control ItemView uses two bindings: the first binding will bind
the data returned from DataSource to property data of
control ItemView to ensure the ItemView can get its required integrated dataset
from the data source; the second binding binds the property enabled
of ItemView to the property IsReady of DataSource, which
means that when the data source has not get ready, e.g. the data source is just
reading/writing data from/to the server, the ItemView will be set disabled. Third,
we have used two-way bindings, which means that not only will the changing of properties
of the source control (the one pointed to by dataContext
property) update the corresponding properties of the target control, but also the
changing of the target control will affect the source control. So, in this
example not only will the data changing in DataSource affect the data to be
shown in the Textbox controls but contents of these Textbox controls affect the
data of the DataSource. And also, we should notice that the changing of
DataSource will make the data dirty - the property isDirty
of the DataSource control will be set to true.
Next, come the definitions of the two navigator buttons.
Listing 9
<button id="previousButton">
<click>
<invokeMethodAction target="detailsView" method="movePrevious" />
</click>
<bindings>
<binding dataContext="detailsView" dataPath="canMovePrevious"
property="element" propertyKey="disabled"
transform="Invert" />
</bindings>
</button>
<label id="rowIndexLabel">
<bindings>
<binding dataContext="detailsView" dataPath="dataIndex"
property="text" transform="Add" />
</bindings>
</label>
<button id="nextButton">
<click>
<invokeMethodAction target="detailsView" method="moveNext" />
</click>
<bindings>
<binding dataContext="detailsView" dataPath="canMoveNext"
property="element" propertyKey="disabled"
transform="Invert" />
</bindings>
</button>
Here, control ItemView supplies some methods and properties
to be used to navigate between the records loaded into it. If the user is
browsing the first record, then property canMovePrevious
is set to false; or else true. In
this example, when the user is browsing the first record, button previousButton is set disabled automatically.
In addition, we specify a corresponding action for the click
event of button previousButton: call method movePrevious of control ItemView. As for button nextButton, things are just the same. Moreover, we read the
index value of current record in the dataset through property dataIndex,
and bind it to a label control.
Now comes the most interesting and important part with
database CRUD operations.
Listing 10
<button id="addButton">
<click>
<invokeMethodAction target="detailsView" method="addItem" />
</click>
<bindings>
<binding dataContext="EmployeeDataSource" dataPath="isReady"
property="element" propertyKey="disabled" transform="Invert" />
</bindings>
</button>
<button id="delButton">
<click>
<invokeMethodAction target="detailsView" method="deleteCurrentItem" />
</click>
<bindings>
<binding dataContext="EmployeeDataSource" dataPath="isReady"
property="element" propertyKey="disabled"
transform="Invert" />
</bindings>
</button>
<button id="saveButton">
<click>
<invokeMethodAction target="EmployeeDataSource" method="save" />
</click>
<bindings>
<binding dataContext="EmployeeDataSource"
dataPath="isDirtyAndReady" property="element"
propertyKey="disabled" transform="Invert" />
</bindings>
</button>
<button id="refreshButton">
<click>
<invokeMethodAction target="EmployeeDataSource" method="load" />
</click>
<bindings>
<binding dataContext="EmployeeDataSource" dataPath="isReady"
property="element" propertyKey="disabled"
transform="Invert" />
</bindings>
</button>
When you need to add a new record to the dataset, you should
call method addItem of control ItemView - only when the
data source get ready can you call this method. As for button delButton,
things are very similar. When the data source is ready, method deleteCurrentItem of control ItemView can be called; or else,
button delButton is set disabled
with data binding.
As for button saveButton, things become
a bit complicated. Only when the dataset becomes dirty and the data source gets
ready, can we save the data. You should recall that the TextBox controls
defined inside the ItemTemplate of ItemView are all bond with two directions. So,
when the user alters contents of any of the TextBox controls the dataset in
control ItemView will be updated automatically, as well as the dataset in its
data source. As a result, the data source becomes dirty, and at the same time,
the data source gets ready, so property isDirtyAndReady
will be set to true. And so, when the data source
becomes dirty and gets ready, button saveButton is
enabled or else disabled.
When you click button refreshButton there
will occur a SELECT query, which will in turn trigger
all the bindings and load the newest data into the controls on the web page.
Note that the refresh operation occurs in the AJAX way (asynchronously), so
only control ItemView is updated without traditional flickering on the whole
web page.
4. Running the sample
Without any trouble, press F5 and you will see the runtime screenshot
like Figure 6.
Figure 6:
In this screen you can operate with the records (possibly
from a faraway database with the DataService as the intermedia) as those done
in a traditional application. Is it easy?