深入剖析微软ASP.NET Ajax中的数据绑定构架下篇之一

深入剖析微软ASP.NET Ajax中的数据绑定构架下篇之一

一、上篇中的示例回顾

在上篇中,为了熟悉MS AJAX在客户端的数据绑定技术,我们给出了一个简单的例子AJAXCTPDev311。其实,这个例子中还有许多重要的特征值得我们作深入分析:

提供了一个创建基本的MS AJAX数据绑定应用程序的典型框架;

在创建这个例子时,我们选用了“ASP.NET AJAX CTP-Enabled Web Site”模板;其实,你还可以选择普通的“ASP.NET AJAX-Enabled Web Site”模板,但是你必须手工地添加对程序集Microsoft.Web.Preview.dll(这个程序集提供对xml-script声明方式编程和数据绑定技术的支持)的引用;

无论创建什么样的MS AJAX应用程序,MS AJAX服务器端控件ScriptManager在整个MS AJAX框架中都起着核心作用。典型情况下,我们都要显式地指定我们要使用的WEB服务相应的*.asmx文件和所使用的程序集。下面我们再次列出这部分的相关代码:


<asp:ScriptManager ID="ScriptManager1" runat="server" >

<Services>

<asp:ServiceReference Path="BookDataService.asmx" />

</Services>

<Scripts>

<asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />

</Scripts>

</asp:ScriptManager>

控件ListView(以及控件ItemView,我们将在第三个例子中使用之)提供了一组现成的模板,而典型情况下这些内容又进一步与一组HTML div元素相关联(它们的内部又可以进一步包含基本的HTML元素,例如buttontextboxspanhyperlink等)。值得注意的是,为了熟练掌握ListViewItemView两个MS AJAX高级控件,你必须细致分析这些元素与这两个控件中各种模板的对应关系;

声明性编程是MS AJAX新提供的一种客户端编程技术,我们将在后面的部分对之作更为深入的讨论; 

下面的框图大致概括了我们所使用的几个关键部件间的关系:

1:几个关系部件间的关系图

在此,请注意:第一,位于ListViewDataSourceWebService处的数据形式是不同的;第二,在实际开发情况下,WebService一般以数据库数据的形式提供数据(我们将在第三个例子中使用这种情形);

在本例中,我们使用了一个特别类型的WebService—DataService来实现从服务器端为客户端的ListView控件提供数据;

我们在本例中直接从客户端调用了WebService方法这是MS AJAX带给我们的巨大变化。

在接下来的部分中,我们将详细更为细致地分析MS AJAX框架提供的xml-script编程(也即声明性编程)思路。

二、MS Ajax中的声明性编程

除了使用直接的JavaScript编程方式外,MS AJAX还提供了另一种实例化客户端类型的方式声明性编程模式(其实在ASP.NET中已经提供了类似的实现方式)。注意,为了使用这种方式进行编程,你必须从微软Ajax站点下载Futures CTP包并且添加一个到脚本文件PreviewScript.js(包含在程序集Microsoft.Web.Preview中)的引用。有关这种ASP.NET AJAX 1.0声明性编程的更为细致的讨论,你可以参考Alessandro Gallo的xml-script明性编程教程。但是,即使这里的教程也只是提及了有关xml-script编程的基本知识;因此,要想精通这种声明性编程,我的意见是,一方面你应该深入研磨伴随MS AJAX发行的示例程序—TaskList;更深一步的话,应该是深入研究文件PreviewScript.js中的有关xml-script语法的解析器部分(这是xml-script编程的优点吗?)。

在此,我们仅列出使用声明性编程的基本步骤,具体应用请参考后面的有关例子:

按通常方式设计你的WEB页面定义相应的HTML元素;

创建MS AJAX客户端控件以关联于这些HTML元素;

操纵这些MS AJAX客户端控件,进而间接控制相应的HTML元素。

为了进一步简化声明性编程,MS AJAX不仅引入了大量的客户端控件(例如LabelButtonInputControlTextBoxImageHyperLinkCheckBoxSelectItemViewListView,等等),而且还新创建了许多高级概念(例如ActionBehavior,数据绑定等)。

