Кажется, что 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
должны получить определение отчета, использовать соответствующие аргументы для получения набора данных, передать данные и любые другие аргументы в отчет, а затем поместить отчет в средство просмотра отчета в форму и вернуть ссылка на форму. Обратите внимание, что можно извлечь большую часть этого процесса, так что вы можете сказать «дайте мне форму для этого отчета из этой сборки, используя эти данные и эти аргументы».
Также обратите внимание, что обе программы должны иметь возможность видеть ваши типы данных, которые имеют отношение к этому проекту, поэтому, надеюсь, вы извлекли ваши классы данных в их собственную библиотеку, на которую обе эти программы могут поделиться ссылкой. Было бы неэффективно иметь все классы данных в основной программе, поскольку между основной программой и программой отчетов была бы круговая зависимость.
Не переусердствуйте с аргументами. Выполните любые запросы к базе данных, которые вам нужны в программе отчетов; не передавайте огромный список объектов (которые, вероятно, не будут работать в любом случае). Вы должны просто передавать простые вещи, такие как поля идентификатора базы данных, диапазоны дат и т. Д. Если у вас есть особенно сложные параметры, вам может потребоваться передать эту часть пользовательского интерфейса в программу отчетов, а не передавать их в качестве аргументов в командной строке.
Вы также можете поместить ссылку на программу отчетов в вашей основной программе, и результирующий .exe и любые связанные с ним .dll будут скопированы в ту же папку вывода. Затем вы можете запустить его без указания пути и просто использовать исполняемое имя файла (то есть: «SomethingReports.exe»). Вы также можете удалить отчеты dll из основной программы.
Одна из проблем заключается в том, что вы получите явную ошибку, если вы никогда не публиковали программу отчетов. Просто постарайтесь опубликовать его один раз, чтобы создать манифест, и тогда он будет работать.
Как только у вас все заработало, очень приятно видеть постоянную память вашей обычной программы при печати отчета. Появляется программа отчетов, которая занимает больше памяти, чем ваша основная программа, а затем исчезает, полностью ее очищая, и ваша основная программа занимает не больше памяти, чем она уже имела.
Другая проблема может заключаться в том, что каждый экземпляр отчета теперь будет занимать больше памяти, чем раньше, поскольку теперь они являются целыми отдельными программами. Если пользователь печатает много отчетов и никогда не закрывает их, он очень быстро израсходует память. Но я думаю, что это все еще намного лучше, поскольку эту память можно легко восстановить, просто закрыв отчеты.
Это также делает ваши отчеты независимыми от вашей основной программы. Они могут оставаться открытыми даже после закрытия основной программы, и вы можете генерировать их из командной строки вручную или из других источников.