.NET: Abhängigkeitsbehandlung

Wer hat aufgrund der Baugruppenumleitung keine Probleme festgestellt? Höchstwahrscheinlich wird jeder, der eine relativ große Anwendung entwickelt hat, früher oder später mit diesem Problem konfrontiert sein.

Jetzt arbeite ich bei JetBrains im JetBrains Rider-Projekt und bin an der Migration von Rider zu .NET Core beteiligt. Zuvor war er in Circuit, einer Cloud-basierten Anwendungshosting-Plattform, mit der gemeinsamen Infrastruktur beschäftigt.



Unter der Zwischensequenz befindet sich das Protokoll meines Berichts von der Moskauer DotNext 2019-Konferenz, auf der ich über die Schwierigkeiten bei der Arbeit mit Baugruppen in .NET sprach und anhand praktischer Beispiele zeigte, was passiert und wie damit umzugehen ist.


In allen Projekten, in denen ich als .NET-Entwickler gearbeitet habe, musste ich mich mit verschiedenen Problemen beim Verbinden von Abhängigkeiten und Laden von Assemblys befassen. Wir werden darüber reden.

Poststruktur:


  1. Abhängigkeitsprobleme
  2. Strikte Rig-Beladung

  3. .NET Core

  4. Debug-Assembly-Downloads


Was sind einige Abhängigkeitsprobleme?


Als sie Anfang der 2000er Jahre mit der Entwicklung von .NET Framework begannen, war das Problem der Abhängigkeitshölle bereits bekannt, als Entwickler in allen Bibliotheken wichtige Änderungen zulassen und diese Bibliotheken für die Verwendung mit bereits kompiliertem Code nicht mehr kompatibel sind. Wie kann man ein solches Problem lösen? Die erste Lösung liegt auf der Hand. Behalten Sie immer die Abwärtskompatibilität bei. Dies ist natürlich nicht sehr realistisch, da das Brechen von Änderungen sehr einfach in Code zu integrieren ist. Zum Beispiel:



Änderungen und .NET-Bibliotheken auflösen

Dies ist ein spezielles Beispiel für .NET. Wir haben eine Methode und haben beschlossen, einen Parameter mit einem Standardwert hinzuzufügen. Der Code wird weiterhin kompiliert, wenn wir ihn wieder zusammensetzen, aber binär sind es zwei völlig unterschiedliche Methoden: Eine Methode hat keine Argumente, die zweite Methode hat ein Argument. Wenn der Entwickler innerhalb der Abhängigkeit die Abwärtskompatibilität auf diese Weise unterbrochen hat, können wir den Code, der mit dieser Abhängigkeit von der vorherigen Version kompiliert wurde, nicht verwenden.

Die zweite Lösung für Abhängigkeitsprobleme besteht darin, die Versionierung von Bibliotheken und Assemblys hinzuzufügen - alles. Möglicherweise gibt es unterschiedliche Versionsregeln. Der Punkt ist, dass wir verschiedene Versionen derselben Bibliothek irgendwie voneinander unterscheiden können und Sie verstehen können, ob das Update nicht funktioniert oder nicht. Sobald wir die Versionen einführen, tritt leider ein anderes Problem auf.



Version Hölle ist die Unfähigkeit, eine Abhängigkeit zu verwenden, die binär kompatibel ist, aber gleichzeitig eine Version hat, die nicht zur Laufzeit passt, oder eine andere Komponente, die diese Versionen überprüft. In .NET ist FileLoadException eine typische Manifestation der Version Hell, obwohl die Datei auf der Festplatte liegt, aber aus irgendeinem Grund nicht mit Laufzeit geladen wird.



In .NET haben Assemblys viele verschiedene Versionen - sie haben versucht, Versions-Hells auf verschiedene Weise zu reparieren und zu sehen, was passiert ist. Wir haben ein Paket System.Collections.Immutable. Viele Leute kennen ihn. Er hat die neueste Version des NuGet-Pakets 1.6.0. Es enthält eine Bibliothek, eine Assembly mit Version 1.2.4.0. Sie haben erhalten, dass Sie keine Build-Bibliothek Version 1.2.4.0 haben. Wie kann man verstehen, dass es im NuGet-Paket 1.6.0 liegt? Es wird nicht einfach. Zusätzlich zur Assembly-Version verfügt diese Bibliothek über mehrere weitere Versionen. Zum Beispiel Assembly Assembly Version, Assembly Information Version. Dieses NuGet-Paket enthält tatsächlich drei verschiedene Assemblys mit denselben Versionen (für verschiedene Versionen des .NET-Standards).

.NET-Dokumentation
Opbuild-Standard

Es wurde viel Dokumentation zum Arbeiten mit Assemblys in .NET geschrieben. Es gibt einen .NET-Leitfaden für die Entwicklung moderner .NET-Anwendungen, der .NET Framework, .NET Standard, .NET Core, Open Source und alles Mögliche berücksichtigt. Etwa 30% des gesamten Dokuments sind dem Laden von Baugruppen gewidmet. Wir werden spezifische Probleme und Beispiele analysieren, die auftreten können.

Warum ist das alles notwendig? Erstens, um nicht auf einen Rechen zu treten. Zweitens können Sie den Benutzern Ihrer Bibliotheken das Leben erleichtern, da sie mit Ihrer Bibliothek nicht die Abhängigkeitsprobleme haben, an die sie gewöhnt sind. Es hilft Ihnen auch bei der Migration komplexer Anwendungen zu .NET Core. Und um das Ganze abzurunden, können Sie ein SRE werden. Dies ist ein Senior (Binding) Redirect-Ingenieur, zu dem jeder im Team kommt und fragt, wie er eine weitere Weiterleitung schreiben soll.

Strikte Montage Laden


Das strikte Laden von Assemblys ist das Hauptproblem, mit dem Entwickler in .NET Framework konfrontiert sind. Es wird ausgedrückt in FileLoadException. Bevor ich mit dem Laden der strengen Baugruppe fortfahre, möchte ich Sie an einige grundlegende Dinge erinnern.

Wenn Sie eine .NET-Anwendung erstellen, erhalten Sie ein Artefakt, das sich normalerweise in Bin / Debug oder Bin / Release befindet und einen bestimmten Satz von Assembly-Assemblys und Konfigurationsdateien enthält. Baugruppen beziehen sich auf Namen und Namen der Baugruppe. Es ist wichtig zu verstehen, dass sich Assembly-Links direkt in der Assembly befinden, die auf diese Assembly verweist. Es gibt keine magischen Konfigurationsdateien, in die Assembly-Referenzen geschrieben werden. Auch wenn es Ihnen so scheint, als ob solche Dateien existieren. Referenzen befinden sich in den Baugruppen selbst in binärer Form.