根据本人的理解,使用声明性编程至少存在两个理由:通过配置相应的XML元素或标签来简化单纯的JavaScript脚本式编程(毕竟有大量的WEB开发人员对JavaScript并不深入了解);这也是适应未来编程模式的要求通过把应用程序的设计与实现分离开来从而简化程序的开发,而且简化系统的维护、更新、国际化、第三方开发、重编译,乃至系统的发布。尽管存在种种优点,但由于这种新模式编程刚刚开始(在其它系统下大概也是如此),所以,也存在一些相关的困难,例如调试问题,尚未出现现成的高级IDE工具来简化这种XML声明式编程。

三、例1—HTML ListView控件绑定到一个Web服务

()设计WEB页面

首先,请注意这个例子与第一部分的那个例子存在很大的相似之处。但是,在此我们将详细分析第一个例子中所没有涉及到的许多问题。现在,请启动Visual Studio 2005并选择模板“ASP.NET AJAX CTP-Enabled Web Site”创建一个新的网站并命名为ListEmployees。下图2展示了这个例子的设计时刻界面。

2:示例程序的设计时刻界面

在这个例子中,我们将仍然使用一个ListView控件来显示一个雇员列表信息;所有这些都是在系统启动后自动完成的。现在,根据前面分析的步骤开始进行操作。

1.设计WEB页面

列表2

//……省略

<link rel="stylesheet" type="text/css" href="site.css" />

//……omitted

<span style="font-size: 24pt ; color: #6600cc">

<strong>Employee Info Searching Results:</strong>

</span>

<div id="header">

<span >

<strong>EmpID</strong> &nbsp;

</span>

<span >

<strong>Name</strong> &nbsp;

</span>

<span>

<strong>Address</strong>

</span>

</div>

<hr style="width: 457px" align="left" />

  <div id="searchResults">

</div>

<div style="visibility:hidden;display: none;">

<div id="searchResults_layoutTemplate">

<div id="searchResults_itemTemplate" >

&nbsp; &nbsp;<span id="searchResults_ID"></span>

         &nbsp;&nbsp;&nbsp;&nbsp;

<span id="searchResults_Name"></span>&nbsp;&nbsp;

<span id="searchResults_Address"></span>

</div>

<div id="searchResults_separatorTemplate"

         class="TaskSeparator">

</div>

</div>

<div id="NoDataTemplate">Waiting...</div>

</div>

在此,我们首先使用了一个固定行(通过一个div块包装)用作下面将列出雇员记录数据的头标题部分。然后,我们构建相应于MS AJAX客户端控件ListView的各种模板的HTML元素。注意,我们也使用了separatorTemplate模板来分离相邻的两条记录。正如在第一部分中所提到的,控件ListView是以AJAX异步的方式来显示记录数据的。为此,我们也使用了emptyTemplate模板—NoDataTemplate以便当数据正从WebService处加载时给用户一种友好的提示。

2.创建相应的MS AJAX客户端控件

在此,我们只创建了一个MS AJAX客户端控件ListView来匹配HTML元素(一组div元素)并使其每一个模板相应于不同的div元素。此外,我们还定义了三个label元素以匹配上面的三个span元素。

()创建Web服务

右击工程并且选择添加新项,然后创建一个新的Web服务,并命名为EmployeeDataService.asmx。注意,我们还要使这个Web服务派生自DataService,然后创建四个相应于典型的数据库操作CRUDWebMethod。下面展示了相应的关键代码:

列表3

//……

using System.ComponentModel;

using System.Web.Script.Services;

using Microsoft.Web.Preview.Services;

//……

[ScriptService]

public class EmployeeDataService : DataService

