使用ASP.NET AJAX异步调用Web Service和页面中的类方法(6):服务器端和客户端数据类型的自动转换:复杂类型
2007-06-12 09:52
1506 查看
本文来自《ASP.NETAJAX程序设计第II卷:客户端MicrosoftAJAXLibrary相关》的第三章《异步调用WebService和页面中的类方法》,请同时参考本章的其他文章。
3.7.3复杂类型
若想传递某些复杂的数据类型,比如某个自定义类型或结构,那么我们要显式告知ASP.NETAJAX异步通讯层为该服务器端类型生成相应的客户端JavaScript类型。
如下C#代码描述的Employee类就可以看作一个“复杂类型”,让我们以这个Employee类为例,说明在ASP.NETAJAX异步通讯层传递复杂类型的方法:
[code]{
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
可以看到,该Employee类共包含4个公有属性:Id、Name、Email和Salary,分别表示员工的编号、姓名、电子邮件地址以及工资。除了这4个公有属性之外,Employee类还提供了两个构造函数。但并没有定义任何方法(即所谓的“退化类”。“退化类”与结构体类似,用来表示结构化的数据)。本小节以及随后的若干小节都将使用该Employee类,为加深印象,我们在这里给出了Employee类的类图,如图3-16所示。
注意:若想让ASP.NETAJAX异步通讯层为服务器端类型自动生成相应的客户端版本,那么该类型一定要提供一个无参数的构造函数,且该类所有的公有属性都应该提供getter和setter访问器。另外,在根据服务器端类型自动生成的客户端版本类型中,只有原服务器端类型的公有属性会被保留下来,原类型的方法和私有字段都将不会映射到客户端对象中(公有字段会被映射到客户端对象中)。
图3-16Employee类的类图
了解了Employee类之后,让我们通过一个示例程序介绍如何在ASP.NETAJAX异步通讯层传递该Employee类型。首先是服务器端的WebService代码,其中定义了两个方法:CreateNewEmployee()和SaveEmployee(),分别将返回或接受一个Employee对象:
[code][WebService(Namespace="http://tempuri.org/")]
[/code]
CreateNewEmployee()和SaveEmployee()方法都不能完成什么具体的工作,仅仅起到演示功能。PeopleManagementService所应用的[GenerateScriptType(typeof(Employee))]属性(上述代码中的粗体部分)才是完成程序功能的关键之处,该属性定义于System.Web.Script.Services命名空间中。若我们希望让ASP.NETAJAX异步通讯层为某一复杂类型生成客户端的JavaScript对应类型,以便完成服务器和客户端之间的通讯,则应该用GenerateScriptType属性显式声明。
注意:实际上,对于“最外层”(即在WebService方法的参数或返回值出现过)的复杂类型,[GenerateScriptType(typeof([TypeName]))]属性并不是强制要求的。但若是最外层复杂类型中还嵌套有内层的复杂类型,比如Employee中有个属性指向的是另一个复杂类型——Manager(这个Manager类型即为嵌套复杂类型),那么我们就一定要显式为WebService添加[GenerateScriptType(typeof([Manager]))]属性。或者某WebService方法的参数或返回值为List<Employee>(将在稍后介绍),那么我们也要为WebService添加[GenerateScriptType(typeof([Employee]))]属性。
正是因为存在着这样的复杂性,所以本书建议对于任何出现过的复杂类型,均在WebService类中用[GenerateScriptType(typeof([TypeName]))]属性标出,以减少由不小心导致的遗漏所带来异常的可能性。另外,[GenerateScriptType(typeof([TypeName]))]属性也可以添加到WebService方法上,效果与添加到WebService类上相同。
然后在ASP.NET页面中添加ScriptManager控件以及相应的WebService的引用:
[code]<Services>
[/code]
页面的UI部分代码非常直观:
[code]<labelfor="tbName">Name</label><inputid="tbName"type="text"/><br/>
[/code]
前面四个<input/>分别将用来显示某个Employee对象的Id、Name、Email以及Salary属性,并提供了对这些属性的编辑功能。后面两个<input/>作为按钮,将分别调用前面WebService中定义的CreateNewEmployee()和SaveEmployee()两个方法的客户端代理。
btnNew_onclick()事件处理函数以及相应的回调函数如下:
[code]PeopleManagementService.CreateNewEmployee(onCreated);
[/code]
注意回调函数中的result对象即ASP.NETAJAX异步通讯层为C#Employee类自动生成的客户端类型。图3-17显示了在VisualStudio调试器中察看到的result对象(类型为Employee)的结构。
图3-17客户端Employee类型的结构
提示:在实际开发中,我们也可以直接使用客户端Employee类的构造函数,在客户端直接构造出一个Employee对象,而不必总是从服务器端取得程序中需要的新对象,以便提高应用程序的整体性能:
上面代码中的employeeInEditing为全局变量,用来保存当前正被编辑的客户端Employee对象,定义在这两个函数之外:
回调函数将服务器端新创建的Employee对象的4个属性值分别显示在页面中的4个文本框中,供用户编辑。如图3-18所示。
图3-18点击“CreateNew”按钮之后,程序将从服务器端取得一个新的Employee对象
编辑好这4个条目之后,点击“Save”按钮,将触发btnSave_onclick()事件处理函数。btnSave_onclick()事件处理函数以及相应的回调函数如下:
[code]employeeInEditing.Id=$get("tbId").value;
[/code]
其中只是简单地更新了客户端Employee对象的4个属性,然后通过ASP.NETAJAX异步通讯层将其传递到服务器端。图3-19显示了服务器端SaveEmployee()方法接收到的从客户端传入的Employee对象。
图3-19SaveEmployee()方法接收到的从客户端传入的Employee对象
在服务器端SaveEmployee()方法成功返回后,客户端onSaved()回调函数将在页面中弹出提示对话框。如图3-20所示。
图3-20点击“Save”按钮保存该Employee对象之后的程序界面
有些时候我们并不希望将某个服务器端类型中所有的属性均暴露给客户端对象。比如Employee类中的Salary属性,出于某些安全或隐私方面的考虑,我们或许想让异步通讯层在自动生成客户端对象(即将其序列化为JSON字符串)时忽略该属性,即只在客户端对象中生成Id、Name和Email三个属性。
ASP.NETAJAX异步通讯层能够很好地支持这个需求,我们所要做的只是为Employee类中的Salary属性添加[System.Web.Script.Serialization.ScriptIgnore]属性,修改后的Salary属性如下,注意其中粗体部分:
[code][System.Web.Script.Serialization.ScriptIgnore]
[/code]
这时,ASP.NETAJAX异步通讯层为Employee类生成的客户端JavaScript类型中就不再包含该Salary属性了。图3-21显示了在VisualStudio调试器中看到的客户端Employee类型的结构。
图3-21忽略Salary属性后客户端Employee类型的结构
总结:想要让ASP.NETAJAX异步通讯层为服务器端复杂类型自动生成相应的客户端JavaScript类型,并在调用过程中传递并接收该复杂类型,我们需要:
为WebService类或WebService中需要暴露给客户端的方法添加[ScriptService]属性;
为WebService中需要暴露给客户端的方法添加[WebMethod]属性;
WebService类中的某个方法的某个参数或返回值为该复杂类型;
为WebService类添加若干个[GenerateScriptType(typeof([TypeName]))]属性,[TypeName]表示该复杂类型或其嵌套的复杂类型的名称;
该复杂类型必须要有一个无参数的构造函数;
该复杂类型的所有公有属性应该提供getter和setter访问器(即需要可读可写),除非如下几种情况:
该属性应用了[System.Web.Script.Serialization.ScriptIgnore]属性,即让ASP.NETAJAX异步通讯层在生成客户端JavaScript类型时忽略该属性,那么其属性可以没有setter或getter。
该服务器端对象只是用来单向输出JSON字符串,那么其属性可以没有setter。
在客户端传入的时候不会设置该属性的值,那么该属性可以没有setter。
在页面中的ScriptManager控件中添加对该WebService的引用。
然后,ASP.NETAJAX异步通讯层在为某服务器端复杂类型生成客户端JavaScript类型时,将:
会把没有应用[System.Web.Script.Serialization.ScriptIgnore]的公有属性(property)或公有字段(field)映射到客户端JavaScript类型中;
不会把该复杂类型的私有字段映射到客户端JavaScript类型中;
不会把该复杂类型的方法(method)映射到客户端JavaScript类型中;
我们也可以使用如下语法在客户端直接创建该复杂类型:
3.7.3复杂类型
若想传递某些复杂的数据类型,比如某个自定义类型或结构,那么我们要显式告知ASP.NETAJAX异步通讯层为该服务器端类型生成相应的客户端JavaScript类型。
如下C#代码描述的Employee类就可以看作一个“复杂类型”,让我们以这个Employee类为例,说明在ASP.NETAJAX异步通讯层传递复杂类型的方法:
publicclassEmployee
[code]{
privateintm_id;
publicintId
{
get{returnm_id;}
set{m_id=value;}
}
privatestringm_name;
publicstringName
{
get{returnm_name;}
set{m_name=value;}
}
privatestringm_email;
publicstringEmail
{
get{returnm_email;}
set{m_email=value;}
}
privateintm_salary;
publicintSalary
{
get{returnm_salary;}
set{m_salary=value;}
}
publicEmployee()
{
}
publicEmployee(intid,stringname,stringemail,intsalary)
{
m_id=id;
m_name=name;
m_email=email;
m_salary=salary;
}
}
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
可以看到,该Employee类共包含4个公有属性:Id、Name、Email和Salary,分别表示员工的编号、姓名、电子邮件地址以及工资。除了这4个公有属性之外,Employee类还提供了两个构造函数。但并没有定义任何方法(即所谓的“退化类”。“退化类”与结构体类似,用来表示结构化的数据)。本小节以及随后的若干小节都将使用该Employee类,为加深印象,我们在这里给出了Employee类的类图,如图3-16所示。
注意:若想让ASP.NETAJAX异步通讯层为服务器端类型自动生成相应的客户端版本,那么该类型一定要提供一个无参数的构造函数,且该类所有的公有属性都应该提供getter和setter访问器。另外,在根据服务器端类型自动生成的客户端版本类型中,只有原服务器端类型的公有属性会被保留下来,原类型的方法和私有字段都将不会映射到客户端对象中(公有字段会被映射到客户端对象中)。
图3-16Employee类的类图
了解了Employee类之后,让我们通过一个示例程序介绍如何在ASP.NETAJAX异步通讯层传递该Employee类型。首先是服务器端的WebService代码,其中定义了两个方法:CreateNewEmployee()和SaveEmployee(),分别将返回或接受一个Employee对象:
[code][WebService(Namespace="http://tempuri.org/")]
[WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)]
[GenerateScriptType(typeof(Employee))]
[ScriptService]
publicclassPeopleManagementService:System.Web.Services.WebService
{
[WebMethod]
publicEmployeeCreateNewEmployee()
{
returnnewEmployee(0,string.Empty,string.Empty,0);
}
[WebMethod]
publicboolSaveEmployee(Employeeem)
{
//保存到数据库中...
returntrue;
}
}
[/code]
CreateNewEmployee()和SaveEmployee()方法都不能完成什么具体的工作,仅仅起到演示功能。PeopleManagementService所应用的[GenerateScriptType(typeof(Employee))]属性(上述代码中的粗体部分)才是完成程序功能的关键之处,该属性定义于System.Web.Script.Services命名空间中。若我们希望让ASP.NETAJAX异步通讯层为某一复杂类型生成客户端的JavaScript对应类型,以便完成服务器和客户端之间的通讯,则应该用GenerateScriptType属性显式声明。
注意:实际上,对于“最外层”(即在WebService方法的参数或返回值出现过)的复杂类型,[GenerateScriptType(typeof([TypeName]))]属性并不是强制要求的。但若是最外层复杂类型中还嵌套有内层的复杂类型,比如Employee中有个属性指向的是另一个复杂类型——Manager(这个Manager类型即为嵌套复杂类型),那么我们就一定要显式为WebService添加[GenerateScriptType(typeof([Manager]))]属性。或者某WebService方法的参数或返回值为List<Employee>(将在稍后介绍),那么我们也要为WebService添加[GenerateScriptType(typeof([Employee]))]属性。
正是因为存在着这样的复杂性,所以本书建议对于任何出现过的复杂类型,均在WebService类中用[GenerateScriptType(typeof([TypeName]))]属性标出,以减少由不小心导致的遗漏所带来异常的可能性。另外,[GenerateScriptType(typeof([TypeName]))]属性也可以添加到WebService方法上,效果与添加到WebService类上相同。
然后在ASP.NET页面中添加ScriptManager控件以及相应的WebService的引用:
<asp:ScriptManagerID="sm"runat="server">
[code]<Services>
<asp:ServiceReferencePath="Services/PeopleManagementService.asmx"/>
</Services>
</asp:ScriptManager>
[/code]
页面的UI部分代码非常直观:
<labelfor="tbId">Id</label><inputid="tbId"type="text"/><br/>
[code]<labelfor="tbName">Name</label><inputid="tbName"type="text"/><br/>
<labelfor="tbEmail">Email</label><inputid="tbEmail"type="text"/><br/>
<labelfor="tbSalary">Salary</label><inputid="tbSalary"type="text"/><br/>
<inputid="btnNew"type="button"value="CreateNew"
onclick="returnbtnNew_onclick()"/>
<inputid="btnSave"type="button"value="Save"
onclick="returnbtnSave_onclick()"/>
[/code]
前面四个<input/>分别将用来显示某个Employee对象的Id、Name、Email以及Salary属性,并提供了对这些属性的编辑功能。后面两个<input/>作为按钮,将分别调用前面WebService中定义的CreateNewEmployee()和SaveEmployee()两个方法的客户端代理。
btnNew_onclick()事件处理函数以及相应的回调函数如下:
functionbtnNew_onclick(){
[code]PeopleManagementService.CreateNewEmployee(onCreated);
}
functiononCreated(result){
employeeInEditing=result;
$get("tbId").value=employeeInEditing.Id;
$get("tbName").value=employeeInEditing.Name;
$get("tbEmail").value=employeeInEditing.Email;
$get("tbSalary").value=employeeInEditing.Salary;
}
[/code]
注意回调函数中的result对象即ASP.NETAJAX异步通讯层为C#Employee类自动生成的客户端类型。图3-17显示了在VisualStudio调试器中察看到的result对象(类型为Employee)的结构。
图3-17客户端Employee类型的结构
提示:在实际开发中,我们也可以直接使用客户端Employee类的构造函数,在客户端直接构造出一个Employee对象,而不必总是从服务器端取得程序中需要的新对象,以便提高应用程序的整体性能:
varem=newEmployee();
上面代码中的employeeInEditing为全局变量,用来保存当前正被编辑的客户端Employee对象,定义在这两个函数之外:
varemployeeInEditing=null;
回调函数将服务器端新创建的Employee对象的4个属性值分别显示在页面中的4个文本框中,供用户编辑。如图3-18所示。
图3-18点击“CreateNew”按钮之后,程序将从服务器端取得一个新的Employee对象
编辑好这4个条目之后,点击“Save”按钮,将触发btnSave_onclick()事件处理函数。btnSave_onclick()事件处理函数以及相应的回调函数如下:
functionbtnSave_onclick(){
[code]employeeInEditing.Id=$get("tbId").value;
employeeInEditing.Name=$get("tbName").value;
employeeInEditing.Email=$get("tbEmail").value;
employeeInEditing.Salary=$get("tbSalary").value;
PeopleManagementService.SaveEmployee(employeeInEditing,onSaved);
}
functiononSaved(result){
if(result){
alert("EmployeeSaved!");
}
}
[/code]
其中只是简单地更新了客户端Employee对象的4个属性,然后通过ASP.NETAJAX异步通讯层将其传递到服务器端。图3-19显示了服务器端SaveEmployee()方法接收到的从客户端传入的Employee对象。
图3-19SaveEmployee()方法接收到的从客户端传入的Employee对象
在服务器端SaveEmployee()方法成功返回后,客户端onSaved()回调函数将在页面中弹出提示对话框。如图3-20所示。
图3-20点击“Save”按钮保存该Employee对象之后的程序界面
有些时候我们并不希望将某个服务器端类型中所有的属性均暴露给客户端对象。比如Employee类中的Salary属性,出于某些安全或隐私方面的考虑,我们或许想让异步通讯层在自动生成客户端对象(即将其序列化为JSON字符串)时忽略该属性,即只在客户端对象中生成Id、Name和Email三个属性。
ASP.NETAJAX异步通讯层能够很好地支持这个需求,我们所要做的只是为Employee类中的Salary属性添加[System.Web.Script.Serialization.ScriptIgnore]属性,修改后的Salary属性如下,注意其中粗体部分:
privateintm_salary;
[code][System.Web.Script.Serialization.ScriptIgnore]
publicintSalary
{
get{returnm_salary;}
set{m_salary=value;}
}
[/code]
这时,ASP.NETAJAX异步通讯层为Employee类生成的客户端JavaScript类型中就不再包含该Salary属性了。图3-21显示了在VisualStudio调试器中看到的客户端Employee类型的结构。
图3-21忽略Salary属性后客户端Employee类型的结构
总结:想要让ASP.NETAJAX异步通讯层为服务器端复杂类型自动生成相应的客户端JavaScript类型,并在调用过程中传递并接收该复杂类型,我们需要:
为WebService类或WebService中需要暴露给客户端的方法添加[ScriptService]属性;
为WebService中需要暴露给客户端的方法添加[WebMethod]属性;
WebService类中的某个方法的某个参数或返回值为该复杂类型;
为WebService类添加若干个[GenerateScriptType(typeof([TypeName]))]属性,[TypeName]表示该复杂类型或其嵌套的复杂类型的名称;
该复杂类型必须要有一个无参数的构造函数;
该复杂类型的所有公有属性应该提供getter和setter访问器(即需要可读可写),除非如下几种情况:
该属性应用了[System.Web.Script.Serialization.ScriptIgnore]属性,即让ASP.NETAJAX异步通讯层在生成客户端JavaScript类型时忽略该属性,那么其属性可以没有setter或getter。
该服务器端对象只是用来单向输出JSON字符串,那么其属性可以没有setter。
在客户端传入的时候不会设置该属性的值,那么该属性可以没有setter。
在页面中的ScriptManager控件中添加对该WebService的引用。
然后,ASP.NETAJAX异步通讯层在为某服务器端复杂类型生成客户端JavaScript类型时,将:
会把没有应用[System.Web.Script.Serialization.ScriptIgnore]的公有属性(property)或公有字段(field)映射到客户端JavaScript类型中;
不会把该复杂类型的私有字段映射到客户端JavaScript类型中;
不会把该复杂类型的方法(method)映射到客户端JavaScript类型中;
我们也可以使用如下语法在客户端直接创建该复杂类型:
varmyObj=new[NameSpace].[ClassName]();
相关文章推荐
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(6):服务器端和客户端数据类型的自动转换:复杂类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(6):服务器端和客户端数据类型的自动转换:复杂类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法:服务器端和客户端数据类型的自动转换:复杂类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(9):服务器端和客户端数据类型的自动转换:DataTable和DataSet
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(8):服务器端和客户端数据类型的自动转换:数组类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(5):服务器端和客户端数据类型的自动转换:基本类型和枚举类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(7):服务器端和客户端数据类型的自动转换:泛型集合类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(10):服务器端和客户端数据类型的自动转换:以XML方式序列化数据、小结
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(7):服务器端和客户端数据类型的自动转换:泛型集合类型
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(9):服务器端和客户端数据类型的自动转换:DataTable和DataSet
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(9):服务器端和客户端数据类型的自动转换:DataTable和DataSet
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(10):服务器端和客户端数据类型的自动转换:以XML方式序列化数据、小结
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(4):异步通讯层生成的客户端代理类、使用HTTP GET进行调用
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(4):异步通讯层生成的客户端代理类、使用HTTP GET进行调用
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(1):调用Web Service、调用页面中的类方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(2):处理异步调用中的异常
- 在asp.net AJAX客户端使用复杂数据类型方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(1):调用Web Service、调用页面中的类方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(1):调用Web Service、调用页面中的类方法
- 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(3):保持用户上下文