ben*_*san 6 c# delegates rdlc strategy-pattern subreports
我已经在rdlc报告中编写了一种处理多个子报告的技术,但是由于我试图使其具有通用性和可重复性,因此我不得不采用模型并针对每种情况稍微调整一下.
例如,如果我定义一个抽象接口,就像这样,我只需根据需要将其从winform剪切并粘贴到winform:
abstract class ISolutionStrategy
{
public abstract void AlgorithmInterface(Int64 searchCriteria, SubreportProcessingEventArgs e);
}
Run Code Online (Sandbox Code Playgroud)
首先,我希望能够通过包含has-a对象将其带入每种形式.我还想封装委托处理调度的行为,并使处理方法也"通用".
因此,设计要求是:
目标是制作一个可以独立测试并且坚固耐用的物体,也不必剪切和粘贴滚轮,并为每个新的winform做一堆手动调整.
在我看来,有人找到了比我现在更好的设计.
创建一个可以包含在winform中的对象,以处理多个子报表处理
到目前为止,我在本地表单加载事件中有一个委托:
this.reportViewer1.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
Run Code Online (Sandbox Code Playgroud)
它由*LocalReport_SubreportProcessing*方法中的switch语句处理.
该方法的主体包含一个switch语句:
void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
String commonSubreportKey = _commonSubreportKey;
switch (e.ReportPath)
{
case "rptSubAlternateParts":
runSubAlternatePart(e, commonSubreportKey, new GetAlternateParts());
break;
case "rptSubGetAssemblies":
runSubFailurePart(e, commonSubreportKey, new GetAssemblies());
break;
case "rptSubGetAssemblies":
runSubGetGetEndItemLRMFailureInfo(e, commonSubreportKey, new GetEndItemLRMFailureInfo());
break;
case "rptSubGetAssemblies":
runSubGetSubAssemblies(e, commonSubreportKey, new GetSubAssemblies());
break;
default:
break;
}
Run Code Online (Sandbox Code Playgroud)
在旁边:在我看来,与我考虑的替代方案相比,这种转换大多是人类可读的.我考虑使用带有报告名称的哈希作为键,并将函数调用数据作为值.但是,我真的不知道怎么做,我认为其他人更难理解.
之后,调用一个函数,该函数重新排列switch语句中从函数调用传递的信息:
private static void runSubAlternatePart(SubreportProcessingEventArgs e1, String commonReportKey, GetAlternatePart myAP)
{
myAP.AlgorithmInterface(commonReportKey, e1);
}
Run Code Online (Sandbox Code Playgroud)
这种重新排列肯定是代码口吃,但是我试图实现的策略模式似乎是必要的中间件:
abstract class IStrategy
{
public abstract void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e);
}
Run Code Online (Sandbox Code Playgroud)
以下是其中一份报告的战略的具体实施:
class GetAlternatePart : IStrategy
{
private BLL.AlternatePartBLL ds = new BLL.AlternatePartBLL();
public override void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e)
{
e.DataSources.Clear();
DataTable myDataTable = ds.GetAlternativePart(searchParam);
DataSet myDataSet = new DataSet();
myDataSet.Tables.Add(myDataTable);
e.DataSources.Add(new ReportDataSource("BLL_AlternatePartBLL", myDataSet.Tables[0]));
}
}
}
Run Code Online (Sandbox Code Playgroud)
无论如何,我的愿望是不必在报告之间反复地连接相同的逻辑,因为我有许多报告有多个子报告.
我想要一个库质量的方法来使用类动态创建出现口吃的中间部分,并且我想传入一个"匿名"函数,它实际上实现了子报表与其相应数据源的详细连接.
对于包含子报告的单个报告,甚至是一些一次性报告,我正在做的事情还可以,但是如何减少手动,更强大和更可测试?
我的环境是Visual Studio 2008,目标是.NET 3.5; 在抽象类的声明方式和编译方式方面似乎存在差异.
我建议的解决方案是对基类进行非常简单的重构,它将您需要在每个 WinForm 中编写的代码减少为两件事:1)用于该表单的报表的设置;2) 定义如何获取该表单的报告数据。
假设每个 WinForm 都继承自一个名为 ReportForm 的基类,则每个 WinForm 的代码将如下所示:
public partial class Form1 : ReportForm
{
public Form1()
{
// Wire up the report used by the Visual Studio-designed report viewer to the base class
base.WinFormReport = reportViewer1.LocalReport;
InitializeComponent();
}
// The search parameters will be different for every winform, and will presumably
// come from some winform UI elements on that form, e.g., parentPartTextBox.Text
protected override DataResult GetReportData(SubreportProcessingEventArgs e)
{
// Return the data result, which contains a data table and a label which will be
// passed to the report data source
// You could use DataSet in DataResult instead of DataTable if needed
switch (e.ReportPath)
{
case "rptSubAlternateParts":
return new DataResult(
new BLL.AlternatePartBLL().GetAlternativePart(parentPartTextBox.Text)
, "BLL_AlternatePartBLL"
);
case "rptSubGetAssemblies":
return new DataResult(
new BLL.SubAssemblyBLL().GetSubAssemblies(someOtherTextBox.Text)
, "BLL_SubAssemblyBLL"
);
default:
throw new NotImplementedException(string.Format("Subreport {0} is not implemented", e.ReportPath));
}
}
.
.
.
Run Code Online (Sandbox Code Playgroud)
上面的代码做了这些事情:
1) 告诉基类(ReportForm) Form 中使用了哪个Report。如果您愿意,您也可以将 Report 重构为 ReportForm,但我的方法仍然允许您在 Visual Studio 中创建和操作 ReportViewer 及其报表。但是,如果您以编程方式传递报表而不是在设计器中,则可能需要将报表从派生的 WinForm 类发送到基类中。
2) 定义报表如何获取其所有子报表的数据。为此,我们只需要返回一个数据表和一个标签,因为这就是报表数据源最终需要的全部内容。将 DataTable 和标签绑定到 RDLC 数据源的代码属于基类 (ReportForm),因为该绑定代码对于所有 WinForms 都是通用的。
现在,ReportForm 代码应如下所示:
/// <summary>
/// Don't cut & paste into any Windows Forms, inherit the behavior you want from a base class
/// </summary>
public abstract class ReportForm : System.Windows.Forms.Form
{
// I'm not sure exactly what this is used for, but I put it in base class in case there is some use for it here
protected string _commonSubreportKey = "12345";
// This will be the one line of code needed in each WinForm--providing the base class a reference
// to the report, so it has access to the SubreportProcessing event
protected Report WinFormReport { get; set; }
// Making this abstract requires each derived WinForm to implement GetReportData--foolproof!
protected abstract DataResult GetReportData(SubreportProcessingEventArgs e);
// Wire up the subreport_processing handler when any WinForm loads
// You could override this in derived WinForms classes if you need different behavior for some WinForms,
// but I would bet this default behavior will serve well in most or all cases
protected virtual void Form1_Load(object sender, EventArgs e)
{
Report.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
}
// When the Subreport processing event fires, handle it here
// You could also override this method in a derived class if need be
protected virtual void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
// Get the data needed for the subreport
DataResult dataResult = this.GetReportData(e);
e.DataSources.Clear();
e.DataSources.Add(new ReportDataSource(dataResult.Label, dataResult.Table));
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,ReportForm 基类继承自 Form,然后所有 WinForm 都将继承自 ReportForm——这是整个设计的关键。下面是这个 ReportForm 基类的工作原理:
1)当实例化WinForm时,设置了基本属性WinFormReport,因此基本对象知道正在使用哪个Report。
2)当WinForm加载时,Form Load事件在基类上被调用,因为它没有在派生类中定义。加载表单时,将连接报表的 Subreport_Processing 事件。
3) 当用户输入参数并单击某些内容以在报表查看器中创建报表时,最终子报表将由 RDLC 实例化,并且 Subreport_Processing 事件会多次触发,每个子报表触发一次。
4) 当事件触发时,基类事件处理程序调用 GetReportData(e),这将调用 WinForm 上定义的 GetReportData 方法。注意,该方法在基类中是抽象的,因此不能在基类上定义,而必须在派生类中定义。
5) WinForm 中的 GetReportData(e) 方法使用您最初指定的调度程序逻辑,将 DataTable(如果需要,也可以是 DataSet)和文本字符串返回到基本处理程序。
6) 基本处理程序获取 DataTable/DataSet 和文本字符串,将它们作为报告数据源提供给报告,并且还可以执行显示报告所需的任何其他操作。
经过深思熟虑,我决定建议将常见行为相当简单地重构为基类,因为我认为它可以满足您的要求,而且我没有看到哪里需要更复杂的东西。我认为您会发现这种方法非常可读,它绝对最小化了每个新 WinForm 中所需的内容,最重要的是,发现它具有极强的可扩展性;也就是说,当您继续开发系统时,您总是会问“这个新代码是否需要在每个 WinForm 中重复,或者是否很常见,因此应该进入基类?”
如果您对此方法有任何疑问或疑虑,请随时添加评论,祝您好运。我希望这正是您正在寻找的!
| 归档时间: |
|
| 查看次数: |
1147 次 |
| 最近记录: |