Das Gedächtnisproblem.
Bei der Arbeit mit den komplizierten Berichten, die inneren Skript verwenden, merkte ich auf Verwendungssteigerung vom operativen Gedächtnis. Für Web-Services kann das kritisch sein.
Warum passiert es? Nach der Kompilation des Berichtskripts bleibt im Gedächtnis eine nicht so große Bibliothek (Kompilation), die vom Frameworkmotor für die nächste Aufrufe gespeichert wird. Am Ende entfernt der Müllsammler diese Bibliotheken, aber bevor dieser Entfernung entsteht die Steigerung des Gedächtnisverbrauches.
Der Ausgang von dieser Situation besteht in der Benutzung einer Domain der Anwendung für den Bericht mit dem Skript. Bei der Domainverwendung können wir Kompilationen vom Gedächtnis löschen, genauer nicht die Kompilationen selbst, sondern die Domain herunterladen, in denen diese Kompilationen gestartet werden. Auf diese Weise können wir unsere Ressourcen sparen und das Gedächtnis freigeben, einfach wenn wir unnötige Domain ausladen.
Aber dazu braucht man eine Kompilation zu erstellen, die in zweiter Domain herunterladen wird und wie eine Folge darauf, trifft man einige Schwierigkeiten, die entstehen, wenn man die Methoden von zweiter Domain aus erster aufruft.
Verwendung der Domain
Beliebige .Net Anwendung hat schon eine Domain standardmäßig. Man erstellt eine Domain und stellt da eine Kompilation mit dem Bericht hin. In der Kompilation, außer den Objekten des Berichts werden auch die Methoden von der Arbeit mit dem Bericht: Start, Export.
Wie ich merkte, wird es einfacher, mit der zweiten Domain durch Marshalization und Proxy zusammenzuwirken. Üblich benutzt man die Marshalization für die Zusammenwirkung zwischen den Prozessen, aber das gilt für Domain auch. Marshalization lässt dem Kunde in einer Domain Methode den Objekten in anderer Domain aufrufen. Wir werden den Objekt in der Domain durch Proxy aufrufen.
Zusammenwirkung zwischen den Domain entsteht ebenfalls wie zwischen den Prozessen. Um den Zugang zum Code von anderer Domain zu bekommen, braucht man unbedingt den Proxy. Proxy gilt wie ein Ersatzmittel vom Objekt. Er leitet die Aufrufe von einer Domain zur anderen um.
Betrachten wir Arbeitsschemen mit dem Bericht in einer und in den beiden Domain.
Auf dem Bild sieht man, dass in einem Prozess eine Domain startet. Und in ihr starten wir einen Bericht mit dem Skript.
Hierbei wird der Berichtsskript in der Assembly kompiliert und in der Domain herunterladet. Wenn wir viele verschiedene Berichte mit dem Skript aufrufen, steigt wesentlich die Zahl solcher Assembly, was führt zur Steigerung des Gedächtnisverbrauches.
Jetzt betrachten wir die Variante mit den beiden Domain.
In diesem Fall startet der Bericht in der getrennten Domain aber beim gleichen Prozess. Hierbei wird die Assembly des Berichtsskripts in der zweiten Domain herunterladen. Dank Proxy Verwendung werden alle Methoden in der ersten Domain verfügbar. Wenn wir die Arbeit mit dem Bericht beenden und die Berichtsform schließen, wird die Domain mit allen Assembly ausgeladen. Dann wird das Gedächtnis freigegeben.
Realisation.
Erstellen wir WindowsForms. Diese Anwendung mit der Default Klasse braucht man für die Erstellung von der zweiten Klasse und den Aufruf der Methoden von der herunterladenen in dieser Klasse Assembly.
Hinzufügen wir zwei Tasten: Berichtsstart und Berichtsexport in PDF.
Hinzufügen wir auch die Methode der Domainerstellung:
1 2 3 4 5 |
public AppDomain NewDomain() { AppDomain domain = AppDomain.CreateDomain("NewDomain"); return domain; } |
Und die Methode für die Domainausladung:
1 2 3 4 |
public void UnloadDomain(AppDomain domain) { AppDomain.Unload(domain); } |
Hinzufügen wir auch in der Solution ein Projekt mit der Klassenbibliothek. Nennen wir das als NewDomain. Also, wir haben eine Assembly, die wir in der neuen Domain eingeladen. Unbedingt folgen wir die Klasse von MarshalByRefObject nach. Für die Arbeit mit FastReport .Net braucht man einen Link auf der Bibliothek FastReport.dll hinzuzufügen.
Jetzt kann man ein Objekt Report erstellen:
1 |
public Report report1 = new Report();
|
In dieses Objekt herunterladen wir den Bericht mit dem Skript. Dafür erstellen wir die Einladungsmethode für den Bericht:
1 2 3 4 |
public void LoadReport(string path) { report1.Load(path); } |
Und Methoden von Start und Export vom Bericht:
1 2 3 4 5 6 7 8 9 10 |
public void ShowReport() { report1.Show(); } public void ExportToPDF() { FastReport.Export.Pdf.PDFExport pdf = new FastReport.Export.Pdf.PDFExport(); pdf.Export(report1); } |
Man hat jetzt alles, was man für die Arbeit mit dem Bericht braucht. Kompilieren wir die Assembly und speichern in den Ordner mit ausführbarer Datei. Oder hinzufügen wir den Link auf die Assembly vom Anwendungsprojekt.
Gehen wir zum Projekt mit der Anwendung.
Früher verschriebt man eine Zusatzmethode von neuer Domain, jetzt muss man in diese Domain oben gebildete Assembly einladen. Für die Arbeit mit dieser Assembly braucht man Proxy.
1 2 3 4 5 |
public dynamic CreateProxy(AppDomain domain) { dynamic proxyOfChildDomainObject = domain.CreateInstanceFromAndUnwrap("NewDomain.dll", "NewDomain.NewDomainClass"); return proxyOfChildDomainObject; } |
Hier erstellen wir ein Exemplar der Proxy-Klasse für unsere Assembly. Die Methode CreateInstanceFromAndUnwrap erstellt ein neues Exemplar dieses Typs, der in der gezeichneten Assembly-Datei bestimmt wurde. Wir definieren den Dateiname und vollen Klassenname in den Parametren.
Also, erstellen wir eine neue Domain und Proxy für die Arbeit mit der Assembly. Jetzt hinzufügen wir den Code für die erste Taste.
1 2 3 4 5 6 7 8 |
private void button1_Click(object sender, EventArgs e) { AppDomain domain = NewDomain(); dynamic proxy1 = CreateProxy(domain); proxy1.LoadReport(Environment.CurrentDirectory + "/Matrix.frx"); proxy1.ShowReport(); UnloadDomain(domain); } |
Besprechen wir dieses Codestück ein bisschen deutlicher. In erster Zeile erstellt man neue Domain der Anwendung mit Hilfe von der Methode NewDomain(). Weiter erstellen wir Proxy für unsere Assembly in zweiter Domain. Herunterladen wir den Bericht. Und starten wir ihn im Vorschaufenster. Nach dem Vorschau wird die Domain ausgeladen.
Den gleichen Code verwenden wir für die zweite Taste. Aber außer dem Bericht führen wir die Exportmethode für den Bericht in PDF aus. Hierbei werden das Einstellungsfenster für Export und Dialogfenster für Speichern angezeigt.
1 2 3 4 5 6 7 8 |
private void button2_Click(object sender, EventArgs e) { AppDomain domain = NewDomain(); dynamic proxy1 = CreateProxy(domain); proxy1.LoadReport(Environment.CurrentDirectory + "/Matrix.frx"); proxy1.ExportToPDF(); UnloadDomain(domain); } |
Schluss damit. Die Anwendung ist fertig.
Schlussfolgerungen.
Die Verwendung von mehreren Domain kann die Arbeit langsamer machen, aber am Ende kann man wesentlich Zeit sparen, wenn man die Berichte mit dem integrierten Skript verwendet. Assembly mit dem Skript wird vom Gedächtnis gemeinsam mit der Domainausladung entfernt, was kann für Systeme, die viel Gedächtnisressource brauchen, wie Web-Services kritisch sein.
Auf meiner Sicht, ist das meinst effektivste Verfahren für die Arbeit mit den Berichten, die Skript enthalten. Start den Bericht wird in getrennter Anwendungsdomain mit der Neuerstellung dieser Domain durch die N-mal Berichtserstellungen durchgeführt. Die Zahl N muss man durch das Experiment auswählen, wenn man die Bilanz von Ressourcenverbrauch und Gedächtnisreinigung bestimmen muss.