更换大开关?

我有一个名为“ReportController.aspx”的页面,其目的是基于查询字符串参数实例化报表(类)

switch (Request.QueryString["Report"]) { case "ReportA": CreateReportAReport("ReportA's Title"); break; case "ReportB": CreateReportBReport("ReportB's Title"); break; case "ReportC": CreateReportCReport("ReportC's Title"); break; case "ReportD": CreateReportDReport("ReportD's Title"); break; ... 

基本上,每次需要新报告时,都会有添加案例和添加方法的开销。 这个switch语句可能会变得非常长。 我读过可以使用Dictionary将报告映射到?。 这看起来如何使用字典(假设这是一种更好的方法)。

此外, CreateReportXReport方法基本上将一堆额外的QueryString值传递给报表类的构造函数(每个报表类都有不同的构造函数)。

假设所有报告都实现了IReport ,你可以使用Func ,如下所示:

 IDictionary> dictToReport = new Dictionary { {"ReportA", () => CreateReportAReport("ReportA's Title") } , {"ReportB", () => CreateReportBReport("ReportB's Title") } , ... }; 

然后,您可以使用以下代码替换开关:

 var myReport = dictToReport[Request.QueryString["Report"]](); 

没有必要在某处输入新信息; 关键是要将其从代码中删除,以避免重新编译和重新部署以进行这种微不足道的更改。

一些好的选择是在XML配置文件中列出这些值,或者更好的是,在数据库中列出这些值。

无论来源如何,您可能都希望用这些数据填写字典。 这将:

  • 使缓存变得容易
  • 制作干净,快速的代码

当您将数据从配置中提取到代码中时,您需要将项添加到字典中,如下所示:

 Dictionary = configDataGetter.GetReportDataFromDB(). ToDictionary(r => r.Name, myReportCreatorFactory(r => r.ReportID)) 

此示例假定您将数据作为某种实体对象获取,并使用将为您的代码创建报告的策略模式的工厂。 当然,你可以有多种方式来做这件事。

我假设报告过于广泛,多样,性质不同,你不能只在数据库中放置sql和样式构建块?

根据op的评论进行编辑

啊,抓住了。 好吧,我不知道你有多少时间,但是就像你把所有东西都推到某种工厂一样 ,你有更好的选择。 我将从一些类似的事情中给你一些希望有所帮助的想法。 每个步骤本身都是一个改进,但也是一个很好的步骤,以真正将报告逻辑与此shell代码分开。 此外,我可以看到你已经知道你正在做什么,我肯定知道我将在下面说些什么,但我不知道你知道什么,这对其他人有帮助。

首先,从代码中提取任何信息到db(如果你还没有),并在改进设置时添加更多数据库字段(和一两个表)。

你可能已经知道了,但我会为其他人提一下,看看我上面提到的策略模式。 您可以将每个“报表函数”的自定义逻辑实际放在各种策略类的构造函数中。 它们都将从您的基础ReportGeneratorinheritance(或使用常见的IReportGenerator接口)。 他们可以而且应该共享相同的构造函数; 变量报告参数将由字典类型的参数处理。 每个类的构造函数实现都知道变量的类型是需要的(来自db配置),并相应地转换/使用它们。

下一步可能是使用reflection来真正摆脱工厂中的select语句。 您必须将类的名称作为db中报告配置数据的一部分(并且具有公共构造函数)。

此时,添加新报告的方式非常简洁,即使您每次都必须添加新类。 那么好。 它履行单一责任和开放式原则。

现在,只是从应用程序删除类的最后一步,因此可以即时添加/编辑它们。 查看MEF 。 这就是它的目的。 您可能在互联网上找到的一些您可能不应该使用的东西是CodeDom (当没有别的东西时很棒,但MEF更好)和.NET 5中的编译即服务function.MEF就是这种方式去。

我认为最好重新设计此代码并将其转换为某些数据库表(“报告”),以保留每个报告的报告列表和ID。

而已。

要使用Dictionary执行此操作,您只需在包含类型中将其构建为静态缓存

 public class Container { private static Dictionary> ReportMap = new Dictionary>(); static Container() { ReportMap["ReportA"] = () => CreateReportAReport("ReportA's Title"); ReportMap["ReportB"] = () => CreateReportBReport("ReportB's Title"); // etc ... } } 

现在构建了地图,您只需在函数中进行查找而不是switch

 Func func; if (!ReportMap.TryGetValue(Request.QueryString["Report"), out func)) { // Handle it not being present throw new Exception(..); } Report report = func();