似乎微软试图将报告放入自己的单独内存空间中以解决所有内存泄漏问题,而不是修复它们。这样做会导致一些严重的崩溃,并最终产生更多的内存泄漏。他们似乎缓存报告定义,但从未使用过也从未清理过,每个新报告都会创建一个新的报告定义,占用越来越多的内存。
我尝试过同样的方法:使用单独的应用程序域并将报告转移到其中。我认为这是一个可怕的解决方案,很快就会变得一团糟。
相反,我做的是类似的:将程序中的报告部分拆分成自己独立的报告程序。这实际上是组织代码的好方法。
棘手的部分是将信息传递给独立的程序。使用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);
}
static void RunWithArgs(params object[] pArgs) {
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文件。
其中一个问题是,如果您从未发布过报表程序,那么您将会收到一个清单错误。只需虚拟发布一次以生成清单,然后它就可以正常工作了。
一旦您完成了这个步骤,当打印报表时,非常好的一点是您的常规程序的内存保持不变。报表程序出现,占用比您的主程序更多的内存,然后消失,完全清理掉您的主程序,使其不再占用更多内存。
另一个问题可能是,每个报表实例现在将占用比以前更多的内存,因为它们现在是完全独立的程序。如果用户打印了很多报表并且从未关闭它们,它将非常快地使用大量内存。但我认为这仍然比较好,因为只需关闭报表即可轻松地回收该内存。
这也使得您的报表独立于主程序。它们甚至可以在关闭主程序后继续保持打开状态,并且您也可以手动从命令行或其他来源生成它们。
BlockingCollection
或ConcurrentQueue
类?ConcurrentQueue
存在内存泄漏问题,可能是一个问题所在。 - Jim MischelConcurrentQueue
在 .NET 的并行扩展中是存在的。从 3.5 转换到 4.0 可能涉及将其替换为System.Collections.Concurrent
。我知道我们在转换到 4.0 时做了这件事。 - Jim Mischel