In .NET gibt es einen Prozess zum Auflösen von Assemblys. In diesem Fall wird die Assemblydefinition bereits in eine echte Assembly konvertiert, die sich auf der Festplatte befindet oder irgendwo im Speicher geladen ist. Das Auflösen von Assemblys wird zweimal durchgeführt: In der Build-Phase, wenn Sie Referenzen in * .csproj haben, und zur Laufzeit, wenn Sie Referenzen in den Assemblys haben, und nach einigen Regeln werden sie zu Assemblys, die heruntergeladen werden können.

// Einfacher Name
MyAssembly, Version = 6.0.0.0,
Kultur = neutral, PublicKeyToken = null

// Starker Name
Newtonsoft.Json, Version = 6.0.0.0,
Kultur = neutral, PublicKeyToken = 30ad4fe6b2a6aeed // PublicKey


Kommen wir zum Problem. Baugruppenname Es gibt zwei Haupttypen. Die erste Art von Baugruppenname ist Einfacher Name. Sie sind leicht daran zu erkennen, dass sie PublicKeyToken = null haben. Es gibt einen starken Namen, es ist leicht, sie daran zu erkennen, dass ihr PublicKeyToken nicht null ist, sondern einen gewissen Wert.



Nehmen wir ein Beispiel. Wir haben ein Programm, das von der Bibliothek mit MyUtils-Dienstprogrammen abhängt, und die Version von MyUtils ist 9.0.0.0. Das gleiche Programm hat einen Link zu einer anderen Bibliothek. Diese Bibliothek möchte auch MyUtils verwenden, jedoch Version 6.0.0.0. MyUtils Version 9.0.0.0 und Version 6.0.0.0 haben PublicKeyToken = null, dh sie haben einen einfachen Namen. Welche Version fällt in das binäre Artefakt 6.0.0.0 oder 9.0.0.0? 9. Version. Kann MyLibrary MyUtils Version 9.0.0.0 verwenden, die in das binäre Artefakt gelangt ist?



Tatsächlich kann dies der Fall sein, da MyUtils einen einfachen Namen hat und dementsprechend das Laden der strengen Assembly nicht vorhanden ist.



Ein anderes Beispiel. Anstelle von MyUtils haben wir eine vollständige Bibliothek von NuGet, die einen starken Namen hat. Die meisten Bibliotheken in NuGet haben einen starken Namen.



In der Build-Phase wird Version 9.0.0.0 in BIN kopiert, aber zur Laufzeit erhalten wir die berühmte FileLoadException. Damit MyLibrary, die Version 6.0.0.0 Newtonsoft.Jsonverwenden soll, Version 9.0.0.0 verwenden kann, müssen Sie die Bindungsumleitung an schreiben App.config.

Bindungsumleitungen







Umleiten von Assemblyversionen Es wird angegeben, dass eine Assembly mit einem solchen Namen und einem solchen publicKeyToken von einem solchen Versionsbereich zu einem solchen Versionsbereich umgeleitet werden muss. Es scheint eine sehr einfache Aufzeichnung zu sein, aber sie befindet sich hier App.config, könnte sich aber in anderen Dateien befinden. In machine.config.NET Framework befindet sich zur Laufzeit eine Datei , in der einige Standardumleitungen definiert sind, die von Version zu Version von .NET Framework unterschiedlich sein können. Es kann vorkommen, dass in 4.7.1 nichts für Sie funktioniert, in 4.7.2 jedoch bereits oder umgekehrt. Sie müssen berücksichtigen, dass Weiterleitungen nicht nur von Ihnen stammen .App.configkönnen. Dies sollte beim Debuggen berücksichtigt werden.

Wir vereinfachen das Schreiben von Weiterleitungen


Niemand möchte verbindliche Weiterleitungen mit den Händen schreiben. Geben wir diese Aufgabe an MSBuild!



Aktivieren und Deaktivieren der automatischen Bindungsumleitung

Einige Tipps zur Vereinfachung der Arbeit mit der Bindungsumleitung. Tipp 1: Aktivieren Sie die automatische Generierung der Bindungsumleitung in MSBuild. Von Eigentum in eingeschaltet *.csproj. Beim Erstellen eines Projekts fällt es in ein binäres Artefakt App.config, das Weiterleitungen zu Versionen von Bibliotheken anzeigt, die sich im selben Artefakt befinden. Dies funktioniert nur zum Ausführen von Anwendungen, Konsolenanwendungen und WinExe. Für Bibliotheken funktioniert dies nicht, da für BibliothekenApp.configMeistens ist es einfach nicht relevant, da es für eine Anwendung relevant ist, die Assemblys selbst startet und lädt. Wenn Sie eine Konfiguration für die Bibliothek vorgenommen haben, können sich in der Anwendung auch einige Abhängigkeiten von denen unterscheiden, die beim Erstellen der Bibliothek vorhanden waren, und es stellt sich heraus, dass die Konfiguration für die Bibliothek nicht sehr sinnvoll ist. Trotzdem sind manchmal für Bibliotheken Konfigurationen immer noch sinnvoll.



Die Situation, wenn wir Tests schreiben. Tests werden normalerweise in ClassLibrary gefunden und benötigen auch Weiterleitungen. Test-Frameworks können erkennen, dass die Bibliothek mit Tests eine DLL-Konfiguration hat, und die darin enthaltenen Weiterleitungen gegen den Code aus den Tests austauschen. Sie können diese Weiterleitungen automatisch generieren. Wenn wir ein altes Format haben*.csprojIm SDK-Stil können Sie auf einfache Weise den OutputType in Exe ändern und einen leeren Einstiegspunkt hinzufügen. Dadurch wird MSBuild gezwungen, Weiterleitungen zu generieren. Sie können den anderen Weg gehen und den Hack verwenden. Sie können eine weitere Eigenschaft hinzufügen *.csproj, sodass MSBuild berücksichtigt, dass für diesen OutputType noch Bindungsumleitungen generiert werden müssen. Obwohl diese Methode wie ein Hack aussieht, können Sie Weiterleitungen für Bibliotheken generieren, die in Exe nicht wiederholt werden können, sowie für andere Arten von Projekten (außer Tests).

Für das neue Format werden *.csprojWeiterleitungen selbst generiert, wenn Sie modernes Microsoft.NET.Test.Sdk verwenden.

