对象的筛选
前面说了泛型和委托,说完筛选和排序之后会说LINQ,所以这段时间的知识会起到一个承上启下的作用.
主要是讲解一下.NET框架中最常用的接口之一:IComparable;还会说点在LINQ出现之前对象筛选和排序的方法.
对象的筛选
不知道大家接触过SQL语句没有,楼主是学过了,这里就当你学过了吧,如果我们要对数据进行筛选,首先想到的是”Where”子句,而不是List<T>.FindAll();当我们需要对数据进行排序的时候,我们会想到”Order By”子句,而不是List<T>.Sort();当我们需要对数据进行分页的时候,首先想到的存储过程,而不是List<T>.GetRange().
为啥呢?因为让数据库去完成这些东西在很多情况下效率高,尤其是在数据量比较大,这就是为啥会出现数据库的原因,一开始的数据量比较小,用的TXT就够了,但是随着数据的增大,TXT不够用了,出现了各种各样的数据库,现在数据库也不够用了,所以出现了分布式存储.在数据量不大的时候,一次将所有数据取出,然后缓存在服务器上,对于以后的排序,筛选,分页请求仅针对缓存进行,这样做绝对有好处,而且效率高,为啥呢?因为服务器的运算效率是很高的,而且由于数据不大,所以数据库在短时间内就能够完成相应的操作.
但是世事无绝对,没有哪种方法绝对的优秀,一种好的方法或者不好的方法仅仅适用于一种范围.
本节的内容就是说一下当数据量特别大的时候,可能不仅仅希望数据先用”where”子句进行筛选,还希望再一次筛选只返回当前页需要显示的数据条目.
基于拼装SQL的筛选
先思考这样一个案例:SQL Server 2000中的默认数据库North Wind中的Orders订单表中,如果要在一个Asp.NET WebForm页面上对它按年月日的任意组合进行筛选,咋办?我觉得步骤应该会是这样:
1.在页面上创建三个下拉框,分别用于对年月日进行选择.
2.当用户第一次访问页面时,显示所有数据.此时有一次对数据库的访问,返回全部数据,SQL语句为”select * from orders”
3.用户选择年月日中的任意一个,产生一个PostBack
4.根据用的选择拼装一个SQL语句,例如”where Year(OrderDate)=@Date and Month(OrderDate)=@Month”.
5.将SQl语句发送到数据库,数据库返回查询结果,显示到用户界面.
如果你对数据就不了解,上面的过程就是根据用户的选择,呈现不同的数据.
在上面的模式中,为了根据用户的选择显示不同的数据,每次操作(即每次产生一个PostBack)都要对数据进行一次访问.下面看一下具体的实现过程是咋样的.
先别管那些乱七八糟的,咱们要想实现前后台的交互,需要创建一个Orders类来表示业务对象Order吧(位于Order.cs).
先看这个Order.cs咋实现
public class Order
{
//分别表示订单ID,用户ID,下单日期,国家
private int orderId;
private string customerId;
private DateTime orderDate;
private string country;
public int OrderId
{
get
{
return orderId;
}
set
{
orderId = value;
}
}
public string CustomerId
{
get
{
return customerId;
}
set
{
customerId = value;
}
}
public string Country
{
get
{
return country;
}
set
{
country = value;
}
}
public DateTime OrderDate
{
get
{
return orderDate;
}
set
{
orderDate = value;
}
}
}
我就想问问还有比这个类还简单的吗?这个表就是表示关于Order的一些信息.对于集合数据,这里使用List<Order>进行存储.接下来需要再创建文件OrderManager.cs,用于从数据库返回结果,并填充List<Order>.OrderManager.cs中通常包含类似这样的代码:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
namespace WebApplication1
{
public class OrderManager
{
public static List<Order> GetList(string query)
{
List<Order> list = null;
SqlDataReader reader = ExecuteReader(query);
if (reader.HasRows)
{
list = new List<Order>();
while (reader.Read())
{
list.Add(GetItem(reader));
}
}
reader.Close();
return list;
}
//获取一个SqlDataReader对象
private static SqlDataReader ExecuteReader(string query)
{
string connString = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return reader;
}
//从一行获取一个Order对象.
private static Order GetItem(IDataRecord record)
{
Order item = new Order();
item.OrderId = Convert.ToInt32(record["orderId"]);
item.CustomerId = record["CustomerId"].ToString();
item.OrderDate = Convert.ToDateTime(record["OrderDate"]);
item.Country = record["ShipCountry"].ToString();
return item;
}
}
}
上面贴的这几段代码都没有,比完全可以不用看,为啥我想贴呢,因为楼主忘了,打一遍可以回复一下记忆.
上面的代码很好理解:GetList()方法接受一个查询语句,然后返回List<Order>列表.
ExecuteReader()方法,该方法根据传递进去的查询语句query创建并返回了一个SqlDataReader对象,该对象用于读取数据库返回的数据.在While语句中,调用了GetItem()方法,该方法根据每一个数据创建一个Order对象.最后将Order对象添加到List<Order>列表中,并返回列表.
可见,需要在页面上调用的方法就是GetList(query)方法了.
一般的基于传统的拼装SQL语句的筛选我就不多了,为啥呢,因为很复杂,我需要做很多工作,接下来咱们重点说一声基于对象筛选是啥样的,又是怎么样提升性能.
(1)在页面上创建三个下拉框,分别用于对年,月,日进行选择.
(2)用户第一次访问页面,显示所有数据.将对数据库进行一次访问,并获取全部数据,SQL语句类似于”select * from Orders”
(3)将返回的数据(已经转换成了List<Order>业务对象)全部进行缓存.
(4)根据用户的选择对缓存中的List<Order>进行筛选,返回筛选结果,显示在页面上.
(5)如此反复,用户每次用户请求都只针对缓存进行.
关于缓存,你只需要只用使用Cache.Insert(key,value)就行了.最后再说一点,当使用Cache.Insert(key,value)插入缓存时,虽然没有过期时间,但是当服务器内存空间不足时,依然会将缓存移除.
本例中,基于对象的筛选就是对List<Order>进行筛选.实现思路很简单,创建一个重载的GetList()方法,在这个GetList()中取回所有的Order并添加到缓存中.然后创建一个新的空List<Order>列表,从缓存中去除List<Order>进行遍历,将符合条件的项加入到刚刚创建的新列表中,最后返回新列表.
public static List<Order> GetList()
{
List<Order> list = HttpContext.Current.Cache["fullList"] as List<Order>;
if (list==null)
{
list = GetList("select orderId,CustomerId,ShipCountry,OrderDate from orders");
//添加缓存,永不过期(可以再删除,更新操作时手动让缓存过期)
HttpContext.Current.Cache.Insert("fullList",list);
}
return list;
}
上面无参数的GetList()方法在没有缓存的情况下调用GetList(query)方法,返回全部列表,然后加入缓存;在有缓存的情况下直接使用缓存中的数据.
使用List<T>.FindAll(Predicate<T>match)进行筛选
在List<T>上提供了一个FindAll(Predicate<T>match)方法来进行筛选工作,有方法的参数Predicate<T>封装筛选的规则.Predicate<T>是一个泛型委托,这意味着match参数是一个返回bool类型的方法,在FindAll()呢部,将会调用传递进去的这个方法.
我写的这些例子应该都不能运行,因为还有好多工作没做,比如连接数据库,配置文件的修改,各种操作.我写代码的原因只是想让大家了解一下我想说的东西,因为楼主的资历尚浅,说也说不明白,我就说说楼主能明白的东西,也希望能用最简单的方式让你明白,那么楼主的作用就达到了.