第二节:ExtJS调用WCF系列-----分页排序列表实现
打开第一节中的那个项目,新建一个Paging.aspx的页面来实现分页列表。
这次我们使用一个测试的数据库CompanyInfoDB,里面有两张表,部门和员工,并外键关联,数据库调用采用Linq的Sqlmetal 命令方式,在Visual Studio 2008的命令提示符中输入以下命令:D:/Program Files/Microsoft Visual Studio 9.0/VC>sqlmetal /conn:server=172.16.1.52;database=CompanyInfoDB;uid=sa;pwd=sa123456 /map:c:/LinqTemp/CompanyInfoDB.map
/code:c:/LinqTemp/CompanyInfoDB.cs /serialization:Unidirectional
然后把生成的CompayInfo.map 文件和CompanyInfo.cs文件加入到项目中,并添加System.Data.Linq的引用,还要修改一下Web.Config 加入数据库链接字符串和XmlMappingSource文件的位置。
<connectionStrings>
<add name="CompanyInfoDBConnectionString" connectionString="Data Source=172.16.1.52;Initial Catalog=CompanyInfoDB;Persist Security Info=True;User ID=sa;Password=sa123456" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="CompanyInfoDBXmlMappingSource" value="E:/ExtJS/ExtJS调用WCF系列博客源文件/ExtJSAndWCFChapter1/ ExtJSAndWCFChapter1/DataBase/CompanyInfoDB.map"/>
</appSettings>
如图:
为了层次更清晰一点,我们新建一个EmployeeBL.cs的类文件来处理Employee的业务逻辑,EmpployeeBL.cs的文件代码如下:
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Linq;
usingSystem.Web;
usingSystem.Web.Security;
usingSystem.Web.UI;
usingSystem.Web.UI.HtmlControls;
usingSystem.Web.UI.WebControls;
usingSystem.Web.UI.WebControls.WebParts;
usingSystem.Xml.Linq;
usingSystem.Data.Linq.Mapping;
usingSystem.IO;
usingSystem.Linq.Dynamic;
usingSystem.Runtime.Serialization;
namespaceExtJSAndWCFChapter1
{
publicclassEmployeeBL
{
CompanyInfoDBctx;
//构造函数
publicEmployeeBL()
{
XmlMappingSourcexms=XmlMappingSource.FromXml(File.ReadAllText(ConfigurationManager.AppSettings["CompanyInfoDBXmlMappingSource"]));
ctx=newCompanyInfoDB(ConfigurationManager.ConnectionStrings["CompanyInfoDBConnectionString"].ConnectionString,xms);
//ctx.Log=Console.Out;
}
publicstringGetEmployeePaging(intstart,intlimit,stringsort,stringdir)
{
stringstrJsonSource="";
varquery=fromempinctx.Employee
selectnew
{
EmployeeID=emp.EmployeeID,
CnName=emp.CnName,
Sex=emp.Sex,
Age=emp.Age,
Email=emp.Email,
OnWorkDate=emp.OnWorkDate,
DeptName=emp.Department.CnName
};
query=query.OrderBy(sort+""+dir);
intTotalCount=query.Count();//共有记录数
intPageNum=start/limit;//共有页数
intPageSize=limit;//每页记录数
query=query.Skip(PageSize*PageNum).Take(PageSize);
stringJsonSource=query.ToJSON();//当前页记录转成JSON格式
strJsonSource=@"{""TotalCount"":"""+TotalCount+"";
strJsonSource=strJsonSource+@""",""EmployeeList"":"+JsonSource+"}";
returnstrJsonSource;
}
}
}
这里需要两个类文件:Dynamic.cs 和 JSONHelper.cs 前者是微软提供的Linq动态查询文件,后者是scottgu的JSON序列化文件。
并在EmployeeService.svc.cs 文件中加入获取员工的分页排序方法,代码如下
///Employee分页排序
///</summary>
///<paramname="start"></param>
///<paramname="limit"></param>
///<paramname="sort"></param>
///<paramname="dir"></param>
///<returns></returns>
[OperationContract]
[WebInvoke(BodyStyle=WebMessageBodyStyle.Wrapped,RequestFormat=WebMessageFormat.Json,ResponseFormat=WebMessageFormat.Json,UriTemplate="/GetEmployeePaging")]
publicstringGetEmployeePaging(intstart,intlimit,stringsort,stringdir)
{
if(start<0||sort=="")thrownewArgumentException("参数错误!");
EmployeeBLbl=newEmployeeBL();
returnbl.GetEmployeePaging(start,limit,sort,dir);
}
接下来我们编写客户端代码,这次我们新建一个paging.js的文件来存放Paging.Aspx的脚本文件,ExtJS调用WCF的时候有两点须注意:
第一点:关于前一节中
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/Get")]中的BodyStyle = WebMessageBodyStyle.Bare 和
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/GetAll")]中的BodyStyle = WebMessageBodyStyle.Wrapped的区别,看前一节的两个例子,我们不难发现两个返回值的不同之处:
Get:
{"Age":28,"Birthday":"//Date(286732800000+0800)//","CnName":"Xiaozhuang","Email":"[email protected]","EmployeeID":"b34f963e-8520-44da-be00-bd0a1aeadc78","Sex":true}
GetAll:
{"GetAllResult":[{"Age":28,"Birthday":"//Date(286732800000+0800)//","CnName":"CnName","Email":"[email protected]","EmployeeID":"2835ba7e-5f0c-41ff-8746-d6e959800850","Sex":true},{"Age":28,"Birthday":"//Date(286732800000+0800)//","CnName":"CnName1","Email":"[email protected]","EmployeeID":"d5b7a088-13a8-4195-8ce9-e0836cd33de4","Sex":false}]}
可以看到GetAll 的返回值给加了一个GetAllResult的根对象,但是这个根对象在ExtJS调用的时候是不需要的,必须去掉这个根对象才行,那么我们为什么不用 WebMessageBodyStyle.Bare非要用BodyStyle = WebMessageBodyStyle.Wrapped?那是因为前者只有在WCF方法只有一个输入参数,而且不需要对其进行序列化的时候才可以使用, 但是很多方法要求都不止一个参数,所以必须用后者。那我们怎样才能去掉这个根对象呢?在http://erichauser.net/?p=35有个去掉 这个根对象的方法,不过他提供的那个文件有问题,不能达到预期的效果,经过调试,我把文件WCFJsonReader.js进行了改进,代码如下:
*@classExt.data.WCFJsonReader
*@extendsExt.data.JsonReader
*
*CustomreaderforreadingdatafromWCF.IfyouareusingWebMessageBodyStyle.Wrapped,thenWCFaddsarootobjecttoyour
*JSONresult:
*
*{"MethodResult":{"Count":0,"Objects":[…]}}
*
*Extdoesnotexpecttheresulttohavearootobjectbeforeparsing,sothisclasswillremoveit.
*/
Ext.data.WCFJsonReader=Ext.extend(Ext.data.JsonReader,{
/**//*@cfg{Boolean}SetswhetherornottheOperationContracthastheisusingWebMessageBodyStyle.Wrapped*/
wrapped:true,
/**//**
*Ifwrappedissettotrue,wewillstripWCF’swrapobject
*/
read:function(response){
varjson=response.responseText;
varo=eval("("+json+")");
if(!o){
throw{message:"JsonReader.read:Jsonobjectnotfound"};
}
if(this.wrapped){
for(varpropino){
if(typeof(prop)=='string'&&prop.substr(prop.length-6,prop.length)=='Result'){
o=this.convert(o[prop]);
//o=o[prop];
break;
}
}
}
if(o.metaData){
deletethis.ef;
this.meta=o.metaData;
this.recordType=Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta,this.recordType,o);
}
returnExt.data.WCFJsonReader.superclass.readRecords.call(this,o);
},
//private
convert:function(o){
o=eval("("+o+")");
varnewResult=newObject();
for(varrootPropino){
newResult[rootProp]=o[rootProp];
}
returnnewResult;
}
});
在调用WCF的时候,用这个WCFJsonReader.js代替原始的JsonReader就可以了。
第二点:我们在调用WCF的时候要指明Content-type是application/json,而且参数必须要进行JSON序列化才可以,但是在 ExtJS提供的分页排序例子中输入参数是直接由GridPanel提供的,所以我们必须截获这些参数,并对它进行JSON序列化,由此我建立了一个 WCFHttpProxy.js的文件,这个文件继承自Ext.data.HttpProxy并重载了它的load方法,在这个方法里截获输入参数,并对 它们进行JSON序列化。代码如下:
*ExtJSLibrary2.0RC1
*Copyright(c)2006-2007,ExtJS,LLC.
*[email protected]
*
*http://extjs.com/license
*/
/**//**
*Authorbyxiaozhuang
*/
Ext.data.WCFHttpProxy=Ext.extend(Ext.data.HttpProxy,{
load:function(params,reader,callback,scope,arg){
if(this.fireEvent("beforeload",this,params)!==false){
Ext.lib.Ajax.defaultPostHeader='application/json';
params=Ext.util.JSON.encode(params);
varo={
params:params||{},
request:{
callback:callback,
scope:scope,
arg:arg
},
reader:reader,
callback:this.loadResponse,
scope:this
};
if(this.useAjax){
Ext.applyIf(o,this.conn);
if(this.activeRequest){
Ext.Ajax.abort(this.activeRequest);
}
this.activeRequest=Ext.Ajax.request(o);
}else{
this.conn.request(o);
}
}else{
callback.call(scope||this,null,arg,false);
}
}
});
其实就是增加了Ext.lib.Ajax.defaultPostHeader = 'application/json';params = Ext.util.JSON.encode(params);两行。
有了这两个文件,我们要改进一下我们的EXTJS,在它的目录下建立WCF的文件夹,并把这两个文件拷贝进去。
现在我们可以编写客户端代码了,Paging.aspx的代码如下:
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
<title>UntitledPage</title>
<linkrel="stylesheet"type="text/css"href="ExtJS/resources/css/ext-all.css"/>
<scripttype="text/javascript"src="ExtJS/adapter/ext/ext-base.js"></script>
<scripttype="text/javascript"src="ExtJS/ext-all.js"></script>
<!--ExtJS调用WCF的专用文件-->
<scripttype="text/javascript"src="ExtJS/WCF/WCFHttpProxy.js"></script>
<scripttype="text/javascript"src="ExtJS/WCF/WCFJsonReader.js"></script>
<!--简体中文语言包-->
<scripttype="text/javascript"src="ExtJS/source/locale/ext-lang-zh_CN.js"></script>
<!--分页排序ExtJS代码-->
<scripttype="text/javascript"src="paging.js"></script>
<!--CommonStylesfortheexamples-->
<styletype="text/css">
body.x-panel{}{
margin-bottom:20px;
}
.icon-grid{}{
background-image:url(ExtJS/icons/fam/grid.png)!important;
}
#button-grid.x-panel-body{}{
border:1pxsolid#99bbe8;
border-top:0none;
}
.add{}{
background-image:url(ExtJS/icons/fam/add.gif)!important;
}
.option{}{
background-image:url(ExtJS/icons/fam/plugin.gif)!important;
}
.remove{}{
background-image:url(ExtJS/icons/fam/delete.gif)!important;
}
.save{}{
background-image:url(ExtJS/icons/save.gif)!important;
}
</style>
</head>
<body>
<formid="form1"runat="server">
<h1>PagingGridExample</h1><br/><br/>
<divid="topic-grid"></div>
</form>
</body>
</html>
Paging.js的代码如下:
*AuthorbyXiaozhuang
*
*
*/
Ext.onReady(function(){
//createtheDataStore
varstore=newExt.data.Store({
//loadusingscripttagsforcrossdomain,ifthedatainonthesamedomainas
//thispage,anHttpProxywouldbebetter
proxy:newExt.data.WCFHttpProxy({
url:'/EmployeeService.svc/GetEmployeePaging'
}),
//createreaderthatreadstheTopicrecords
reader:newExt.data.WCFJsonReader({
root:'EmployeeList',
totalProperty:'TotalCount',
id:'EmployeeID',
fields:[
{name:'EmployeeID',type:'int'},
{name:'CnName',type:'string'},
{name:'Sex',type:'string'},
{name:'Age',type:'int'},
{name:'Email',type:'string'},
{name:'OnWorkDate',type:'string'},
{name:'DeptName',type:'string'}
]
}),
//turnonremotesorting
remoteSort:true
});
store.setDefaultSort('EmployeeID','ASC');
//把true和false转化为男或者女,这个其实可以在服务器端进行转化,写在这里只是为了测试
functionrenderSex(value,p,record){
returnrecord.data.Sex=="true"?"男":"女";
}
//这个函数演示了怎样把服务器端的DateTime类型转为Javascript的日期
functionrenderOnWorkDate(value,p,record){
varjsondate=record.data.OnWorkDate;
returneval("new"+jsondate.substr(1,jsondate.length-2)).toLocaleDateString();
}
//thecolumnmodelhasinformationaboutgridcolumns
//dataIndexmapsthecolumntothespecificdatafieldin
//thedatastore
varnm=newExt.grid.RowNumberer();
varsm=newExt.grid.CheckboxSelectionModel();//addcheckboxcolumn
varcm=newExt.grid.ColumnModel([nm,sm,{
header:"员工ID",
dataIndex:'EmployeeID',
width:100
//renderer:renderTopic
},{
header:"姓名",
dataIndex:'CnName',
width:200
},{
header:"性别",
dataIndex:'Sex',
width:70,
renderer:renderSex
},{
header:"年龄",
dataIndex:'Age',
width:70
},{
header:"Email",
dataIndex:'Email',
width:150
},{
header:"入职时间",
dataIndex:'OnWorkDate',
width:150,
renderer:renderOnWorkDate
},{
header:"部门",
dataIndex:'DeptName',
width:200
}]);
//bydefaultcolumnsaresortable
cm.defaultSortable=true;
vargrid=newExt.grid.GridPanel({
//el:'topic-grid',
renderTo:document.body,
width:800,
height:500,
title:'分页和排序列表',
store:store,
cm:cm,
trackMouseOver:false,
sm:sm,
loadMask:true,
viewConfig:{
forceFit:true,
enableRowBody:true,
showPreview:true,
getRowClass:function(record,rowIndex,p,store){
return'x-grid3-row-collapsed';
}
},
//inlinetoolbars
tbar:[{
text:'添加',
tooltip:'添加一条记录',
iconCls:'add'
},'-',{
text:'修改',
tooltip:'修改',
iconCls:'option'
},'-',{
text:'删除',
tooltip:'删除记录',
iconCls:'remove',
handler:handleDelete
}],
bbar:newExt.PagingToolbar({
pageSize:25,
store:store,
displayInfo:true
})
});
//renderit
grid.render();
//triggerthedatastoreload
varrequest={start:0,limit:25};
store.load({params:request});
functionhandleDelete(){
varselectedKeys=grid.selModel.selections.keys;//returnsarrayofselectedrowsidsonly
if(selectedKeys.length>0)
{
Ext.MessageBox.confirm('提示','您确实要删除选定的记录吗?',deleteRecord);
}
else
{
Ext.MessageBox.alert('提示','请至少选择一条记录!');
}//end
}
functiondeleteRecord(btn){
if(btn=='yes'){
store.reload();
}//endifclick'yes'onbutton
}//enddeleteRecord
});
源代码下载在这里