请跟我来--使用Ext+llano快速搞定CRUD

    上篇文章我们感觉到用Ext组件做界面是很强大和灵活的,同时我们也发现了很多地方我们在硬编码,比如列表的列头设置,还有编辑框各字段等,在实际项目中一定让人十分疲惫!有时可能因为不注意少打一个分号而跟踪半天等。所以接下下我想介绍一下使用元数据定义,用Ext自带的灵活方法动态生成合适的列表和编辑框的做法,这在以前的C/S架构下一直是程序员追求的,在如今Ext利用js的动态灵活特性成为可能了。
    先看看我重构后的代码是这样:
Ext.onReady(
  
function(){
    Ext.BLANK_IMAGE_URL
="http://www.cnblogs.com/resources/default/s.gif";
    Ext.QuickTips.init();
    
var member_meta_def ='id',
    
{fieldLabel:'姓名',name: 'Name',type: 'string',allowBlank: false,minLength:3,maxLength:8,anchor:'90%',width:50},
    
{fieldLabel:'邮件',name: 'Email',type: 'string',anchor:'90%',width:80}
    
{fieldLabel:'手机',name: 'Mobile',type: 'string',anchor:'90%',width:80}
    
{fieldLabel:'毕业年份',name: 'GradYear',type: 'int',anchor:'90%',width:20},  
    
{fieldLabel:'登记日期',name: 'sCreatedDate', type: 'date',dateFormat: 'Y-m-d H:i:s', anchor:'60%',width:30}
  ];
  
 
var member_meta_rec= Ext.data.Record.create(member_meta_def);
var new_member=new member_meta_rec({
  Name: 
'hh',Email: 'xhhy',Mobile: '136',GradYear:2008,
  sCreatedDate: 
new Date()}
); 
  
    CRUD(
{entityName:'member',showEditBox:true,
      list_id:
'list1',edit_id:'edit1',list_title:'list',edit_title:'edit',list_width:800,list_height:400,editt_width:300,
      confirmShowColumName:
'Name',
      meta:member_meta_def,facade:
'facade.aspx',sortFld:'Name',sortDir:'ASC',
      new_record:new_member}
);
  }

);


   可以实际从数据库模糊查询符合条件的记录,比上个例子增加了分页处理!


    还可以直接在列表中直接修改内容,更像传统C/S软件了。


    关键说明如下,让我们先从简单的后台说起:
1)实体对象
    前台后后台交换的数据定义,按照llano的要求,数据表以T开头,视图以V开头,代码大致如下:

namespace demo.data
{
    using Castle.ActiveRecord;
    using llano.data;
    using Nullables;
    [ActiveRecord(
"app_Members")]
    public class TMember : Entity
    
{

        private string _Name;
        private string _Mobile;
        private string _Email;
        private NullableInt32 _GradYear;
        private 
int _Score;


        public TMember()
            : base()
        
{
        }



        public TMember(string username, string mb)
            : 
this()
        
{
            
this._Name = username;
            
this._Mobile = mb;
        }




        [Property(Length 
= 10)]
        public string Name
        
{
            get 
return _Name; }
            set 
{ _Name = value; }
        }

  
  }

}

  使用llano的好处是她天生就支持Ext,当你直接打印一个Entity的实例时,实际就会输出它的JSON字符串,当你要输出一个数组时,一句话就能搞定,看看下面的示例就明白了:

  String sql = "Mobile like :Mobile";
            Dictionary
<String, object> args = new Dictionary<stringobject>();
            args.Add(
"Mobile""%9%");
            
int total = dao.GetTotalCnt(sql, args);
            TMember[] ps 
= dao.Load(sql, args, 02);
            Console.WriteLine(JsonHelper
<TMember>.SerializeArray(total, ps));