{

static List _data;

static int _nextId;

static object _dataLock = new object();

private static List Data

{

get

{

if (_data == null)

{

lock (_dataLock)

{

if (_data == null)

{

_data = new List();

_data.Add(new Employees(0

"John Smitch""1970 Napa Ct."));

_data.Add(new Employees(1

"Mary Smitch""9833 Mt. Dias Blv."));

_data.Add(new Employees(2

"Mike Jodan""7484 Roundtree Drive"));

_data.Add(new Employees(3

"Ronald Adina""9539 Glenside Dr"));

_data.Add(new Employees(4

"Blue Yonder Airlines""1226 Shoe St."));

_data.Add(new Employees(5

"Milton Albury""1399 Firestone Drive"));

_data.Add(new Employees(6

"Phyllis Allen""5672 Hale Dr."));

_data.Add(new Employees(7

"Stanley Alan""6387 Scenic Avenue"));

_data.Add(new Employees(8
"Alexander Berger"

"8713 Yosemite Ct."));

_nextId = 9;

}

}

}

return _data;

}

}

[WebMethod]

[DataObjectMethod(DataObjectMethodType.Delete)]

public void DeleteRecords(int id)

{

foreach (Employees row in _data)

{

if (row.Id == id)

{

lock (_dataLock)

{

_data.Remove(row);

}

break;

}

}

}

[WebMethod]

[DataObjectMethod(DataObjectMethodType.Select)]

public Employees[] SelectRecords()

{

return EmployeeDataService.Data.ToArray();

}

[WebMethod]

[DataObjectMethod(DataObjectMethodType.Insert)]

public Employees InsertRecords(string name
string Address)

{

Employees newRow;

lock (_dataLock)

{

newRow = new Employees(_nextId++
name Address);

_data.Add(newRow);

}

return newRow;

}

[WebMethod]

[DataObjectMethod(DataObjectMethodType.Update)]

public void UpdateRecords(Employees updateRow)

{

foreach (Employees row in _data)

{

if (row.Id == updateRow.Id)

{

row.Name = updateRow.Name;

row.Address = updateRow.Address;

break;

}

}

}

}

public class Employees

{

private int _id;

private string _name;

private string _address;

[DataObjectField(true
true)]

public int Id

{

get { return _id; }

set { _id = value; }

}

[DataObjectField(false)]

[DefaultValue("")]

public string Name

{

get { return _name; }

set { _name = value; }

}

[DataObjectField(false)]

[DefaultValue("")]

public string Address

{

get { return _address; }

set { _address = value; }

}

public Employees()

{

_id = -1;

}

public Employees(int id
string name string address)

{

_id = id;

_name = name;

_address = address;

}

}

注意,在这个例子中,为了简化问题,我们使用了一个内存数据源以实现对有状态的WEB服务的模拟,但在实际开发中这一般是不推荐使用的。

接下来,让我们深入探讨那个神秘DataService

()关于DataService的讨论

