我创建了一个类,它可以帮助您模拟报告的页面代码隐藏类:
public abstract class ReportGenerator
{
private readonly List<ReportParameter> _reportParameters;
private readonly List<ReportDataSource> _reportDataSources;
private readonly LocalReport _report;
private readonly string _friendlyName;
private IList<ReportGenerator> _subReports;
private readonly string _repName;
internal string ReportName
{
get { return _repName; }
}
public List<ReportDataSource> ReportDataSources
{
get { return _reportDataSources; }
}
public List<ReportParameter> ReportParameters
{
get { return _reportParameters; }
}
public abstract void SetParamsAndDataSources();
protected ReportGenerator(string friendlyName, string reportName)
{
_friendlyName = friendlyName;
_report = new LocalReport { ReportPath = string.Format("Reports/{0}.rdlc", reportName) };
_repName = reportName;
_reportParameters = new List<ReportParameter>();
foreach (var paramIter in _report.GetParameters())
{
_reportParameters.Add(new ReportParameter(paramIter.Name));
}
_reportDataSources = new List<ReportDataSource>();
foreach (var dsName in _report.GetDataSourceNames())
{
_reportDataSources.Add(new ReportDataSource { Name = dsName });
}
}
private void report_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
if(_subReports == null)
{
throw new ApplicationException(string.Format("Report '{0}' has subreport(s) which are not properly set up.", _repName));
}
var srLst = _subReports.Where(srIter => srIter.ReportName.Equals(e.ReportPath)).ToList();
if (!srLst.Any())
{
return;
}
var srFound = srLst[0];
foreach (var dsIter in srFound.ReportDataSources)
{
e.DataSources.Add(dsIter);
}
}
public void RefreshReportViewer(out ReportViewer rvw)
{
InitReport();
rvw = new ReportViewer();
rvw.LocalReport.ReportPath = _report.ReportPath;
rvw.LocalReport.SetParameters(ReportParameters);
foreach (var dsIter in ReportDataSources)
{
rvw.LocalReport.DataSources.Add(dsIter);
}
rvw.LocalReport.SubreportProcessing += report_SubreportProcessing;
rvw.LocalReport.Render("Word");
}
public byte[] GenerateReport(Formats reportFormat, out string mimeType, out string fileName)
{
SetParamsAndDataSources();
ValidateReportParameters();
ValidateReportDataSources();
_report.SetParameters(_reportParameters);
foreach (var dsIter in _reportDataSources)
{
_report.DataSources.Add(dsIter);
}
_report.SubreportProcessing += report_SubreportProcessing;
Warning[] warnings;
string[] streamIds;
string encoding;
string extension;
fileName = string.Format("{0}_{1}.", _friendlyName, DateTime.Now.ToString("ddMMyyhhmmss"));
var repExp = _report.Render(reportFormat.ToString("G"), null, out mimeType, out encoding, out extension, out streamIds, out warnings);
fileName += extension;
return repExp;
}
private void InitReport()
{
SetParamsAndDataSources();
ValidateReportParameters();
ValidateReportDataSources();
_report.SetParameters(_reportParameters);
foreach (var dsIter in _reportDataSources)
{
_report.DataSources.Add(dsIter);
}
}
protected void SetSubReports(params ReportGenerator[] subReports)
{
_subReports = subReports.ToList();
}
private void ValidateReportParameters()
{
var paramsMetaData = _report.GetParameters();
foreach (var paramIter in _reportParameters.Where(paramIter => paramIter.Values.Count == 0 && !paramsMetaData[paramIter.Name].AllowBlank))
{
throw new ApplicationException(string.Format(
"Report {0}: Value not supplied for the parameter: {1}", _report.ReportPath, paramIter.Name));
}
}
private void ValidateReportDataSources()
{
foreach (var dsIter in _reportDataSources.Where(paramIter => paramIter.Value == null))
{
throw new ApplicationException(string.Format("Report {0}: Value not supplied for the DataSource: {1}", _report.ReportPath, dsIter.Name));
}
}
internal void SetReportParameterValue(string paramName, params string[] paramValues)
{
foreach (var paramIter in _reportParameters.Where(paramIter => paramIter.Name.Equals(paramName)))
{
paramIter.Values.AddRange(paramValues.ToArray());
return;
}
}
internal void SetDataSourceValue(string dataSourceName, object dataSourceValue)
{
foreach (var dsIter in _reportDataSources.Where(dsIter => dsIter.Name.Equals(dataSourceName)))
{
dsIter.Value = dataSourceValue;
return;
}
}
public enum Formats
{
PDF = 1,
Excel,
Word
}
}
报告的示例代码隐藏:
public class YourReport: ReportGenerator
{
public string Parameter1 { get; set; }
public string Parameter2 { get; set; }
public string Parameter3 { get; set; }
public string SubReportParameter1 { get; set; }
public string SubReportParameter2 { get; set; }
public List<SomeEntity> EntityList { get; set; }
public List<SomeOtherEntity> SubReportEntityList { get; set; }
private readonly CodeBehindClassForAnySubReport _anySubReport;
public YourReport():base("ReportName", "ReportFileName")
{
_anySubReport = new YourReport();
SetSubReports(_anySubReport);
}
public override void SetParamsAndDataSources()
{
SetReportParameterValue("Param1NameInReport", Parameter1);
SetReportParameterValue("Param2NameInReport", Parameter2);
SetReportParameterValue("Param3NameInReport", Parameter3);
SetReportParameterValue("SubParam1NameInReport", SubReportParameter1);
SetReportParameterValue("SubParam2NameInReport", SubReportParameter2);
base.SetDataSourceValue("DataSourceNameInReport", EntityList);
_anySubReport.EntityList = this.SubReportEntityList;
//Call this only when subreport contains any datasource.
//Parameters for subreports should be chained in the report design
_anySubReport.SetParamsAndDataSources();
}
}
前端代码隐藏类的示例用法:
var repGen = new YourReport
{
Parameter1 = someValue1,
Parameter2 = someValue2,
Parameter3 = someValue3,
SubReportParameter1 = someValue4,
SubReportParameter2 = someValue5,
EntityList = someList1,
SubReportEntityList = someList2
};
var repExp = repGen.GenerateReport(format, out mimeType, out fileName);
Response.Buffer = true;
Response.Clear();
Response.ContentType = mimeType;
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.BinaryWrite(repExp);
Response.Flush();
还有一些说明可以帮助您:
什么是报告生成器?
Report Generator 是一个类似于 MS-Reports 代码隐藏的库。它使报表导出在 web 层中易于使用。
如何使用报告生成器?
对于任何新报告,请按照以下步骤模拟代码隐藏类:
- 对于报告中的每个子报告,确保以下内容: (a) 报告生成器代码隐藏类已经使用与主报告相同的步骤进行编码。(b) 在报表设计器中,主报表的参数被链接到子报表参数。因此,子报告参数应该冒泡并作为主要报告参数公开。
- 为派生自“ReportGenerator”类的主报表创建一个新类。
- 对于主报告中的每个参数(其中一些是从 1b 中提到的子报告中冒出来的),为新类添加一个新属性。
- 为每个子报表创建一个只读字段。字段的类型应该是子报表的代码隐藏类。
- 将任何子报表数据源属性公开为主报表属性
- 主报表类的构造函数应该: (a) 调用基类构造函数,将报表的物理名称作为方法参数。(b) 初始化所有子报表字段,并通过调用 SetSubReports 方法添加到子报表列表中。
- 覆盖方法“SetParamsAndDataSources”。此方法应调用: (a) SetReportParameterValue 为每个报告参数设置值。请注意,这些值将是相应的类属性的值。(b) SetDataSourceValue 为每个报告数据源设置值。请注意,这些值将是相应的类属性的值。(c) 每个子报告字段的 SetParamsAndDataSources。
按照以下简单步骤使用在 web 层中创建的新类:
- 创建新报告生成器类的实例
- 为参数和数据源属性赋值
- 调用 GenerateReport 方法,将导出的报告作为字节数组返回。将此作为文件附件级联到响应。
使用报告生成器有什么好处?
- 它消除了 Web 层中参数名称和数据源的所有硬编码。即使这些在报告生成器类中再次被硬编码,我们也可以控制我们正在编码的内容。
- mocked-code-behind 类是相应报告的对象表示,因此使任何面向对象的开发人员的生活更轻松。
- 报告生成的所有复杂功能都隐藏在 ReportGenerator 基类中,因此对于任何新报告只需进行有限的编码。
报告生成器基类是否可在任何其他项目中重用?
是的。
有什么可以改进的吗?
是的,很多。其中很少有: 1. 向 ReportGenerator 基类添加一个可以将报表附加到报表查看器的方法。2. 目前,基类将所有报告参数视为“字符串”类型。可以修改以适应任何类型的参数。