基于对象的数据筛选与排序(二)

来源:互联网 发布:sql去除重复数据 编辑:程序博客网 时间:2024/06/04 19:35

OK,在上一篇中,我们从对象的角度将数据从传统的SQL数据库筛选模式转换了出来,这样做的好处在于如果数据量不是特别大的情况下,一次性的提取出数据缓存到缓存中,将数据的操作从数据库中脱离出来,利用对象在缓存的基础上操作,从而更高效率的处理数据。
上一节中,主要对数据的筛选做了分析与运用,当然,在大多数情况下,排序也是一个常用且重要的数据操作。
在对业务对象进行排序时,不能使用ObjectDataSource作为数据源,因为它只支持DataView、DataTable、DataSet的自动排序。当然,仍然可以对GridView进行Sorting处理,直接拼装SQL语句,利用“Order By”子句即可完成排序。
基本的数据库操作这里就不再举例,和进行数据筛选思路一样,我们同样可以将数据一次性抽到缓存中,后继的请求只针对缓存了的业务对象进行。本节将在上篇的基础上讨论如何对业务对象进行排序,包括简单排序和高级排序。
同样,我们这里先建立一个显示交互页面(由于ObjectDataSource的原因无法用之前的列子)。创建一个AgriDataSort.aspx文件,拖放一个Repeater控件,编写前端代码:

<asp:Repeater ID="rpAgriDataList" runat="server">            <HeaderTemplate>                <table>                    <tr>                        <th>                                                 <asp:LinkButton runat="server" ID="lbtDataID">AgriDataID</asp:LinkButton>                                                                                </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataTem">AgriDataTem</asp:LinkButton>                                                       </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataSun">AgriDataSun</asp:LinkButton>                                                              </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataEle">AgriDataEle</asp:LinkButton>                        </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataWater">AgriDataWater</asp:LinkButton>                        </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataBelong">AgriDataBelong</asp:LinkButton>                        </th>                        <th>                            <asp:LinkButton runat="server" ID="lbtAgriDataTime">AgriDataTime</asp:LinkButton>                                                        </th>                    </tr>                </HeaderTemplate>            <ItemTemplate>                <tr>                    <td><%#Eval("ID") %></td>                    <td><%#Eval("Tem") %></td>                    <td><%#Eval("Sun") %></td>                    <td><%#Eval("Ele") %></td>                    <td><%#Eval("Water") %></td>                    <td><%#Eval("Belong") %></td>                    <td><%#Eval("Date") %></td>                </tr>            </ItemTemplate>            <FooterTemplate>                </table>            </FooterTemplate>                </asp:Repeater>   

显示效果如图:
这里写图片描述
接下来用相同的方法将要处理的数据放入到缓存中,我们在ObjAgriManager类下创建该方法(方便显示就提取前十五条数据):

