SilverLight之路(七)

接上回,数据表格搞定了,不过当数据量超大时,比如我这个项目,客户数将近100W,那么选择这种分页会死人的,传统作法,服务器端分页吧。

我们还是使用DataPager分布控件,想一下之前的实现,原理就是它与DataGrid绑定同一数据源,分页的操作其实也是在数据源上进行的,这里是PagedCollectionView。而如果我们要实现服务器端分页,它们就不能绑在一起啦,这里是从网上找到的一个实现方法,实现类似如下

 

先看服务器WCF端,加入客户实体类(前面有说过步骤,不再累述),然后定义操作契约

 

public List<DAL.WFT_Batch_CustomerClassification> GetCustomerListPager(CustomerClassificationFilter filter, out int totalCount)
{
using (var entities = new DAL.WFT_101104Entities())
{
int rowsCount = 0;
var query
= entities.WFT_Batch_CustomerClassification;

if (filter.PageIndex <= 0)
rowsCount
= query.Count();
totalCount
= rowsCount;
query
= query.OrderBy(t => t.GUID).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);

return query.ToList();
}
}

数据契约 

[DataContract]
public class CustomerClassificationFilter
{
[DataMember]
public int PageIndex { set; get; }
[DataMember]
public int PageSize { set; get; }
}

这里同意提供该服务器端分页实现方法的网友的说法,建议把查询条件也进行实体类包装。目前我们的查询条件并未包含具体业务逻辑条件,只包括了分页大小与当前页索引。这里有一个问题,排序的时候没有指定排序字段,且每次都是固定排序,这个问题我们稍候再来解决,现在我们还是把重点放到分页上。

 

SL端实现,首先给DataPager增加一个扩展方法

 

public static class DataPageExtension
{
/// <summary>
/// BindSource扩展方法
/// </summary>
/// <param name="dataPager">分页控件</param>
/// <param name="totalCount">记录总数</param>
/// <param name="pageSize">分页大小</param>
public static void BindSource(this DataPager dataPager, int totalCount, int pageSize)
{
List
<int> list = new List<int>(totalCount);
for (int i = 0; i < totalCount; i++) list.Add(i);
PagedCollectionView pcv
= new PagedCollectionView(list);
pcv.PageSize
= pageSize;
dataPager.Source
= pcv;
}
}

这里本想给pcv指定一个记录总数,似乎没有,所以只能用一个循环来模拟数据源了。

数据绑定时

 

private void BindGrid(int pageIndex)
{
CustomerClassificationFilter ccf
= new CustomerClassificationFilter();
ccf.PageIndex
= pageIndex;
ccf.PageSize
= this.dpCustomerList.PageSize;
client.GetCustomerListPagerAsync(ccf);
}

事件的回调可以放到初始化时去做,否则会多次注册事件,嗯,也可以叫做注册了多个事件响应方法。

 

client.GetCustomerListPagerCompleted += new EventHandler<WcfService.GetCustomerListPagerCompletedEventArgs>(
(s, ex)
=>
{
PagedCollectionView pcv
= new PagedCollectionView(ex.Result);
this.dgCustomerList.ItemsSource = pcv;

if (this.dpCustomerList.PageIndex <= 0)
{
this.dpCustomerList.PageIndexChanged -= dpCustomerList_PageIndexChanged;
this.dpCustomerList.BindSource(ex.totalCount, this.dpCustomerList.PageSize);
this.dpCustomerList.PageIndexChanged += dpCustomerList_PageIndexChanged;
}
});

注意分页事件的注册,如果不这样做,会进行两次绑定,因为在分页时也要进行数据查询的,而绑定时会自动触发分页事件,这种情况在做下拉列表默认选择项时也会存在,也可以考虑用这种办法。

这里还有一个要注意的地方,注意我们的WCF操作契约的实现,里面使用了一个Out关键字,大家都知道这个是什么意思吧。如果我们这里这样定义了,在wcf服务的代理类生成时,会把它作为result的参数传回到客户端的,如:ex.totalCount,并不需要我们在调用时指定,这与传统的使用方法有区别,所以要注意一下。调用时只传一个参数就可以了,如:client.GetCustomerListPagerAsync(ccf);

分页事件

 

void dpCustomerList_PageIndexChanged(object sender, EventArgs e)
{
BindGrid(
this.dpCustomerList.PageIndex);
}

上面就是全部的实现代码,它的原理是使DataPager绑定一个虚拟的数据源,只是使用它的分页功能,具体的说就是页面大小、当前页索引与分页事件。还有一点,为了性能的考虑,原作者做了页索引为零时的判断,这样就只有在未获得结果之前进行结果总数的计算,这一点很重要,特别是数据量大的时候。

 

现在我们再来看看排序的功能,因为进行了服务器端分页,那么排序功能当然也要在服务器端来实现了,否则就只有在当前页(因为是服务器端分页,所以结果就只有当前页数据)进行了。

 

原理很好理解,把排序字段也传到服务器端就好办了,我们先在条件参数中把排序字段加上

