ReportViewer,RDLC 报表开发之分页

前段时间开发报表,采用了 ReportViewer + RDLC , 开发整理如下.

分页思路

MS 的DataGrid ,GridView,和 ReportViewer 分页机制差不多,都需要绑定全部数据,MS控件自动处理分页。 只是ReportViewer 是按页面布局分页的,而不是按数据条数。绑定全部数据的方式会无谓的增加服务器压力,尤其是数据计算和表关联多的情况下。为了减轻服务器压力,我们采用数据分页,数据传到应用服务器端后,再加工一下(插入空数据),做也可分页数据源。

如果数据库有 100 条数据,每页10条数据,那存储过程返回10条数据后,还需要再插入90条数据。当数据大的时候,插入的数据也很多。有没有插入数据再少一点的办法呢?

当然是有了,没有就不写了。大笑。先提两个概念:维度和指标.

维度: 是指公司,项目,区域等要分析的对象.

指标: 是指针对维度进行计算的结果

1. 先对维度进行分页. 

2.针对分页结果,有针对性的进行指标计算 ,可以把每个计算列,做成函数,方便调用,同时,也方便维护和测试。

实现方案

方案是把页码做成分组,这样再插入的时候,就只插入 Celling( 90 ÷ 10 ) 条的空分组数据即可。 插入的数据少了 n 倍。

存存过程示例代码:

CREATE proc  [dbo].[R_ANALYSIS] 
(
@Query varchar(50)= '', --查询条件

@PageIndex int = 1 , --从1开始
@PageSize int = 50 , --每页条数,传入 <=0 表示不分页。
@PageCount int=0 output --总页数 输出参数
)
as
begin
declare @RowCount int ;

--写业务,返回分页结果 , 分页结果放到 表变量 @list 中
/*示例:

With Query_Rank as
(
select ROW_NUMBER() OVER (order by 1 )as RowNumber ,*
from T_ROOM
)

select Col1,col2, dbo.Func1(Col1) , dbo.Func2(col1,col2)
fromQuery_Rank
whereRowNumber between 100 and 150

*/
 
if(@PageSize > 0 )
begin
--计算 @RowCount
set @PageCount = ceiling( @RowCount *1.0 / @PageSize) ;

end
else
begin
set @PageCount = 1
end

end

所需要的两个程序加工函数:

public static IEnumerable<T> ReportWrap<T>(this IEnumerable<T> Source, int CurrentPageIndex, int PageCount)
where T : IReportModel, new()
{
if (PageCount < 2)
{
foreach (var item in Source)
{
yield return item;
}
yield break;
}

Func<int, T> CreateEmptyObj = (CurrentIndex) =>
{
T retVal = new T();

retVal.SysPageIndex = CurrentIndex;

return retVal;
};

for (int i = 0; i < CurrentPageIndex; i++)
{
yield return CreateEmptyObj(i + 1);
}


foreach (var item in Source)
{
item.SysPageIndex = CurrentPageIndex + 1;
yield return item;
}

for (int i = CurrentPageIndex + 1; i < PageCount; i++)
{
yield return CreateEmptyObj(i + 1);
}
}

public static DataTable ReportWrap(this DataTable Source, int CurrentPageIndex, int PageCount)
{
if (PageCount <=1)
{
return Source;
}
Func<int, DataRow> CreateEmptyObj = (CurrentIndex) =>
{
DataRow retVal = Source.NewRow();

retVal["SysPageIndex"] = CurrentIndex;

return retVal;
};

for (int i = 0; i < CurrentPageIndex; i++)
{
Source.Rows.InsertAt(CreateEmptyObj(i + 1), 0);
}

for (int i = CurrentPageIndex + 1; i < PageCount; i++)
{
Source.Rows.Add(CreateEmptyObj(i + 1));
}

return Source;
}

程序调用函数代码,很简单:

DataTable dt = db.Exec_SP_DataTable("sp_Report_ModuleVisitStatisView", pars);
pageCount = Convert.ToInt32(pars[6].Value);
dt.ReportWrap(pageIndex -1 , pageCount);

之后,再把 DataTable 转成 实体列表。方便RDLC数据绑定。

另注:

上面的 IReportModel , 是一个只包含 SysPageIndex 属性的接口. 它约束了报表返回的Model 必须继承自 IReportModel 且必须拥有 无参构造函数.

1. 无参构造函数的作用是 初始化空对象. 比如: 字符串类型的,要设置为 string.Empty . 数值类型要设置为 0.

2.接口是约束Model 必须具有 SysPageIndex 属性用于页码值.

 

报表RDLC 的设置:

绑定数据源,添加父组,选 SysPageIndex。在该列的属性上设置: Hidden = True , Width = 0cm。即隐藏该列。后端绑定代码:

ReportDataSource data = new ReportDataSource("LogSource", MyBiz.GetLogReportData("query", pageIndex, pageSize, out pageCount));

this.ReportViewer1.LocalReport.LoadReportDefinition(new FileStream(Server.MapPath("~/Admin/Report/Log.rdlc")));
this.ReportViewer1.LocalReport.DataSources.Clear();
this.ReportViewer1.LocalReport.DataSources.Add(data);

至此,分页就做好了.

posted @ 2012-03-04 14:48  NewSea  阅读(...)  评论(...编辑  收藏