Dritter Tipp: Verwenden Sie die Bindungsumleitungsgenerierung nicht mit NuGet. NuGet bietet die Möglichkeit, eine Bindungsumleitung für Bibliotheken zu generieren, die von Paketen auf die neuesten Versionen übertragen werden. Dies ist jedoch nicht die beste Option. Alle diese Weiterleitungen müssen hinzugefügt App.configund festgeschrieben werden. Wenn Sie mit MSBuild Weiterleitungen generieren, werden während der Erstellung Weiterleitungen generiert. Wenn Sie sie festschreiben, können Zusammenführungskonflikte auftreten. Sie können einfach vergessen, die Bindungsumleitung in der Datei zu aktualisieren, und wenn sie während des Builds generiert werden, werden Sie nicht vergessen.



Baugruppenreferenz
auflösen Bindungsumleitungen generieren

Hausaufgaben für diejenigen, die besser verstehen möchten, wie die Generierung von Bindungsumleitungen funktioniert: Finden Sie heraus, wie es funktioniert, lesen Sie dies im Code. Wechseln Sie in das .NET-Verzeichnis und stoßen Sie überall auf die Eigenschaft name, mit der die Generierung aktiviert wird. Dies ist im Allgemeinen ein so gängiger Ansatz. Wenn es eine seltsame Eigenschaft für MSBuild gibt, können Sie die Verwendung nutzen. Glücklicherweise wird Eigenschaft normalerweise in XML-Konfigurationen verwendet, und Sie können ihre Verwendung leicht finden.

Wenn Sie untersuchen, was in diesen XML-Zielen enthalten ist, werden Sie feststellen, dass diese Eigenschaft zwei MSBuild-Aufgaben auslöst. Die erste Aufgabe wird aufgerufen ResolveAssemblyReferencesund generiert eine Reihe von Weiterleitungen, die in Dateien geschrieben werden. Die zweite Aufgabe GenerateBindingRedirectsschreibt die Ergebnisse der ersten Aufgabe inApp.config. Es gibt eine XML-Logik, die den Vorgang der ersten Aufgabe leicht korrigiert und unnötige Weiterleitungen entfernt oder neue hinzufügt.

Alternative zu XML-Konfigurationen


Es ist nicht immer bequem, Weiterleitungen in der XML-Konfiguration beizubehalten. Es kann vorkommen, dass die Anwendung das Plugin herunterlädt und dieses Plugin andere Bibliotheken verwendet, für die Weiterleitungen erforderlich sind. In diesem Fall sind uns möglicherweise die benötigten Weiterleitungen nicht bekannt, oder wir möchten möglicherweise kein XML generieren. In einer solchen Situation können wir eine AppDomain erstellen und beim Erstellen immer noch dorthin übertragen, wo sich das XML mit den erforderlichen Weiterleitungen befindet. Wir können Fehler beim Laden von Baugruppen auch direkt zur Laufzeit behandeln. Rantime .NET bietet eine solche Möglichkeit.

AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) => 
{ 
   var name = eventArgs.Name; 
   var requestingAssembly = eventArgs.RequestingAssembly; 
   
   return Assembly.LoadFrom(...); // PublicKeyToken should be equal
};


Es hat ein Ereignis, es heißt CurrentDomain.AssemblyResolve. Wenn Sie dieses Ereignis abonnieren, erhalten wir Fehler zu allen fehlgeschlagenen Assembly-Downloads. Wir erhalten den Namen der Baugruppe, die nicht geladen wurde, und wir erhalten die Baugruppenbaugruppe, die das Laden der ersten Baugruppe angefordert hat. Hier können wir die Assembly manuell von der richtigen Stelle laden, z. B. die Version löschen, sie einfach aus der Datei entnehmen und dieses Ereignis vom Handler zurückgeben. Oder geben Sie null zurück, wenn wir nichts zurückgeben müssen, wenn wir die Assembly nicht laden können. PublicKeyToken sollte gleich sein, Assemblys mit unterschiedlichen PublicKeyToken sind in keiner Weise miteinander befreundet.



Dieses Ereignis gilt nur für eine Anwendungsdomäne. Wenn unser Plugin eine AppDomain in sich selbst erstellt, funktioniert diese Umleitung in der Laufzeit in ihnen nicht. Sie müssen dieses Ereignis in der gesamten AppDomain abonnieren, die das Plugin erstellt hat. Wir können dies mit dem AppDomainManager tun.

AppDomainManager ist eine separate Assembly, die eine Klasse enthält, die eine bestimmte Schnittstelle implementiert. Mit einer der Methoden dieser Schnittstelle können Sie jede neue AppDomain initialisieren, die in der Anwendung erstellt wird. Sobald die AppDomain erstellt wurde, wird diese Methode aufgerufen. Darin können Sie dieses Ereignis abonnieren.

Striktes Laden von Assemblys und .NET Core


In .NET Core gibt es kein Problem namens "Striktes Laden von Assemblys". Dies liegt daran, dass für signierte Assemblys genau die angeforderte Version erforderlich ist. Es gibt noch eine andere Anforderung. Unabhängig davon, ob sie mit einem starken Namen signiert sind oder nicht, wird für alle Assemblys überprüft, ob die zur Laufzeit geladene Version größer oder gleich der vorherigen ist. Wenn wir uns in einer Situation mit einer Anwendung mit Plugins befinden, kann es vorkommen, dass das Plugin beispielsweise aus einer neuen Version des SDK erstellt wurde und die Anwendung, in die es heruntergeladen wird, die bisherige alte Version des SDK verwendet und nicht auseinanderfällt. Wir können dieses Ereignis auch abonnieren, jedoch bereits in .NET Core, und die vorhandene Assembly laden. Wir können diesen Code schreiben:
AppDomain.CurrentDomain.AssemblyResolve += (s, eventArgs) => 
{ 
     CheckForRecursion(); 
     var name = eventArgs.Name;
     var requestingAssembly = eventArgs.RequestingAssembly; 
    
     name.Version = new Version(0, 0); 
     
     return Assembly.Load(name); 
};


Wir haben den Namen der Assembly, die nicht gestartet wurde. Wir machen die Version ungültig und rufen sie Assembly.Loadvon derselben Version aus auf. Hier findet keine Rekursion statt, da ich die Rekursion bereits überprüft habe.



Es war notwendig, MyUtils Version 0.0.2.0 herunterzuladen. In BIN haben wir MyUtils Version 0.0.1.0. Wir haben eine Umleitung von Version 0.0.2.0 zu Version 0.0 durchgeführt. Version 0.0.1.0 wird bei uns nicht geladen. Ein Exit wird für uns herausfliegen, dass es nicht möglich war, die Assembly mit Version 0.0.2 16–1 zu laden . 2 16–1 .