在写本文时,我细致地使用关键字“DataService”搜索了整个因特网但却得到极少的几篇与这个主题相关的文章(例如这一篇http://forums.asp.net)。所以,我们的主要参考资料就是随同MS AJAX框架发行的几个例子。此外,我还使用VS2005中的对象浏览器和反射工具搜索到了有关的一少部分信息。现在,我把它们粘合在一起,谈谈自己对这个神秘DataService的理解。

首先,尽管最近你可以从WEB或者是MS AJAX所附教程中非常容易地找到大量的从ASP.NET AJAX框架的客户端JavaScript中消费普通WEB服务相关的操作技巧,但是有关于直接从客户端JavaScript消费派生自DataServiceWEB服务方面的内容却实在不多。根据本人的粗浅研究,DataService应该主要使用于如下的典型场所下:当我们使用高级客户端控件ItemViewListView,而且其数据源来自于WEB服务提供的数据库,并且要求进行数据库相关的典型操作时(例如CRUD);而在其它情况下,我们一般选用普通的WebService。微软是否针对ListView/ItemView特别设计了DataService?通过进一步使用.NET对象浏览器和Lutz Roeder的高级.NET Reflector工具(提示:这是一个相当优秀的工具,借助于它你可以获取类DataService的全部源码)剖析程序集—Microsoft.Web.Preview.dll,我们还注意到DataService派生自WebService,而且只对外暴露了两个Web方法:

列表4

public DataTable GetData(object parameters string loadMethod);

public DataTable SaveData(ChangeList changeList

object parametersstring loadMethod);

通过进一步分析位于文件PreviewScript.js内的Sys.Preview.Data.DataSource.saveSys.Preview.Data.DataSource.load的源码,我们会很容易发现:

1、方法save要求DataSourceserviceType属性必须为类型Sys.Preview.Data.ServiceType.DataService;之后,方法save被转向调用类DataService的方法SaveData

2、对于方法load则存在两种情况:当DataSourceserviceType属性为类型DataService时,方法load被转向调用类DataService的方法GetData;而当DataSource的属性serviceType不属于类型DataService时(例如为Handler),系统将创建一个普通的代理,然后把方法load转发到Sys.Net.WebRequest()。当我们使用.NET Reflector来进一步研究类DataService的方法SaveDataGetData时,我们发现了更为有趣的事情了解DataSourceloadMethod属性的意义以及数据库CRUD操作如何响应于你的DataServiceDelete(DeleteRecords)Insert(InsertRecords)Select(SelectRecords)Update(UpdateRecords)方法的,还蕴含了更多的秘密……

【作者注】极力推荐你研究一下类DataService的实现源码,你将会发现许多有用的东西。

借助于新引入的属性—DataObjectMethodDataObjectField(你可以参考MSDN中两个类DataObjectFieldAttributeDataObjectMethodAttribute作进一步研究),我们可以很容易地实现我们的目标提供为控件DataSource所使用的属性和方法(如FillSelectUpdateInsertDelete等操作)。所有这些都可以在下一节中通过轻松的xml-script声明式编程来实现。

()实现绑定

现在,我们来看一下如何使用xml-script声明性方式来实现对于控件ListView的数据绑定。下面是相应于前面HTML元素定义部分的xml-script代码部分。

列表5

<script type="text/xml-script">

<page xmlns="http://schemas.microsoft.com/xml-script/2005">

<components>

<dataSource id="EmployeeDataSource"

serviceURL="EmployeeDataService.asmx" >

</dataSource>

<listView id="searchResults"

itemTemplateParentElementId="searchResults_layoutTemplate" >

<bindings>

<binding dataContext="EmployeeDataSource"

dataPath="data" property="data" />

</bindings>

<layoutTemplate>

<template layoutElement="searchResults_layoutTemplate" />

</layoutTemplate>

<itemTemplate>

<template layoutElement="searchResults_itemTemplate">

<label id="searchResults_ID">

<bindings>

<binding dataPath="Id" transform="Add"

property="text" />

</bindings>

</label>

<label id="searchResults_Name">

<bindings>

<binding dataPath="Name" property="text" />

</bindings>

</label>

<label id="searchResults_Address">

<bindings>

<binding dataPath="Address" property="text" />

</bindings>

</label>

</template>

</itemTemplate>

<separatorTemplate>

<template

layoutElement="searchResults_separatorTemplate" />

</separatorTemplate>

<emptyTemplate>

<template layoutElement="NoDataTemplate" />

</emptyTemplate>

</listView>

<application>

<load>

<invokeMethodAction target="EmployeeDataSource"

method="load" />

</load>

</application> 

</components>

</page>

</script>

在上面代码的第一行,我们定义了一个将用于后面相应控件的dataSource。注意,在此虽然没有显式声明,但是dataSource控件的ServiceType属性已经被置为DataService。接下来,我们开始具体的数据绑定,使用属性dataContext指定控件ListView的数据源,使用属性dataPath指定源绑定属性,使用属性property指定目标的被绑定属性。接下来,我们开始把数据源的属性Id绑定到标签searchResults_IDtext属性上,把数据源的属性Name绑定到标签searchResults_Nametext属性上,把数据源的属性Address绑定到标签searchResults_Addresstext属性上。在第一个绑定中,我们引入了一种Add类型的转换器(还有许多转换器,请参考位于文件PreviewScript.js内的类Sys.Preview.BindingBase的代码部分)。另外,由于控件DataTable暴露的DataIndex属性值是从0开始的,通过使用Add类型转换器可以自动导致其值加1,这更适合于大多数用户的习惯。最后的块部分告诉系统自动调用数据源的EmployeeDataSourceload方法。在此为什么使用load以及这个load方法背后又隐藏着什么机理呢?请详细参考前面有关DataService的讨论。

观看运行结果

毋庸多言,请观看图3中的运行时刻结果快照吧!

3:示例1的运行时刻快照

在此,当示例程序启动时,数据绑定自动发生;之后,客户端控件ListView把相应于服务器端的数据展示到客户端屏幕上。

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值