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();
}
运行如图:
确实完成了我们的要求。