new Version(0, 0) == new Version(0, 0, -1, -1) 

class Version { 
     readonly int _Build; 
     readonly int _Revision; 
     readonly int _Major; 
     readonly int _Minor; 
} 
(ushort) -1 == 65535


In der Versionsklasse sind nicht alle Komponenten obligatorisch, und anstelle der optionalen Komponenten –1 werden sie gespeichert. Irgendwo im Inneren tritt jedoch ein Überlauf auf, und es werden genau die 2 16–1 erhalten . Bei Interesse können Sie versuchen, genau herauszufinden, wo der Überlauf auftritt.



Wenn Sie mit Reflection-Assemblys arbeiten und alle Typen abrufen möchten, kann es sein, dass nicht alle Typen Ihre GetTypes-Methode erhalten können. Eine Assembly hat eine Klasse, die von einer anderen Klasse erbt, die sich in einer Assembly befindet, die nicht geladen ist.

static IEnumerable GetTypesSafe(this Assembly assembly) 
{ 
    try 
    { 
        return assembly.GetTypes(); 
    }
    catch (ReflectionTypeLoadException e) 
   { 
        return e.Types.Where(x => x != null); 
    } 
}



In diesem Fall besteht das Problem darin, dass eine ReflectionTypeLoadException ausgelöst wird. Im Inneren ReflectionTypeLoadExceptionbefindet sich eine Eigenschaft, in die sich die Typen befinden, die noch geladen werden konnten. Nicht alle gängigen Bibliotheken berücksichtigen dies. AutoMapper, mindestens eine seiner Versionen, fiel bei ReflectionTypeLoadException einfach aus, anstatt die Typen aus dem Inneren der Ausnahme auszuwählen.

Starke Benennung


Baugruppen mit starken Namen

Lassen Sie uns darüber sprechen, was das Laden strenger Baugruppen verursacht. Dies ist ein starker Name.
Starker Name ist die Signatur der Assembly durch einen privaten Schlüssel mit asymmetrischer Verschlüsselung. PublicKeyToken ist der Public-Key-Hash dieser Assembly.

Mit Strong Naming können Sie zwischen verschiedenen Assemblys mit demselben Namen unterscheiden. Zum Beispiel ist MyUtils kein eindeutiger Name. Es kann mehrere Assemblys mit diesem Namen geben. Wenn Sie jedoch Strong Name signieren, haben diese unterschiedliche PublicKeyToken und wir können sie auf diese Weise unterscheiden. Für einige Assembly-Ladeszenarien ist ein starker Name erforderlich.

Zum Beispiel, um eine Assembly im globalen Assemblycache zu installieren oder mehrere Versionen von nebeneinander gleichzeitig herunterzuladen. Am wichtigsten ist, dass stark benannte Assemblys nur auf andere stark benannte Assemblys verweisen können. Da einige Benutzer ihre Builds mit einem starken Namen signieren möchten, signieren die Bibliotheksentwickler auch ihre Bibliotheken, damit Benutzer sie einfacher installieren können und die Benutzer diese Bibliotheken nicht erneut signieren müssen.

Starker Name: Vermächtnis?


Starke Namens- und .NET-Bibliotheken

Microsoft sagt in MSDN ausdrücklich, dass Sie aus Sicherheitsgründen keinen starken Namen verwenden sollten, sondern nur, um verschiedene Assemblys mit demselben Namen zu unterscheiden. Der Assembly-Schlüssel kann in keiner Weise geändert werden. Wenn Sie ihn geändert haben, werden die Weiterleitungen an alle Benutzer unterbrochen. Wenn Sie einen privaten Teil des Schlüssels für den Namen "Stark" für den öffentlichen Zugriff verloren haben, können Sie diese Signatur in keiner Weise zurückziehen. Das SNK-Dateiformat, in dem sich der starke Name befindet, bietet keine solche Möglichkeit, und andere Formate zum Speichern von Schlüsseln enthalten mindestens einen Link zur Sperrliste für CRL-Zertifikate, anhand derer verstanden werden kann, dass dieses Zertifikat nicht mehr gültig ist. In SNK gibt es nichts Vergleichbares.

Das Open-Source-Handbuch enthält die folgenden Empfehlungen. Erstens, zusätzlich zu Sicherheitszwecken andere Technologien verwenden. Zweitens wird bei einer Open-Source-Bibliothek im Allgemeinen empfohlen, den privaten Teil des Schlüssels in das Repository zu übernehmen, damit die Benutzer Ihre Bibliothek leichter aufteilen, neu erstellen und in eine vorgefertigte Anwendung einfügen können. Drittens ändern Sie niemals den starken Namen. Zu destruktiv. Trotz der Tatsache, dass es zu destruktiv ist und im Open-Source-Handbuch darüber geschrieben wird, hat Microsoft manchmal Probleme mit seinen eigenen Bibliotheken.



Es gibt eine Bibliothek namens System.Reactive. Bisher waren dies mehrere NuGet-Pakete, eines davon ist Rx-Linq. Dies ist nur ein Beispiel, das auch für den Rest der Pakete gilt. In der zweiten Version wurde es mit einem Microsoft-Schlüssel signiert. In der dritten Version wechselte er in das Repository des Projekts github.com/dotnet und erhielt eine .NET Foundation-Signatur. Die Bibliothek hat tatsächlich den Namen Strong geändert. Das NuGet-Paket wurde umbenannt, aber die Assembly wird im Inneren genauso wie zuvor aufgerufen. Wie leite ich von der zweiten Version zur dritten um? Diese Weiterleitung kann nicht durchgeführt werden.

Starke Namensvalidierung


Gewusst wie: Deaktivieren Sie die Umgehungsfunktion für starke Namen

Ein weiteres Argument, dass Strong Name bereits der Vergangenheit angehört und rein formal bleibt, ist, dass sie nicht validiert werden. Wir haben eine signierte Assembly und möchten einen Fehler darin beheben, haben jedoch keinen Zugriff auf die Quellen. Wir können einfach dnSpy verwenden - dies ist ein Dienstprogramm, mit dem Sie bereits kompilierte Assemblys dekompilieren und reparieren können. Alles wird für uns funktionieren. Da standardmäßig die Umgehung der Überprüfung starker Namen aktiviert ist, wird nur überprüft, ob das PublicKeyToken gleich ist, und die Integrität der Signatur selbst wird nicht überprüft. Möglicherweise gibt es Umweltstudien, in denen die Signatur noch überprüft wird, und hier ist IIS ein anschauliches Beispiel. Die Signaturintegrität wird in IIS überprüft (die Umgehung der Validierung starker Namen ist standardmäßig deaktiviert), und alles wird unterbrochen, wenn wir die signierte Assembly bearbeiten.

