zoukankan      html  css  js  c++  java
  • ExtAspNet应用技巧(二十四) AppBox之Grid数据库分页排序与批量删除


    界面截图

    页面加载后,Grid默认按照“时间”倒序排列:


    点击“时间”标题栏,改变排序:


    改变其它列的排序:


    如果没有选中任一项纪录时就点击“删除选中纪录”,会有客户端提示:


    删除记录前会有Confirm确认对话框,注意这里对选中的数目采用红色粗体字,等会可以关注下实现:


    查看详细信息页面:



    Grid标签定义

        <ext:Grid ID="Grid1" runat="server" AnchorValue="100% -36px" ShowBorder="true" ShowHeader="false"
            EnableCheckBoxSelect="true" EnableRowNumber="true" DataKeyNames="Logid" AllowSorting="true"
            OnSort="Grid1_Sort" AllowPaging="true" IsDatabasePaging="true" OnPageIndexChange="Grid1_PageIndexChange">
            <Toolbars>
                <ext:Toolbar ID="Toolbar1" runat="server">
                    <Items>
                        <ext:Button ID="btnDeleteSelected" runat="server" Text="删除选中记录" OnClick="btnDeleteSelected_Click">
                        </ext:Button>
                    </Items>
                </ext:Toolbar>
            </Toolbars>
            <Columns>
                <ext:BoundField DataField="DatetimeX" SortField="DatetimeX" DataFormatString="{0:yyyy-MM-dd HH:mm:ss}"
                    Width="120px" HeaderText="时间" />
                <ext:BoundField DataField="LogLevel" SortField="LogLevel" Width="50px" HeaderText="级别" />
                <ext:BoundField DataField="Logger" SortField="Logger" Width="100px" HeaderText="源" />
                <ext:BoundField DataField="Message" ExpandUnusedSpace="true" HeaderText="错误信息" />
                <ext:BoundField DataField="Exception" Width="200px" HeaderText="异常信息" />
                <ext:WindowField Text="查看" Title="查看" WindowID="Window1" DataIFrameUrlFields="Logid"
                    DataIFrameUrlFormatString="~/admin/log_view.aspx?id={0}" Width="50px" />
            </Columns>
        </ext:Grid>
        



    btnDeleteSelected的客户端脚本

    不用担心,这里不是让大家来写JavaScript,而是调用ExtAspNet定义好的方法。

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                LoadData();
            }
        }
        private void LoadData()
        {
            // 默认的排序列和排序方向
            Grid1.CurrentSortColumnIndex = 0;
            Grid1.Columns[0].SortDirection = "DESC";
            // 每页记录数
            Grid1.PageSize = XConfigHelper.PageSize;
            // 点击删除按钮时,至少选中一项
            btnDeleteSelected.OnClientClick = Grid1.GetNoSelectionAlertInParentReference("至少选中一项!");
            btnDeleteSelected.ConfirmTarget = "_parent";
            btnDeleteSelected.ConfirmText = String.Format("确定要删除 <span style=\"color:red;font-weight:bold;\"><script>{0}</script></span> 项纪录吗?", Grid1.GetSelectCountReference());
    
            BindGrid();
        }
        


    我们重点来看下为按钮添加删除之前的确认提醒,去除其中为选中数添加样式的部分,精简后的代码如下:

        btnDeleteSelected.ConfirmText = String.Format("确定要删除 <script>{0}</script> 项纪录吗?", Grid1.GetSelectCountReference());
        


    这里ExtAspNet会自动查找ConfirmText字符串中<script></script>的部分,并生成正确的JavaScript代码。

    如果你打开页面源代码的话,你会发现上面简单的两句C#代码的调用被转化为复杂的JS函数,而这一切都是ExtAspNet已经封装好的:


    当有人ExtAspNet能给我们带来什么好处时,我经常会说ExtAspNet让Web2.0开发变得简单。
    ExtAspNet尽量把一些常见的任务封装起来,呈现在程序员面前的是最佳实践的调用方法,这样就不必每个人都重新实现了。

    当然,ExtAspNet不是万能的,我们的设计初衷就是能够覆盖80%以上应用场景的快速开发。
    因此如果你觉得ExtAspNet中缺少什么特性的话,尽管发信给我,我会在评估不会增加ExtAspNet复杂度的前提下增加这些特性。



    排序,数据库分页与SubSonic

    1. 如果要使用Grid排序,必须满足如下条件:
    • Grid增加属性AllowSorting="true"和OnSort="Grid1_Sort"
    • 需要排序的列增加SortField="LogLevel"
    • 在页面初始化时设置当前排序列Grid1.CurrentSortColumnIndex = 0;以及当前排序列的方式Grid1.Columns[0].SortDirection = "DESC";
    • 在Sort事件中设置Grid1.CurrentSortColumnIndex = e.ColumnIndex;并重新绑定Grid


    2. 如果使用Grid的数据库分页,必须满足如下条件:
    • Grid增加属性AllowPaging="true", IsDatabasePaging="true"和OnPageIndexChange="Grid1_PageIndexChange"
    • Grid数据绑定之前需要设置PageSize(每页记录数),RecordCount(总记录数),PageIndex(当前第几页,默认为0)


    3. SubSonic支持数据库分页,非常方便:

    • SqlQuery q = new Select().From<XLog>();
    • q.Paged(Grid1.PageIndex + 1, Grid1.PageSize);
    • XLogCollection logs = q.ExecuteAsCollection<XLogCollection>();

    需要注意的是,SubSonic的分页是从1开始的,而ExtAspNet中Grid的PageIndex是从0开始的。


    现在看一下BindGrid的完整代码:
        private void BindGrid()
        {
            SqlQuery q = new Select().From<XLog>();
            q.Where("1").IsEqualTo("1");
            // 在错误信息中搜索
            string searchText = ttbSearchMessage.Text.Trim();
            if (!String.IsNullOrEmpty(searchText))
            {
                q.And(XLog.MessageColumn).ContainsString(searchText);
            }
            // 过滤错误级别
            if (ddlSearchLevel.SelectedValue != "ALL")
            {
                q.And(XLog.LogLevelColumn).IsEqualTo(ddlSearchLevel.SelectedValue);
            }
            // 过滤搜索范围
            if (ddlSearchRange.SelectedValue != "ALL")
            {
                DateTime today = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
                switch (ddlSearchRange.SelectedValue)
                {
                    case "TODAY":
                        q.And(XLog.DatetimeXColumn).IsGreaterThanOrEqualTo(today);
                        break;
                    case "LAST3DAYS":
                        q.And(XLog.DatetimeXColumn).IsGreaterThanOrEqualTo(today.AddDays(-3));
                        break;
                    case "LAST7DAYS":
                        q.And(XLog.DatetimeXColumn).IsGreaterThanOrEqualTo(today.AddDays(-7));
                        break;
                    case "LASTMONTH":
                        q.And(XLog.DatetimeXColumn).IsGreaterThanOrEqualTo(today.AddMonths(-1));
                        break;
                    case "LASTYEAR":
                        q.And(XLog.DatetimeXColumn).IsGreaterThanOrEqualTo(today.AddYears(-1));
                        break;
                }
            }
    
            // 在查询添加之后,排序和分页之前获取总记录数
            // Grid1总共有多少条记录
            Grid1.RecordCount = q.GetRecordCount();
    
            // 排列
            q.OrderBys.Add(GetSortExpression(Grid1, XLog.Schema));
            // 数据库分页
            q.Paged(Grid1.PageIndex + 1, Grid1.PageSize);
            XLogCollection logs = q.ExecuteAsCollection<XLogCollection>();
    
            Grid1.DataSource = logs;
            Grid1.DataBind();
        }
        


    其实这里面进行了两次数据库查询,第一次在Grid1.RecordCount = q.GetRecordCount();,第二次是查询XLogCollection logs集合。

    这段代码其实隐藏了很多应用技巧,我会一一列出:

    1. q.And(XLog.MessageColumn).ContainsString(searchText);

    这个是用来在Message字段中查找包含searchText文本的记录,如果使用SqlServer数据库,这段代码等价于:
        q.And(XLog.MessageColumn).Like(String.Format("%{0}%", searchText));
        

    从这一点也可以看到SubSonic考虑的很周全,其实ContainsString这个函数是兼容多种数据库的。

    2. q.Where("1").IsEqualTo("1");

    可能有些网友就觉得奇怪了,为什么要加上这段奇怪的代码。
    这是为了防止查询中缺少Where子句,也防止查询中有多个Where子句而导致SubSonic解析错误。
    这也类似于我们这些写SQL语句:select * from Log where 1=1 and .....

    3. Grid1.RecordCount = q.GetRecordCount();这句话的摆放位置

    这是很有讲究的,它应该放在添加查询条件之后,排序和分页之前。这是获取总记录数的SQL语句效率才高。
    不过或者SubSonic本身做了优化,GetRecordCount时忽略排序的SQL子句也说不定。

    4. q.OrderBys.Add(GetSortExpression(Grid1, XLog.Schema));

    我们就来看看定义在PageBase中的GetSortExpression的声明:
        protected string GetSortExpression(ExtAspNet.Grid grid, TableSchema.Table schema)
        {
            ExtAspNet.GridColumn column = grid.Columns[grid.CurrentSortColumnIndex];
            string columnName = GetTableColumnName(schema, column.SortField);
            return String.Format("{0} {1}", columnName, column.SortDirection);
        }
        protected string GetTableColumnName(TableSchema.Table table, string propertyName)
        {
            foreach (TableSchema.TableColumn column in table.Columns)
            {
                if (column.PropertyName == propertyName)
                {
                    return column.ColumnName;
                }
            }
            return String.Empty;
        }
        


    可见GetSortExpression也就是根据当前排序的列CurrentSortColumnIndex来生成排序SQL子句而已。


    GetTableColumnName是做啥的?

    可能另一个函数 GetTableColumnName 更让大家迷惑,它的作用是将 column.SortField 转化为数据库列的名称。
    有网友就问了:column.SortField 不就是 数据库列的名称 吗?
    ------------不是这样的!

    我们都知道SubSonic这类的工具有一个通用的名称是ORM,也就是Object Releation Mapping(对象与关系数据库的映射)。
    SubSonic会将数据库表的列映射为C#实体类的属性,比如messgage映射为Message,user_name映射为UserName,而DateTime则被映射为DateTimeX,这是为了防止和C#的关键字冲突。

    所以你看我们在声明Grid列的时候:
        <ext:BoundField DataField="DatetimeX" SortField="DatetimeX" DataFormatString="{0:yyyy-MM-dd HH:mm:ss}"
            Width="120px" HeaderText="时间" />
        

    对比下数据库中的定义:


    而GetTableColumnName正是为了将实体类的属性(DateTimeX)转化为数据库的列名称(DateTime)。


    批量删除,翻页与排序

        protected void btnDeleteSelected_Click(object sender, EventArgs e)
        {
            // 从每个选中的行中获取Logid(在Grid1中定义的DataKeyNames)
            List<int> logids = new List<int>();
            foreach (int rowIndex in Grid1.SelectedRowIndexArray)
            {
                logids.Add(Convert.ToInt32(Grid1.DataKeys[rowIndex][0]));
            }
            // 执行数据库操作
            new Delete().From<XLog>()
                 .Where(XLog.LogidColumn).In(logids)
                 .Execute();
            // 清空当前选中的项
            Grid1.SelectedRowIndexArray = null;
            // 重新绑定表格
            BindGrid();
        }
        
        protected void Grid1_Sort(object sender, ExtAspNet.GridSortEventArgs e)
        {
            Grid1.CurrentSortColumnIndex = e.ColumnIndex;
            BindGrid();
        }
    
        protected void Grid1_PageIndexChange(object sender, ExtAspNet.GridPageEventArgs e)
        {
            Grid1.PageIndex = e.NewPageIndex;
            BindGrid();
        }
        



    至此,日志管理模块已经介绍完毕。下一章开始我们将关注用户权限管理方面的实现。


    下载全部源代码


    这可能是十一前的最后一篇日志了,同时也祝愿大家在2009年的十一长假中玩的快乐。
  • 相关阅读:
    MapiRule例子
    P/invoke in .NET Compact Framework
    MFC C++类型学习
    Windows Mobile上实现可拖动的窗口
    在Wince下使用钩子函数
    VC++动态链接库编程之MFC规则DLL
    VC++动态链接库编程之DLL典型实例
    Using keyboard hooks in WinCE
    Override VK_TTALK & VK_TEND
    Getphonenumber获得电话号码的例子
  • 原文地址:https://www.cnblogs.com/sanshi/p/1574634.html
Copyright © 2011-2022 走看看