似乎 Microsoft 尝试将报告放入自己的单独内存空间以解决所有内存泄漏问题,而不是修复它们。在这样做的过程中,他们引入了一些严重的崩溃,并最终导致更多的内存泄漏无论如何。他们似乎缓存了报表定义,但从不使用它,从不清理它,每个新报表都会创建一个新的报表定义,占用越来越多的内存。
我也尝试过做同样的事情:使用单独的应用程序域并将报告编组给它。我认为这是一个糟糕的解决方案,而且很快就会弄得一团糟。
我所做的与此类似:将程序的报告部分拆分为自己的单独报告程序。无论如何,这证明是组织代码的好方法。
棘手的部分是将信息传递给单独的程序。使用Process 类启动报告程序的新实例并在命令行上传递它需要的任何参数。第一个参数应该是一个枚举或类似的值,指示应该打印的报告。我在主程序中的代码如下所示:
const string sReportsProgram = "SomethingReports.exe";
public static void RunReport1(DateTime pDate, int pSomeID, int pSomeOtherID) {
RunWithArgs(ReportType.Report1, pDate, pSomeID, pSomeOtherID);
}
public static void RunReport2(int pSomeID) {
RunWithArgs(ReportType.Report2, pSomeID);
}
// TODO: currently no support for quoted args
static void RunWithArgs(params object[] pArgs) {
// .Join here is my own extension method which calls string.Join
RunWithArgs(pArgs.Select(arg => arg.ToString()).Join(" "));
}
static void RunWithArgs(string pArgs) {
Console.WriteLine("Running Report Program: {0} {1}", sReportsProgram, pArgs);
var process = new Process();
process.StartInfo.FileName = sReportsProgram;
process.StartInfo.Arguments = pArgs;
process.Start();
}
报告程序看起来像:
[STAThread]
static void Main(string[] pArgs) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var reportType = (ReportType)Enum.Parse(typeof(ReportType), pArgs[0]);
using (var reportForm = GetReportForm(reportType, pArgs))
Application.Run(reportForm);
}
static Form GetReportForm(ReportType pReportType, string[] pArgs) {
switch (pReportType) {
case ReportType.Report1: return GetReport1Form(pArgs);
case ReportType.Report2: return GetReport2Form(pArgs);
default: throw new ArgumentOutOfRangeException("pReportType", pReportType, null);
}
}
您的GetReportForm 方法应该拉取报表定义,使用相关参数来获取数据集,将数据和任何其他参数传递给报表,然后将报表放在表单上的报表查看器中并返回一个参考表格。请注意,可以提取此过程的大部分内容,以便您基本上可以说“使用此数据和这些参数从该程序集中给我一份此报告的表格”。
另外请注意,两个程序都必须能够看到与该项目相关的数据类型,因此希望您已将数据类提取到它们自己的库中,这两个程序可以共享引用。将所有数据类都放在主程序中是行不通的,因为您将在主程序和报表程序之间存在循环依赖关系。
也不要过度使用参数。在报告程序中进行您需要的任何数据库查询;不要传递一个庞大的对象列表(这可能无论如何都行不通)。您应该只传递简单的东西,如数据库 ID 字段、日期范围等。如果您有特别复杂的参数,您可能需要将 UI 的那部分也推送到报告程序,而不是在命令行上将它们作为参数传递。
您还可以在主程序中引用报表程序,生成的 .exe 和任何相关的 .dll 将被复制到同一个输出文件夹。然后,您可以在不指定路径的情况下运行它,而只使用可执行文件名本身(即:“SomethingReports.exe”)。您还可以从主程序中删除报告 dll。
其中一个问题是,如果您从未真正发布过报告程序,您将收到一个明显的错误。只需虚拟发布一次,生成清单,然后它就会工作。
一旦您完成了这项工作,很高兴看到您的常规程序的内存在打印报告时保持不变。报告程序出现,占用的内存比您的主程序多,然后消失,完全清理它,您的主程序占用的内存不超过它已经拥有的内存。
另一个问题可能是每个报表实例现在将比以前占用更多内存,因为它们现在是完全独立的程序。如果用户打印了很多报表并且从不关闭它们,它会很快消耗大量内存。但我认为这仍然要好得多,因为只需关闭报告即可轻松回收内存。
这也使您的报告独立于您的主程序。即使在关闭主程序后它们也可以保持打开状态,您可以从命令行手动生成它们,也可以从其他来源生成它们。