レポートのページ コード ビハインド クラスを模倣するのに役立つクラスを作成しました。
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 メソッドを呼び出します。これを添付ファイルとして応答にカスケードします。
Report Generator を使用する利点は何ですか?
- これにより、Web レイヤーでのパラメーター名とデータソースのハードコーディングがすべてなくなります。これらもレポート ジェネレーター クラスでハードコーディングされていますが、コーディング内容を制御できます。
- モック分離コード クラスは、対応するレポートのオブジェクト表現であるため、オブジェクト指向の開発者にとって作業が楽になります。
- レポート生成のすべての複雑な機能は、ReportGenerator 基底クラス内に隠されているため、新しいレポート用に限られたコーディングのみを行う必要があります。
Report Generator の基本クラスは他のプロジェクトで再利用できますか?
はい。
改善点はありますか?
はい、多く。1. ReportGenerator 基本クラスに、レポートをレポート ビューアーに添付できるメソッドを追加します。2. 現在、基本クラスはすべてのレポート パラメータを「文字列」型と見なしています。あらゆるタイプのパラメーターに対応するように変更できます。