Zusatz:Sie können die Signaturüberprüfung für die Assembly mithilfe des öffentlichen Zeichens deaktivieren . Damit wird nur der öffentliche Schlüssel zum Signieren verwendet, was die Sicherheit des Assemblynamens gewährleistet. Die von Microsoft verwendeten öffentlichen Schlüssel werden hier veröffentlicht .
In Rider kann das öffentliche Zeichen in den Projekteigenschaften aktiviert werden.





Wann müssen die Versionen der Dateibaugruppe geändert werden?

Das Open-Source-Handbuch bietet auch einige Versionsrichtlinien, mit denen die Anzahl der erforderlichen Bindungsumleitungen und Änderungen für Benutzer im NET Framework verringert werden soll. Diese Versionsrichtlinie besagt, dass wir die Assembly-Version nicht ständig ändern sollten. Dies kann natürlich zu Problemen bei der Installation im GAC führen, sodass das installierte native Image möglicherweise nicht der Assembly entspricht und Sie die JIT-Kompilierung erneut durchführen müssen. Meiner Meinung nach ist dies jedoch weniger schlimm als die Probleme bei der Versionierung. Bei CrossGen werden native Assemblys nicht global installiert - es treten keine Probleme auf.

Das NuGet-Paket Newtonsoft.Json verfügt beispielsweise über mehrere Versionen: 12.0.1, 12.0.2 usw. Alle diese Pakete verfügen über eine Assembly mit Version 12.0.0.0. Die Empfehlung lautet, dass die Assembly-Version aktualisiert werden sollte, wenn sich eine Hauptversion des NuGet-Pakets ändert.

Ergebnisse


Befolgen Sie die Tipps für .NET Framework: Generieren Sie Weiterleitungen manuell und versuchen Sie, in allen Projekten Ihrer Lösung dieselbe Version von Abhängigkeiten zu verwenden. Dies sollte die Anzahl der Weiterleitungen erheblich minimieren. Sie benötigen eine starke Benennung nur, wenn Sie ein bestimmtes Build-Ladeszenario haben, in dem es benötigt wird, oder wenn Sie eine Bibliothek entwickeln und das Leben für Benutzer vereinfachen möchten, die eine starke Benennung wirklich benötigen. Ändern Sie nicht den starken Namen.

.NET Standard


Wir gehen zu .NET Standard über. Es ist ziemlich eng mit der Version Hell in .NET Framework verwandt. .NET Standard ist ein Tool zum Schreiben von Bibliotheken, die mit verschiedenen Implementierungen der .NET-Plattform kompatibel sind. Implementierungen beziehen sich auf .NET Framework, .NET Core, Mono, Unity und Xamarin.



* Link zur Dokumentation

Dies ist die .NET Standard-Unterstützungstabelle für verschiedene Versionen verschiedener Versionen von Laufzeiten. Und hier können wir sehen, dass .NET Framework die .NET Standard Version 2.1 in keiner Weise unterstützt. Die Veröffentlichung von .NET Framework, das .NET Standard 2.1 und höher unterstützt, ist noch nicht geplant. Wenn Sie eine Bibliothek entwickeln und möchten, dass sie für Benutzer in .NET Framework funktioniert, müssen Sie ein Ziel für .NET Standard 2.0 haben. Neben der Tatsache, dass .NET Framework die neueste Version des .NET-Standards nicht unterstützt, sollten wir auf das Sternchen achten. .NET Framework 4.6.1 unterstützt .NET Standard 2.0, jedoch mit einem Sternchen. Es gibt eine solche Fußnote direkt in der Dokumentation, woher ich diese Tabelle habe.



Betrachten Sie ein Beispielprojekt. Eine Anwendung in .NET Framework mit einer Abhängigkeit für den .NET-Standard. So etwas wie das: ConsoleApp und ClassLibrary. Zielbibliothek .NET Standard. Wenn wir dieses Projekt zusammenstellen, wird es in unserem BIN so sein.



Wir werden dort hundert DLLs haben, von denen sich nur eine auf die Anwendung bezog, alles andere kam, um den .NET-Standard zu unterstützen. Tatsache ist, dass .NET Standard 2.0 später als .NET Framework 4.6.1 erschien, sich jedoch gleichzeitig als API-kompatibel herausstellte und die Entwickler beschlossen, .NET 4.6.1 um Standard 2.0-Unterstützung zu erweitern. Wir haben es nicht nativ gemacht (durch Aufnahme netstandard.dllin die Laufzeit selbst), sondern so, dass .NET Standard * .dll und alle anderen Baugruppenfassaden direkt in BIN platziert werden.



Wenn wir uns die Abhängigkeiten der Version von .NET Framework ansehen, auf die wir abzielen, und die Anzahl der Bibliotheken, die in die BIN fallen, werden wir feststellen, dass es in 4.7.1 nicht so viele davon gibt, und seit 4.7.2 gibt es überhaupt keine zusätzlichen Bibliotheken und .NET Standard wird dort nativ unterstützt.



Dies ist ein Tweet von einem der .NET-Entwickler, der dieses Problem beschreibt und die Verwendung von .NET Framework Version 4.7.2 empfiehlt, wenn wir über .NET Standard-Bibliotheken verfügen. Nicht einmal mit Version 2.0 hier, sondern mit Version 1.5.

Ergebnisse


Wenn möglich, erhöhen Sie das Ziel-Framework in Ihrem Projekt auf mindestens 4.7.1, vorzugsweise 4.7.2. Wenn Sie eine Bibliothek entwickeln, um Bibliotheksbenutzern das Leben zu erleichtern, erstellen Sie ein separates Ziel für .NET Framework. Dadurch wird eine große Anzahl von DLLs vermieden, die mit etwas in Konflikt stehen können.

.NET Core


Beginnen wir mit einer allgemeinen Theorie. Wir werden diskutieren, wie wir JetBrains Rider auf .NET Core gestartet haben und warum wir überhaupt darüber sprechen sollten. Rider ist ein sehr großes Projekt, es hat eine riesige Unternehmenslösung mit einer großen Anzahl verschiedener Projekte, ein komplexes System von Abhängigkeiten. Sie können es nicht einfach nehmen und gleichzeitig auf eine andere Laufzeit migrieren. Dazu müssen wir einige Hacks verwenden, die wir auch analysieren werden.

.NET Core-Anwendung