[DataContract]
public class CustomerClassificationFilter
{
[DataMember]
public SortDescription Sorted { set; get; }
[DataMember]
public int PageIndex { set; get; }
[DataMember]
public int PageSize { set; get; }
}

在查询时加入排序的逻辑

 

public List<DAL.WFT_Batch_CustomerClassification> GetCustomerListPager(CustomerClassificationFilter filter, out int totalCount)
{
using (var entities = new DAL.WFT_101104Entities())
{
int rowsCount = 0;
var query
= entities.WFT_Batch_CustomerClassification;

if (filter.PageIndex <= 0)
rowsCount
= query.Count();
totalCount
= rowsCount;
if (filter.Sorted != null)
query
= SilverlightClassLibrary.DBHelper.DataSorting<DAL.WFT_Batch_CustomerClassification>(query, filter.Sorted).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
else
query
= query.OrderBy(t => t.GUID).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
return query.ToList();
}
}

DataSorting方法我稍候介绍。

在SL端放一变量来存储上一次的排序字段,默认可以指定一个,如

 

//上一次的排序列
private SortDescription oldsort = new SortDescription("GUID", ListSortDirection.Ascending);

在调用时把它传到服务器端

 

private void BindGrid(int pageIndex)
{
CustomerClassificationFilter ccf
= new CustomerClassificationFilter();
ccf.Sorted
= oldsort;
ccf.PageIndex
= pageIndex;
ccf.PageSize
= this.dpCustomerList.PageSize;
client.GetCustomerListPagerAsync(ccf);
}

下面我们要做的就是如何取得这个字段了,那么,如何得到这个值呢?其实排序也与分页类似,其实也是在数据源上进行的,我们的数据源就是PagedCollectionView(注意这个与DataPager的Source可不是同一个,这个是真实的数据源)。那么我们通过它的SortDescriptions属性就可以得到了,而默认它是支持多列排序的(按shift实现),因此我们得到的是一个集合,而恰好它实现了INotifyCollectionChanged接口,我们这里就是要利用这个接口的CollectionChanged事件,代码如下:

 

System.Collections.Specialized.INotifyCollectionChanged scn = pcv.SortDescriptions;
scn.CollectionChanged
+= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
(scnS, scnE)
=>
{
if (scnE.Action == NotifyCollectionChangedAction.Remove && scnE.NewStartingIndex == -1)
{
return;
}
if (scnE.Action == NotifyCollectionChangedAction.Reset)
return;
pcv.SortDescriptions.Clear();
oldsort
= (SortDescription)scnE.NewItems[0];
BindGrid(
this.dpCustomerList.PageIndex);
});

 

这个方法也是花费了我好久才在网上找到的,现在再感叹一下吧!!!!不过这里只实现了单列排序,可以通过DataGrid的属性来控制是否使用多列排序功能,如果想实现多列排序的话,就要再进行扩展了,目前我没有用到这个功能。

 

回过头再看看服务器端动态排序的实现,这个与sl与wcf无关了,纯属linq的应用了,按我们对Linq的理解,我们会写出类似这样的DataSorting方法的实现

 

switch (filter.Sorted.PropertyName)
{
case "姓名":
if (filter.Sorted.Direction == ListSortDirection.Ascending)
query
= query.OrderBy(t=>t.客户姓名);
else
query
= query.OrderByDescending(t=>t.客户姓名);
break;
case "年龄":
if (filter.Sorted.Direction == ListSortDirection.Ascending)
query
= query.OrderBy(t => t.年龄);
else
query
= query.OrderByDescending(t => t.年龄);
break;
}

这肯定不行啊,继续百度吧,于是我找到了这个解决办法。

 

Linq初体验——Order By 通过属性名动态排序

http://www.cnblogs.com/xxfss2/archive/2010/12/13/1905023.html

 

稍作修改,实现主要代码如

 

public static IQueryable<T> DataSorting<T>(IQueryable<T> source, SortDescription sort)
{
string sortExpression = sort.PropertyName;
string sortingDir = string.Empty;
if (sort.Direction == ListSortDirection.Ascending)
sortingDir
= "OrderBy";
else if (sort.Direction == ListSortDirection.Descending)
sortingDir
= "OrderByDescending";
ParameterExpression param
= System.Linq.Expressions.Expression.Parameter(typeof(T), sortExpression);
PropertyInfo pi
= typeof(T).GetProperty(sortExpression);
Type[] types
= new Type[2];
types[
0] = typeof(T);
types[
1] = pi.PropertyType;
System.Linq.Expressions.Expression expr
= System.Linq.Expressions.Expression.Call(typeof(Queryable), sortingDir, types, source.Expression, System.Linq.Expressions.Expression.Lambda(System.Linq.Expressions.Expression.Property(param, sortExpression), param));
IQueryable
<T> query = source.AsQueryable().Provider.CreateQuery<T>(expr);
return query;
}

 

至此,这个列表功能主体就基本完成了。

 

posted @ 2011-06-16 20:16  第八颗流星  阅读(555)  评论(0编辑  收藏  举报