2)后台服务对象
    在上一个例子中,facade.aspx已经把思路说得很清楚了。在实际项目中通常有上百张表,所以我们就把那段方法判断得代码提到了基类中,而继承的服务类就重写查询处理部分代码即可:

 public class MemberMgr : BaseService<TMember, TMember>, IMemberMgr {
        ILog logger 
= LogManager.GetLogger(typeof(MemberMgr));


        
public override Dictionary<String, object> MakeSearchWhere(NameValueCollection args)
        
{
            
string keyword = "";
            Dictionary
<String, object> rt = new Dictionary<stringobject>();
            
try
            
{
                keyword 
= args["query"].ToString();
                
if (keyword.IndexOf("="> 0)
                    SearchByFileds(keyword, rt);
                
else
                    SearchByKeyWords(keyword, rt);

            }

            
catch
            
{
                SearchByKeyWords(
"", rt);
            }

            
//logger.Info(rt["sql"]);
            return rt;
        }


  
public void SearchByFileds(string flds, Dictionary<String, object> map) {
            Dictionary
<String, object> args = new Dictionary<stringobject>();
            String sql 
= "sInvalid=0";
            Dictionary
<String, string> fs = GetQueryArgs(flds);
            
foreach (string pname in fs.Keys) {
                sql 
+= " and ";
                
if ("GradYear".Equals(pname)) {
                    sql 
+= "GradYear = :GradYear";
                    args.Add(
"GradYear"int.Parse(fs["GradYear"]));
                }

                
else if ("Name".Equals(pname)) {
                    sql 
+= "Name like = :Name";
                    args.Add(
"Name""%" + fs["Name"+ "%");
                }

                
else {
                    sql 
+= pname + " = :" + pname;
                    args.Add(pname, fs[pname]);
                }


            }

            map.Clear();
            map.Add(
"sql", sql);
            map.Add(
"args", args);

        }

}

    单元测试代码就很容易懂了:

[Test]
        
public void TC01_Query() {
            IMemberMgr service 
= (IMemberMgr)container["serviceMember"];
            NameValueCollection ns 
= new NameValueCollection();

            ns.Clear();
            ns.Add(
"s_method""list");
            ns.Add(
"query""GradYear=2007");
            Console.WriteLine(service.Execute(ns));


            ns.Clear();
            ns.Add(
"s_method""list");
            ns.Add(
"query""679");
            Console.WriteLine(service.Execute(ns));

            ns.Clear();
            ns.Add(
"s_method""list");
            ns.Add(
"start""0");
            ns.Add(
"limit""3");
            Console.WriteLine(service.Execute(ns));
        }


        [Test]
        
public void TC02_Execute() {
            IMemberMgr service 
= (IMemberMgr)container["serviceMember"];
            NameValueCollection ns 
= new NameValueCollection();
            
string data = "{\"Name\":\"demo\",\"Mobile\":\"13922666720\",\"Email\":\"lsj@21cn.com\",\"GradYear\":2007,\"Score\":0}";
            ns.Clear();
            ns.Add(
"s_method""save");
            ns.Add(
"id""");
            ns.Add(
"data", data);
            Console.WriteLine(service.Execute(ns));


            ns.Clear();
            ns.Add(
"s_method""delete");
            ns.Add(
"id", service.GetEntityDao().Load("Name='demo'"null)[0].id);
            Console.WriteLine(service.Execute(ns));

        }

    结果大致如下:
......
{"totalCount":"50","data":[{"Name":"Name40","Mobile":"13922666740","GradYear":2000,"Score":0,"id":"0171abecb19a472ea2aec903bf18329d","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false},{"Name":"Name3","Mobile":"1392266673","GradYear":2003,"Score":0,"id":"029eb0082beb4285a69ec731d662b99d","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false},{"Name":"Name6","Mobile":"1392266676","GradYear":2006,"Score":0,"id":"0bd959917c6e436b9d77a35de6858dd2","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false}]}

{"success":"True","data":{"Name":"demo","Mobile":"13922666720","Email":"lsj@21cn.com","GradYear":2007,"Score":0}}
{"success":"True","data":"0eaad2dea35244cb8a16657fb147cb98"}
    可以看到,这正式Ext要的数据格式。

3)前端重用函数
   下面这些例子仅仅用于说明原理,实际项目需要改造才能让客户满意
首先是生成编辑板:

function createXFileldCfg(entityName,meta){
  
var rt=meta;
  Ext.apply(rt,
{id:entityName+'_'+meta.name});
  
if(true==meta.ex_IsSelect){
    Ext.apply(rt,
{
      xtype:
'combo',hiddenName:meta.name, triggerAction: 'all', editable: false,
      store: 
new Ext.data.SimpleStore({
        fields: [
'retrunValue''displayText'],
        data: meta.ex_Data}
),
      valueField :
"retrunValue",displayField: "displayText",mode: 'local',
      forceSelection: 
true, blankText:'请选择',emptyText:'请选择'
    }
);
  }

  
else if(meta.type==="string"){
    
if(meta.maxLength>50)
      Ext.apply(rt,
{xtype:'textarea'});
    
if(meta.ex_IsPassword===true)
      Ext.apply(rt,
{inputType:'password'});
  }

  
else if(meta.type==="date")
    Ext.apply(rt,
{xtype:'datefield', format:meta.dateFormat });
  
else if(meta.type==="int"||meta.type==="float")
    Ext.apply(rt,
{xtype:'numberfield'});
  
else if(meta.type==="bool")
    Ext.apply(rt,
{xtype:'checkbox'});
  
return rt;
}

function createFormFields(entityName,metas){
  
var rt=[];
  
for(var p in metas){
    
if(typeof metas[p] == "string"||metas[p]['fieldLabel']==null||metas[p]['ex_IsViewField']===truecontinue;
    
var def=createXFileldCfg(entityName,metas[p]);
    rt.push(def);
  }

  
return rt;
}

生成列表函数也类似:

function createXColumnCfg(meta){
  
var rt={
    header: meta.fieldLabel,dataIndex:meta.name,
    width:meta.width,
    editor: 
new Ext.form.TextField({allowBlank: meta.allowBlank})
  }
;
  
if(true==meta.ex_IsSelect){
    Ext.apply(rt,
{editor: new Ext.form.ComboBox({
      xtype:
'combo',hiddenName:meta.name, triggerAction: 'all', editable: false,
      store: 
new Ext.data.SimpleStore({
        fields: [
'retrunValue''displayText'],
        data: meta.ex_Data}
),
      valueField :
"retrunValue",displayField: "displayText",mode: 'local',
      forceSelection: 
true, blankText:'请选择',emptyText:'请选择'}
)
    }
);
  }

  
else if(meta.type==="date")
    rt
= Ext.apply(rt,{
      renderer: formatDate,format:meta.dateFormat,
      editor: 
new Ext.form.DateField({
        disabledDays: meta.disabledDays,
        disabledDaysText: meta.disabledDaysText
      }
)
    }
);
  
else if(meta.type==="int"||meta.type==="float")
    rt
= Ext.apply(rt,{
      minValue:meta.minValue,maxValue:meta.maxValue,
      editor: 
new Ext.form.NumberField()}
);
  
else if(meta.type==="bool")
    rt
= Ext.apply(rt,{renderer: formatBoolean,  editor: new Ext.form.Checkbox({boxLabel :''})});
  
return rt;
}

function createColumns(metas){
  
var rt=[];
  
for(var p in metas){
    
if(typeof metas[p] == "string"||metas[p]['fieldLabel']==null||metas[p]['width']===0continue;
    
var def=createXColumnCfg(metas[p]);
    rt.push(def);
  }

  
return rt;
}

其他代码请看例子,看scripts/Global.js即可!

运行说明:
1) llano集成了castle,所以你如果没有安装则先去下载:

  • Installer version: CastleProject-1.0-RC3.msi
  • 下载测试驱动并安装

    2)在showme.Model--test.cs修改数据库连接字符串并运行测试案例创建数据表和示例数据
    3)如果vs2005要运行Web(vs2008可直接运行),则要求:
    Service Pack 1 can be found at http://msdn2.microsoft.com/en-us/vstudio/bb265237.aspx
    Web Project support can be found at http://msdn2.microsoft.com/en-us/asp.net/aa336618.aspx
    注意安装Service Pack 1要几个小时,请不要以为死机!

    好了,手也打累了,下周再给大家讲讲ext和llano使用的注意事项。

    今天就到这吧,祝各位周么愉快!

    alex


     




     

  • posted @ 2008-01-04 10:40  成为-行动-拥有(BeDoHave)  阅读(4258)  评论(5编辑  收藏  举报