Wie sieht eine typische .NET Core-Anwendung aus? Hängt davon ab, wie genau es bereitgestellt wird und was es letztendlich tun wird. Wir können mehrere Szenarien haben. Die erste ist eine Framework-abhängige Bereitstellung. Dies ist dasselbe wie in .NET Framework, wenn die Anwendung die auf dem Computer vorinstallierte Laufzeit verwendet. Es kann sich um eine eigenständige Bereitstellung handeln. In diesem Fall verfügt die Anwendung über eine Laufzeit. Möglicherweise gibt es eine Bereitstellung mit nur einer Datei. In diesem Fall erhalten wir eine Exe-Datei. Im Fall von .NET Core in dieser Exe-Datei befindet sich jedoch ein Artefakt einer in sich geschlossenen Anwendung. Dies ist ein selbstextrahierendes Archiv.



Wir werden nur die Framework-abhängige Bereitstellung berücksichtigen. Wir haben eine DLL mit der Anwendung, es gibt zwei Konfigurationsdateien, von denen die erste erforderlich ist, diese runtimeconfig.jsonunddeps.json. Ab .NET Core 3.0 wird eine exe-Datei generiert, die erforderlich ist, damit die Anwendung bequemer ausgeführt werden kann, sodass Sie unter Windows nicht den Befehl .NET eingeben müssen. Abhängigkeiten fallen in dieses Artefakt, beginnend mit .NET Core 3.0. In .NET Core 2.1 müssen Sie eine andere Eigenschaft veröffentlichen oder verwenden *.csproj.

Gemeinsame Frameworks, .runtimeconfig.json





.runtimeconfig.jsonenthält die Laufzeiteinstellungen, die zum Ausführen erforderlich sind. Es gibt an, unter welchem ​​Shared Framework die Anwendung gestartet wird, und es sieht so aus. Wir weisen darauf hin, dass die Anwendung unter "Microsoft.NETCore.App" Version 3.0.0 ausgeführt wird. Möglicherweise gibt es ein anderes freigegebenes Framework. Hier können auch andere Einstellungen vorgenommen werden. Sie können beispielsweise den Server Garbage Collector aktivieren.



.runtimeconfig.jsonwährend der Montage des Projekts generiert. Und wenn wir den Server-GC einbinden möchten, müssen wir diese Datei im Voraus irgendwie ändern, noch bevor wir das Projekt zusammenstellen oder von Hand hinzufügen. Hier können Sie Ihre Einstellungen hinzufügen. Wir können entweder Eigenschaften in aufnehmen *.csproj, wenn diese Eigenschaften von .NET-Entwicklern bereitgestellt werden, oder wenn Eigenschaften nicht bereitgestellt werden, können wir eine Datei mit dem Namen erstellenruntimeconfig.template.jsonund schreiben Sie hier die notwendigen Einstellungen. Während der Montage werden dieser Vorlage weitere erforderliche Einstellungen hinzugefügt, z. B. dasselbe Shared Framework.



Das Shared Framework besteht aus einer Reihe von Laufzeitprogrammen und Bibliotheken. In der Tat war das gleiche wie die .NET Framework-Laufzeit, die früher nur einmal auf dem Computer installiert wurde und für alle eine Version war. Shared Framework und im Gegensatz zu einer einzelnen .NET Framework-Laufzeit können verschiedene Anwendungen unterschiedliche Versionen der installierten Laufzeiten verwenden. Auch Shared Framework kann vererbt werden. Das Shared Framework selbst kann an solchen Stellen auf der Festplatte angezeigt werden, die normalerweise auf dem System installiert sind.



Es gibt mehrere Standard-Shared Frameworks, z. B. Microsoft.NETCore.App, auf dem herkömmliche Konsolenanwendungen ausgeführt werden, AspNetCore.App für Webanwendungen und WindowsDesktop.App, das neue Shared Framework in .NET Core 3, auf dem Desktopanwendungen ausgeführt werden. unter Windows Forms und WPF. Die letzten beiden Shared Frameworks ergänzen im Wesentlichen das erste, das für Konsolenanwendungen benötigt wird, dh sie enthalten keine ganz neue Laufzeit, sondern ergänzen einfach das vorhandene mit den erforderlichen Bibliotheken. Diese Vererbung scheint auch in den Shared Framework-Verzeichnissen vorhanden zu sein, runtimeconfig.jsonin denen das Basis-Shared Framework angegeben ist.

Abhängigkeitsmanifest ( .deps.json)



Standardprüfung - .NET Core Die

zweite Konfigurationsdatei ist diese .deps.json. Diese Datei enthält eine Beschreibung aller Abhängigkeiten der Anwendung oder des Shared Framework oder der Bibliothek, die die Bibliotheken .deps.jsonauch haben. Es enthält alle Abhängigkeiten, einschließlich der transitiven. Das Verhalten der .NET Core-Laufzeit hängt davon ab, ob .deps.jsondie Anwendung über diese verfügt oder nicht. Wenn .deps.jsonnicht, kann die Anwendung alle Assemblys laden, die sich in ihrem Shared Framework oder in ihrem BIN-Verzeichnis befinden. Wenn dies der Fall .deps.jsonist, ist die Validierung aktiviert. Wenn eine der in aufgelisteten Assemblys .deps.jsonnicht vorhanden ist, wird die Anwendung einfach nicht gestartet. Sie sehen den oben dargestellten Fehler. Wenn die Anwendung versucht, eine Assembly zur Laufzeit zu laden, welche.deps.json Wenn Sie beispielsweise Assembly-Lademethoden verwenden oder während des Auflösungsprozesses von Assemblys, wird ein Fehler angezeigt, der dem Laden strenger Assemblys sehr ähnlich ist.

Jetbrains Fahrer


Rider ist eine .NET IDE. Nicht jeder weiß, dass Rider eine IDE ist, die aus einem Frontend, das auf IntelliJ IDEA basiert und in Java und Kotlin geschrieben ist, und einem Backend besteht. Das Backend ist im Wesentlichen R #, das mit IntelliJ IDEA kommunizieren kann. Dieses Backend ist jetzt eine plattformübergreifende .NET-Anwendung.
Wo läuft es? Windows verwendet das .NET Framework, das auf dem Computer des Benutzers installiert ist. Auf anderen Informationssystemen unter Linux und Mac wird Mono verwendet.

Dies ist keine ideale Lösung, wenn es überall unterschiedliche Laufzeiten gibt, und ich möchte zum nächsten Status übergehen, damit Rider auf .NET Core ausgeführt wird. Um die Leistung zu verbessern, sind in .NET Core alle neuesten Funktionen damit verbunden. Speicherverbrauch reduzieren. Jetzt gibt es ein Problem damit, wie Mono mit dem Speicher arbeitet.