public static List<AgriData> GetSortList()        {            List<AgriData> list = HttpContext.Current.Cache["SortList"] as List<AgriData>;            if (list == null)            {                //获取前15条记录                list = SqlAgriDataManager.GetList("select Top (15) * from AgriData");                HttpContext.Current.Cache.Insert("SortList", list);            }            return list;        }

基本的数据源得到之后,将它与页面链接起来,在AgriDataSort.aspx.cs的Page_Load事件下,将数据源绑定:

protected void Page_Load(object sender, EventArgs e){    List<AgriData> list = ObjAgriDataManager.GetSortList();    rpAgriDataList.DataSource = list;    rpAgriDataList.DataBind();}

运行效果如下:
这里写图片描述

简单排序——IComarable接口的实现

接下来便进行简单的排序操作,利用集合自带的Sort方法即可,通常代码会这样编写:

List<AgriData> list = ObjAgriDataManager.GetSortList();            list.Sort();            rpAgriDataList.DataSource = list;            rpAgriDataList.DataBind();

实际上这段代码是会报错的,原因很简单,没有实现IComparable< T>接口,从本质上讲,List< T>.Sort()的实现基础就是实现了IComparable< T>接口,而诸如int、char、string一类基础类型都是实现了该接口才能进行排序操作的。
IComparable< T>的定义很简单:

    public interface IComparable<in T>    {        // 摘要:         //     比较当前对象和同一类型的另一对象。        //        // 参数:         //   other:        //     与此对象进行比较的对象。        //        // 返回结果:         //     一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此对象小于 other 参数。 零 此对象等于 other。 大于零        //     此对象大于 other。        int CompareTo(T other);    }

详细的注释源码上已经很清楚了,根据返回额int型数值,来判断数据的先后位置。我们这里默认根据Tem的大小进行比较,因为在业务对象AgriData中Tem的类型为int,所以比较操作可以直接调用其CompareTo方法:

 public class AgriData:IData,IComparable<AgriData>    {        //...略       int IComparable<AgriData>.CompareTo(AgriData other)        {            return this.Tem.CompareTo(other.Tem);        }    }

运行程序,效果如预期所致:
这里写图片描述

高级排序——IComparer接口的实现

很显然,在实际运用中,我们经常需要对多个列进行排序,同时还要考虑升序和降序,比如我们先对ID进行升序排列,再对Sun值进行降序排列,这种情况下CompareTo虽然也可以实现但需要对AgriData添加额外的属性。这些.Net Framework已经考虑到了,并提供IComparer< T>接口进行了排序规则。

    public interface IComparer<in T>    {        // 摘要:         //     比较两个对象并返回一个值,指示一个对象是小于、等于还是大于另一个对象。        //        // 参数:         //   x:        //     要比较的第一个对象。        //        //   y:        //     要比较的第二个对象。        //        // 返回结果:         //     一个有符号整数,指示 x 与 y 的相对值,如下表所示。 值 含义 小于零 x 小于 y。 零 x 等于 y。 大于零 x 大于 y。        int Compare(T x, T y);    }

IComparer< T>同样需要实现一个方法,Compare(),计算机制和CompareTo基本相同,不过需要注意的是,这个接口不是要求AgriData对象实现的,而是要求另一个对象实现它,比如AgriDataComparer,而在调用Sort方法时,再将它作为参数传递过去,由于这个AgriDataComparer只针对AgriData进行,所以将它作为嵌套类更为合适。
首先考虑到排序的规则(升降?对那一列进行升降?),这里同样可以定义2个内部枚举:

 public enum SortDir { Ascending = 0, Descending }        public enum SortField { ID, Tem, Ele, Sun, Water, Belong, Date }

为了方便将这两个规则做参数处理,我们将它设计为一个结构体:

 //内部排序属性结构体        public struct Sorter         {            public SortDir dir;            public SortField field;            public Sorter(SortField field,SortDir dir)            {                this.dir = dir;                this.field = field;            }        }

这个排序规则结构体就作为AgriDataComparer的内部的字段存储,当然,实际中一组排序规则不一定足够,所以最好封装成List< Sorter>存储给为合适:

  public class AgriDataComparer : IComparer<AgriData>        {            //一组排序规则集合            private List<Sorter> list;        } public AgriDataComparer(List<Sorter> list)            {                this.list = list;            }

我们先从简单的单个属性的某种升降序入手,不考虑多个属性,我们来便写一个Compare方法体:

 public int Compare(AgriData x, AgriData y, SortDir dir, SortField field)            {                int result = 0;                switch (field)                {                    case SortField.ID:                        if (dir == SortDir.Ascending) result = x.ID.CompareTo(y.ID);                        else result = y.ID.CompareTo(x.ID);                        break;                    case SortField.Tem:                        if (dir == SortDir.Ascending) result = x.Tem.CompareTo(y.Tem);                        else result = y.Tem.CompareTo(y.Tem);                        break;                    case SortField.Sun:                        if (dir == SortDir.Ascending) result = x.Sun.CompareTo(y.Sun);                        else result = y.Sun.CompareTo(x.Sun);                        break;                    case SortField.Ele:                        if (dir == SortDir.Ascending) result = x.Ele.CompareTo(y.Ele);                        else result = y.ID.CompareTo(x.Ele);                        break;                    case SortField.Water:                        if (dir == SortDir.Ascending) result = x.Water.CompareTo(y.Water);                        else result = y.ID.CompareTo(x.Water);                        break;                    case SortField.Belong:                        if (dir == SortDir.Ascending) result = x.Belong.CompareTo(y.Belong);                        else result = y.ID.CompareTo(x.Belong);                        break;                    case SortField.Date:                        if (dir == SortDir.Ascending) result = x.Date.CompareTo(y.Date);                        else result = y.ID.CompareTo(x.Date);                        break;                }

通过传递的2个AgriData对象,和属性、升降序规则,对对象进行排序操作,不过,这个方法不能作为IComparer< T>的实现方法,因为IComparer< T>.Compare只接受两个参数传递,那么排序规则怎么去处理呢?多个排序规则又怎么处理呢?这个时候之前定义的List< Sorter>集合和之前的Compare就派上用场了,我们只要遍历List< Sorter>这个集合,每轮遍历对其中的排序规则调用之前定义的Compare方法处理即可,当两个值的某个属性相等时,触发下一组排序属性直到两个值不同为止:

 //实现ICompare接口            int IComparer<AgriData>.Compare(AgriData x, AgriData y)            {                int result = 0;                foreach (Sorter n in list)                {                    result = Compare(x, y, n.dir, n.field);                    if (result != 0) break;                }                return result;            }

哈哈,代码就是这么简单,这样就很好的解决了多组排序规则的处理。
为了方便Sort方法参数的传递,我们继续编写一组GetCompare方法做相关处理,首先我们看下按排序要求进行的Sort重载方法:

public void Sort(IComparer<T> comparer);

很显然,我们只要传递一个IComparer< T>类型的参数即可,为了方便调用,我们将GetCompare作为静态方法,并且返回类型为IComparer< T>,所以这组重载方法可以这样定义:

 //单个指定排序            public static AgriDataComparer GetComparer(SortField field,SortDir dir)            {                Sorter sorter = new Sorter(field, dir);                List<Sorter> list = new List<Sorter>();                list.Add(sorter);                return new AgriDataComparer(list);            }            //单个默认排序——ID、升序            public static AgriDataComparer GetComparer()            {                List<Sorter> list = new List<Sorter>()                {                    new Sorter(SortField.ID,SortDir.Ascending),                };                return new AgriDataComparer(list);            }            //多个指定排序            public static AgriDataComparer GetComparer(List<Sorter> list)            {                return new AgriDataComparer(list);            }

好的,我们在页面的后台进行测试:

 protected void Page_Load(object sender, EventArgs e)        {            List<AgriData.Sorter> sorter = new List<AgriData.Sorter>()            {                //1级条件——光照度降序                new AgriData.Sorter(AgriData.SortField.Sun,AgriData.SortDir.Descending),                //2级条件——湿度升序                new AgriData.Sorter(AgriData.SortField.Water,AgriData.SortDir.Ascending),            };            List<AgriData> list = ObjAgriDataManager.GetSortList();            list.Sort(AgriData.AgriDataComparer.GetComparer(sorter));            rpAgriDataList.DataSource = list;            rpAgriDataList.DataBind();        }

运行如图:
这里写图片描述
确实完成了我们的要求。