In diesem Artikel erzählen wir über einen spezifischen Fall der Verwendung von unseren Komponenten in den Benutzer-Projekten und die mit diesem Fall verbundenen potenziellen Probleme.
Einführung
In .NET Framework werden die Mechanismen für Ausführen von Gruppen der Anwendungen in einem Prozess – Anwendungsdomänen (AppDomains) – realisiert. Jede Domäne ist ein isolierter logischer Container, in dem eine Aufgabe mit ihrer Sammlung der Assemblierungen erfüllt wird. In jede Domäne kann man nur eine Version der Assemblierung hochladen, aber man kann nicht die Assemblierung aus der Domäne herunterladen, es ist möglich, nur die ganze Domäne herunterzuladen.In .NET kann man nur eine Anwendungsdomäne verwenden, aber gibt es auch die Bereiche des Ladens der Assemblierungen (AssemblyLoadContext). Standardmäßig, werden alle Assemblierungen in den Bereich AssemblyLoadContext.Default hochgeladen, aber kann man auch die neuen Bereiche erstellen, in die man verschiedene Versionen der Assemblierungen hochladen, und auch die Bereiche laden, kann. Wichtig!
Die gleichen Typen aus verschiedenen Bereichen sind inkompatibel, und, wenn Sie dieselbe Assemblierung in verschiedene Bereiche hochladen, werden die Typen aus diesen Bereichen zwischen einander inkompatibel.
Der Kern des Problems
Die große Rolle spielt die Stelle, wo die Assemblierung geladen wird:- wenn die Assemblierung in die Hauptanwendung hinzugefügt wird, wird sie in einen standardmäßigen Bereich geladen, und wird für alle anderen Assemblierungen verfügbar;
- wenn die Assemblierung schon in einem anderen Assemblierung-Plugin verwendet wird, wird sie dynamisch und in denselben Bereich wie das Plugin geladen. Hier entstehen zwei Optionen:
- wenn das Plugin in den Hauptbereich geladen wird, entstehen keine Probleme;
- wenn das Plugin in einen separaten Bereich geladen wird, können die Probleme entstehen, über die wir unten erzählen.
Erstes Problem
Der Kern des Problems besteht darin, dass einige Befehle den Bereich, in den sie erfüllt werden, „verlieren“, und darum können nicht die Typen aus diesem Bereich erhalten, und, dementsprechend, funktionieren unrichtig.Zum Beispiel, der Befehl TypeDescriptor.GetConverter(type) verweist auf System-Assemblierung, die nur in standardmäßigen Bereich geladen werden kann, und während der Arbeit „vergisst“ über aufrufenden Bereich. Wenn type sich im aufrufenden Bereich befindet, kann der Befehl nicht diesen Typen erhalten und seinen Konverter abrufen. Diese Konverter sind in den Attributen einiger Klassen (StiMargin, StiBorder usw.) beschrieben und bei der Serialisierung verwendet.
Darum:
- funktioniert nicht das Hochladen von Berichten aus XML-Format mit der Nachricht «... TypeConverter cannot convert from System.String»;
- funktioniert nicht die Kompilierung der Berichte.
Wir können nicht dieses Problem auf unserer Seite beheben, weil wir diesen Befehl nicht aufrufen, er wird innerhalb anderer System-Methoden aufgerufen.
Die .NET-Entwickler betrachten es nicht als wesentliches Problem und bieten workarounds stattdessen zu verwenden.
Lösungsmöglichkeiten
Option 1, einfach, aber mit den Beschränkungen:
Laden vom Bericht: man kann die .mrt-Datei mit JSON-Auszeichnung verwenden, sein Laden funktioniert anders und entsteht darum kein Problem.
Berechnungsmodus: wir empfehlen nicht die Kompilation als Berechnungsmodus im Bericht zu verwenden. Das heißt, dass man das Berechnungsmodus als Interpretation stellen muss.
Wir haben auch eine Verbesserung verwirklicht: wenn dieser Fehler entsteht, wird der Berechnungsmodus des Berichtes als Interpretation gestellt.
Option 2:
Man kann zu unseren Klassen einen speziellen Befehl, der die Verwendung vom Bereich erzwingt, hinzufügen:
StiReport stiReport;
using (AssemblyLoadContext.EnterContextualReflection(Assembly.GetExecutingAssembly()))
{
stiReport = new StiReport();
stiReport.Load(templateStream);
stiReport.Render(false);
}
Wichtig!Das Ergebnis der Arbeit des Befehls TypeDescriptor.GetConverter(type) wird irgendwo innerhalb System-Bibliothek zwischengespeichert, darum wenn diese Methode irgendwo ohne Verpackung aufgerufen wird, gibt der erneute Aufruf mit richtiger Verpackung das erste unrichtige Ergebnis zurück.
Zweites Problem
Das zweite Problem ist dem ersten Problem ähnlich. Der Befehl Type.GetType(name) kann auch nicht in einigen Fällen erforderliche Typen erhalten.Lösungsmöglichkeiten
Dieser Befehl wird in unserem Code verwendet, und wir haben vor Kurzem eine Verbesserung realisiert, um dieses Problem zu beheben.
Drittes Problem
Bei der Kompilation des Berichtes wird die Assemblierung in einen separaten Bereich hochgeladen, damit der Bericht bei Dispose hochgeladen werden kann. Aber früher wurde nur der Fall beachtet, wann unsere Assemblierungen in einen separaten Bereich hochgeladen sind.Lösungsmöglichkeiten
Wir haben eine kleine Verbesserung verwirklicht, und derzeit funktioniert es für Plugins.