前段時間開發報表,采用了 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);
至此,分頁就做好了.