Wenn Sie zu .NET Core wechseln, können Sie ältere, nicht unterstützte Technologien aufgeben und einige Korrekturen für die zur Laufzeit festgestellten Probleme beheben. Wenn Sie zu .NET Core wechseln, können Sie die Version der Laufzeit steuern. Das heißt, Rider wird nicht mehr auf dem auf dem Computer des Benutzers installierten .NET Framework ausgeführt, sondern auf einer bestimmten Version von .NET Core, die als eigenständige Bereitstellung gesperrt werden kann. Der Übergang zu .NET Core ermöglicht schließlich die Verwendung neuer APIs, die speziell in Core importiert werden.

Jetzt ist das Ziel, einen Prototyp zu starten, ihn zu starten, nur um zu überprüfen, wie er funktioniert, was die potenziellen Fehlerquellen sind, welche Komponenten erneut neu geschrieben werden müssen und welche globale Verarbeitung erforderlich ist.

Funktionen, die die Übersetzung von Rider in .NET Core erschweren


Visual Studio stürzt bei großen Lösungen, in denen sich Projekte mit SDK-Stil * .csproj befinden , aus dem Arbeitsspeicher ab , auch wenn R # nicht darin installiert ist . SD. -Stil * .csproj ist eine der Hauptbedingungen für eine vollständige .NET Core-Verlagerung.

Dies ist ein Problem, da Rider auf R # basiert und sich im selben Repository befindet. R # -Entwickler möchten mit Visual Studio ihr eigenes Produkt in ihrem Produkt entwickeln, um daraus Lebensmittel zu machen. In R # gibt es Links zu bestimmten Bibliotheken für das Framework, mit denen Sie etwas tun müssen. Unter Windows können wir das Framework für Desktop-Anwendungen verwenden, und unter Linux und Mac wird Mock bereits für Windows-Bibliotheken mit minimaler Funktionalität verwendet.

Entscheidung


Wir haben uns entschlossen, vorerst auf den alten zu bleiben *.csprojund unter dem vollständigen Framework zusammenzustellen. Da die Assemblys von Framework und Core jedoch binär kompatibel sind, führen Sie sie auf Core aus. Wir verwenden keine inkompatiblen Funktionen, fügen alle erforderlichen Konfigurationsdateien manuell hinzu und laden ggf. spezielle Versionen von Abhängigkeiten für .NET Core herunter.

Zu welchen Hacks mussten Sie gehen?


Ein Hack: Wir möchten eine Methode aufrufen, die nur im Framework verfügbar ist. Diese Methode wird beispielsweise in R # benötigt, nicht jedoch in Core. Das Problem ist, dass, wenn es keine Methode gibt, die Methode, die sie während der JIT-Kompilierung aufruft, früher fällt MissingMethodException. Das heißt, eine Methode, die nicht existiert, hat die Methode, die sie aufruft, ruiniert.

static void Method() { 
  if (NetFramework) 
     CallNETFrameworkOnlyMethod();

  ... 
} 
[MethodImpl(MethodImplOptions.NoInlining)] 
static void CallNETFrameworkOnlyMethod() { 
  NETFrameworkOnlyMethod(); 
}


Die Lösung ist hier: Wir rufen inkompatible Methoden in separate Methoden auf. Es gibt noch ein weiteres Problem: Eine solche Methode kann inline werden, daher markieren wir sie mit einem Attribut NoInlining.

Hack Nummer zwei: Wir müssen in der Lage sein, Assemblys in relativen Pfaden zu laden. Wir haben eine Assembly für das Framework, es gibt eine spezielle Version für .NET Core. Wie laden wir die .NET Core-Version für .NET Core herunter?



Sie werden uns helfen .deps.json. Schauen wir uns .deps.jsondie System.Diagnostics.PerformanceCounter-Bibliothek an. Eine solche Bibliothek ist bemerkenswert in Bezug auf ihre.deps.json. Es hat einen Laufzeitabschnitt, in dem eine Version der Bibliothek mit ihrem relativen Pfad angegeben ist. In dieser Bibliothek wird die Assembly zu allen Laufzeiten geladen und nur die Ausführungen ausgelöst. Wenn es beispielsweise unter Linux geladen wird, funktioniert der PerformanceCounter unter Linux nicht mit Design, und von dort aus fliegt eine PlatformNotSupportedException. In diesem .deps.jsonAbschnitt befindet sich auch ein Abschnitt zu runtimeTargets. Hier ist bereits die Version dieser Assembly speziell für Windows angegeben, in der PerformanceCounter funktionieren soll.

Wenn wir den Laufzeitabschnitt nehmen und darin den relativen Pfad zu der Bibliothek schreiben, die wir laden möchten, hilft uns dies nicht weiter. Der Laufzeitabschnitt legt tatsächlich den relativen Pfad innerhalb des NuGet-Pakets fest und nicht relativ zur BIN. Wenn wir diese Assembly in BIN suchen, wird nur der Dateiname von dort verwendet. Der Abschnitt runtimeTargets enthält bereits einen ehrlichen relativen Pfad, einen ehrlichen Pfad relativ zu BIN. Wir werden einen relativen Pfad für unsere Assemblys im Abschnitt runtimeTargets vorgeben. Anstelle der Laufzeitkennung, die hier "win" ist, können wir eine andere nehmen, die uns gefällt. Zum Beispiel schreiben wir die Laufzeitkennung "any" und diese Assembly wird im Allgemeinen auf allen Plattformen geladen. Oder wir schreiben "Unix" und es bootet unter Linux, Mac und so weiter.

Nächster Hack: Wir möchten unter Linux und Mac Mock herunterladen, um WindowsBase zu erstellen. Das Problem ist, dass die Assembly mit dem Namen WindowsBase bereits im Shared Framework vorhanden ist Microsoft.NETCore.App, auch wenn wir nicht unter Windows arbeiten. In Windows Shared Framework Microsoft.WindowsDesktop.Appdefiniert WindowsBase die Version neu, in der es sich befindet NETCore.App. Schauen wir uns .deps.jsondieses Framework genauer an, genauer gesagt die Abschnitte, die WindowsBase beschreiben.



Hier ist der Unterschied:



Wenn einige Bibliothekskonflikte auftreten und in mehreren vorhanden sind .deps.json, wird das Maximum für das Paar ausgewählt, das aus assemblyVersionund besteht fileVersion. Das .NET-Handbuch besagt, dass fileVersiones nur benötigt wird, um es im Windows Explorer anzuzeigen, aber nicht, es fällt in.deps.json. Dies ist der einzige Fall , dass ich weiß, wann die Version vorgeschriebenen .deps.json, assemblyVersionund fileVersion, tatsächlich verwendet werden. In allen anderen Fällen stellte ich ein Verhalten fest .deps.json, bei dem die Assembly trotzdem geladen wird , unabhängig davon, in welchen Versionen sie geschrieben wurden.



Vierter Hack. Aufgabe: Wir haben eine .deps.json-Datei für die beiden vorherigen Hacks und benötigen sie nur für bestimmte Abhängigkeiten. Da sie .deps.jsonim halbmanuellen Modus generiert werden, haben wir ein Skript, das sie gemäß einer Beschreibung dessen, was dort ankommen soll, während des Builds generiert. Wir möchten dies so .deps.jsongering wie möglich halten, damit wir verstehen können, was darin enthalten ist. Wir möchten die Validierung deaktivieren und den Download von Assemblys zulassen, die sich in der BIN befinden, aber nicht in beschrieben sind .deps.json.

Lösung: Aktivieren Sie die benutzerdefinierte Konfiguration in runtimeconfig. Diese Einstellung wird tatsächlich für die Abwärtskompatibilität mit .NET Core 1.0 benötigt.

Ergebnisse


Also, .runtime.jsonund .deps.jsonauf .NET Core - das sind Analoga App.config. App.configSie können die gleichen Aktionen ausführen, z. B. Baugruppen auf relative Weise laden. Wenn Sie .deps.jsones manuell umschreiben, können Sie das Laden von Assemblys in .NET Core anpassen, wenn Sie ein sehr komplexes Szenario haben.

Debug-Assembly-Downloads


Ich habe über einige Arten von Problemen gesprochen, daher müssen Sie in der Lage sein, Probleme beim Laden von Assemblys zu debuggen. Was kann dabei helfen? Zunächst schreiben Laufzeiten Protokolle darüber, wie sie Assemblys laden. Zweitens können Sie sich die Ausführungen, die zu Ihnen fliegen, genauer ansehen. Sie können sich auch auf Laufzeitereignisse konzentrieren.

Fusionsprotokolle





Zurück zu den Grundlagen: Verwenden des Fusion Log Viewer zum Debuggen obskurer Fehler
Fusion

Der Mechanismus zum Laden von Assemblys in .NET Framework heißt Fusion und kann protokollieren, was auf der Festplatte geschehen ist. Um die Protokollierung zu aktivieren, müssen Sie der Registrierung spezielle Einstellungen hinzufügen. Dies ist nicht sehr praktisch, daher ist es sinnvoll, Dienstprogramme zu verwenden, nämlich Fusion Log Viewer und Fusion ++. Fusion Log Viewer ist ein Standarddienstprogramm, das mit Visual Studio geliefert wird und über die Visual Studio-Befehlszeile Visual Studio Developer Command Prompt gestartet werden kann. Fusion ++ ist ein Open-Source-Analogon dieses Tools mit einer schöneren Oberfläche.



Der Fusion Log Viewer sieht so aus. Dies ist schlimmer als WinDbg, da sich dieses Fenster nicht einmal ausdehnt. Trotzdem können Sie hier die Häkchen durchstechen, obwohl nicht immer klar ist, welche Häkchen korrekt sind.



Fusion ++ verfügt über eine Schaltfläche "Protokollierung starten". Anschließend wird die Schaltfläche "Protokollierung beenden" angezeigt. Darin können Sie alle Datensätze zum Laden von Assemblys anzeigen und die Protokolle darüber lesen, was genau passiert ist. Diese Protokolle sehen auf prägnante Weise ungefähr so ​​aus.



Dies ist eine Ausnahme vom strengen Laden von Baugruppen. Wenn wir uns die Fusion-Protokolle ansehen, werden wir feststellen, dass wir Version 9.0.0.0 herunterladen mussten, nachdem wir alle Konfigurationen verarbeitet haben. Wir haben eine Datei gefunden, in der vermutet wird, dass wir die Baugruppe haben, die wir benötigen. Wir haben gesehen, dass sich Version 6.0.0.0 in dieser Datei befindet. Wir haben eine Warnung, dass wir die vollständigen Namen der Baugruppen verglichen haben und sie sich in der Hauptversion unterscheiden. Und dann ist ein Fehler aufgetreten - Versionsinkongruenz.

Laufzeitereignisse





Protokollieren von Laufzeitereignissen

In Mono können Sie die Protokollierung mithilfe von Umgebungsvariablen aktivieren. Die Protokolle werden schließlich in stdoutund geschrieben stderr. Nicht so bequem, aber die Lösung funktioniert.



Standardprüfung - .NET Core-
Dokumentation / Entwurfsdokumente / Host-Ablaufverfolgung

. .NET Core verfügt außerdem über eine spezielle Umgebungsvariable, COREHOST_TRACEdie die Anmeldung umfasst stderr. Mit .NET Core 3.0 können Sie Protokolle in eine Datei schreiben, indem Sie den Pfad dazu in einer Variablen angeben COREHOST_TRACEFILE.


Es gibt ein Ereignis, das ausgelöst wird, wenn die Assemblys nicht geladen werden können. Dies ist eine Veranstaltung AssembleResolve. Es gibt ein zweites nützliches Ereignis, dieses FirstChanceException. Sie können es abonnieren und eine Fehlermeldung beim Laden von Assemblys erhalten, selbst wenn jemand try..catch geschrieben und alle Ausführungen an der Stelle verpasst hat, an derFileLoadExceptionaufgetreten. Wenn die Anwendung bereits kompiliert wurde, können Sie sie starten perfviewund .NET-Ausführungen überwachen. Dort finden Sie diejenigen, die sich auf Download-Dateien beziehen.

Ergebnisse


Übertragen Sie die Arbeit auf Tools, Entwicklungstools, IDE und MSBuild, mit denen Sie Weiterleitungen generieren können. Sie können zu .NET Core wechseln, dann werden Sie vergessen, was Strict Assembly Loading ist, und Sie können die neue API genau so verwenden, wie wir es in Rider erreichen möchten. Wenn Sie die .NET Standard-Bibliothek verbinden, erhöhen Sie die Zielversion von .NET Framework auf mindestens 4.7.1. Wenn Sie sich in einer hoffnungslosen Situation befinden, suchen Sie nach Hacks, verwenden Sie sie oder entwickeln Sie Ihre eigenen Hacks für hoffnungslose Situationen. Und rüsten Sie sich mit Debugging-Tools aus.

Ich empfehle Ihnen dringend, die folgenden Links zu lesen:



DotNext 2020 Piter . , 8 JUG Ru Group.

All Articles