Informacje o pliku System.Runtime.Loader.AssemblyLoadContext
Klasa została wprowadzona AssemblyLoadContext na platformie .NET Core i nie jest dostępna w programie .NET Framework. Ten artykuł uzupełnia dokumentację interfejsu AssemblyLoadContext API informacjami koncepcyjnymi.
Ten artykuł jest istotny dla deweloperów wdrażających ładowanie dynamiczne, zwłaszcza deweloperów platform ładowania dynamicznego.
Co to jest AssemblyLoadContext?
Każda aplikacja .NET 5+ i .NET Core niejawnie używa metody AssemblyLoadContext. Jest to dostawca środowiska uruchomieniowego do lokalizowania i ładowania zależności. Za każdym razem, gdy jest ładowana zależność, wystąpienie jest wywoływane w AssemblyLoadContext celu zlokalizowania go.
- AssemblyLoadContext udostępnia usługę lokalizowania, ładowania i buforowania zarządzanych zestawów oraz innych zależności.
- Aby obsługiwać dynamiczne ładowanie i zwalnianie kodu, tworzy izolowany kontekst ładowania kodu i jego zależności we własnym AssemblyLoadContext wystąpieniu.
Reguły przechowywania wersji
Pojedyncze AssemblyLoadContext wystąpienie jest ograniczone do ładowania dokładnie jednej wersji na prostą Assembly nazwę zestawu. Jeśli odwołanie do zestawu zostanie rozpoznane względem AssemblyLoadContext wystąpienia, które ma już załadowaną zestaw o tej nazwie, żądana wersja jest porównywana z załadowaną wersją. Rozwiązanie powiedzie się tylko wtedy, gdy załadowana wersja jest równa lub wyższa od żądanej wersji.
Kiedy potrzebujesz wielu wystąpień AssemblyLoadContext?
Ograniczenie, że pojedyncze AssemblyLoadContext wystąpienie może załadować tylko jedną wersję zestawu, może stać się problemem podczas dynamicznego ładowania modułów kodu. Każdy moduł jest kompilowany niezależnie, a moduły mogą zależeć od różnych wersji elementu Assembly. Jest to często problem, gdy różne moduły zależą od różnych wersji powszechnie używanej biblioteki.
Aby obsługiwać dynamicznie ładowanie kodu, AssemblyLoadContext interfejs API umożliwia ładowanie sprzecznych wersji elementu Assembly w tej samej aplikacji. Każde AssemblyLoadContext wystąpienie udostępnia unikatowy słownik, który mapuje każdy AssemblyName.Name z nich na określone Assembly wystąpienie.
Zapewnia również wygodny mechanizm grupowania zależności związanych z modułem kodu na potrzeby późniejszego zwolnienia.
Wystąpienie AssemblyLoadContext.Default
Wystąpienie AssemblyLoadContext.Default jest automatycznie wypełniane przez środowisko uruchomieniowe podczas uruchamiania. Używa ona domyślnego sondowania do lokalizowania i znajdowania wszystkich zależności statycznych.
Rozwiązuje to typowe scenariusze ładowania zależności.
Zależności dynamiczne
AssemblyLoadContext ma różne zdarzenia i funkcje wirtualne, które mogą zostać zastąpione.
Wystąpienie AssemblyLoadContext.Default obsługuje tylko zastępowanie zdarzeń.
Artykuły Managed assembly loading algorithm (Algorytm ładowania zestawu zarządzanego), Satellite assembly loading algorithm (Algorytm ładowania zestawu satelitarnego) i Unmanaged (natywny) algorytm ładowania biblioteki odwołują się do wszystkich dostępnych zdarzeń i funkcji wirtualnych. W artykułach przedstawiono względne położenie poszczególnych zdarzeń i funkcji w algorytmach ładowania. Ten artykuł nie odtworzy tych informacji.
W tej sekcji omówiono ogólne zasady dotyczące odpowiednich zdarzeń i funkcji.
- Powtarzalne. Zapytanie dotyczące określonej zależności musi zawsze powodować tę samą odpowiedź. Należy zwrócić to samo załadowane wystąpienie zależności. To wymaganie ma podstawowe znaczenie dla spójności pamięci podręcznej. W szczególności w przypadku zestawów zarządzanych tworzymy pamięć podręczną Assembly . Klucz pamięci podręcznej to prosta nazwa zestawu. AssemblyName.Name
- Zazwyczaj nie rzucaj. Oczekuje się, że te funkcje są zwracane
null
, a nie zwracane, gdy nie można odnaleźć żądanej zależności. Wyrzucenie przedwcześnie zakończy wyszukiwanie i propaguje wyjątek do elementu wywołującego. Zgłaszanie powinno być ograniczone do nieoczekiwanych błędów, takich jak uszkodzony zestaw lub stan braku pamięci. - Unikaj rekursji. Należy pamiętać, że te funkcje i programy obsługi implementują reguły ładowania na potrzeby lokalizowania zależności. Implementacja nie powinna wywoływać interfejsów API, które wyzwalają rekursję. Kod powinien zwykle wywoływać funkcje ładowania AssemblyLoadContext , które wymagają określonej ścieżki lub argumentu odwołania do pamięci.
- Załaduj do poprawnego elementu AssemblyLoadContext. Wybór miejsca ładowania zależności jest specyficzny dla aplikacji. Wybór jest implementowany przez te zdarzenia i funkcje. Gdy kod wywołuje funkcje AssemblyLoadContext load-by-path, wywołają je w wystąpieniu, w którym ma zostać załadowany kod. Czasami zwracanie
null
i umożliwienie uchwytu AssemblyLoadContext.Default obciążenia może być najprostszą opcją. - Pamiętaj o wyścigach wątków. Ładowanie może być wyzwalane przez wiele wątków. Element AssemblyLoadContext obsługuje wyścigi wątków przez niepodzielne dodawanie zestawów do pamięci podręcznej. Wystąpienie przegranego wyścigu jest odrzucane. W logice implementacji nie dodawaj dodatkowej logiki, która nie obsługuje poprawnie wielu wątków.
Jak są izolowane zależności dynamiczne?
Każde AssemblyLoadContext wystąpienie reprezentuje unikatowy zakres Assembly wystąpień i Type definicji.
Nie ma izolacji binarnej między tymi zależnościami. Są one odizolowane tylko przez nie znalezienie siebie nawzajem według nazwy.
W każdym z nich AssemblyLoadContext:
- AssemblyName.Name może odwoływać się do innego Assembly wystąpienia.
- Type.GetType może zwrócić inne wystąpienie typu dla tego samego typu
name
.
Współużytkowane zależności
Zależności można łatwo udostępniać między AssemblyLoadContext wystąpieniami. Ogólny model służy do AssemblyLoadContext ładowania zależności. Inne współdzielą zależność przy użyciu odwołania do załadowanego zestawu.
To udostępnianie jest wymagane w zestawach środowiska uruchomieniowego. Te zestawy można załadować tylko do pliku AssemblyLoadContext.Default. To samo jest wymagane w przypadku struktur, takich jak ASP.NET
, WPF
lub WinForms
.
Zaleca się załadowanie współużytkowanych zależności do AssemblyLoadContext.Defaultprogramu . To udostępnianie jest typowym wzorcem projektowania.
Udostępnianie jest implementowane w kodowaniu wystąpienia niestandardowego AssemblyLoadContext . AssemblyLoadContext ma różne zdarzenia i funkcje wirtualne, które mogą zostać zastąpione. Gdy którakolwiek z tych funkcji zwraca odwołanie do Assembly wystąpienia, które zostało załadowane w innym AssemblyLoadContext wystąpieniu, Assembly wystąpienie jest współużytkowane. Standardowy algorytm ładowania odchyli się do AssemblyLoadContext.Default ładowania, aby uprościć wspólny wzorzec udostępniania. Aby uzyskać więcej informacji, zobacz Managed assembly loading algorithm (Algorytm ładowania zestawu zarządzanego).
Problemy z konwersją typów
Gdy dwa AssemblyLoadContext wystąpienia zawierają definicje typów o tym samym name
typie, nie są tego samego typu. Są one tego samego typu, jeśli i tylko wtedy, gdy pochodzą z tego samego Assembly wystąpienia.
Aby skomplikować sprawy, komunikaty wyjątków dotyczące tych niezgodnych typów mogą być mylące. Typy są określane w komunikatach wyjątków według ich prostych nazw typów. Typowy komunikat o wyjątku w tym przypadku jest następujący:
Nie można przekonwertować obiektu typu "IsolatedType" na typ "IsolatedType".
Debugowanie problemów z konwersją typu
Biorąc pod uwagę parę niezgodnych typów, ważne jest również, aby wiedzieć:
- Każdy typ to Type.Assembly.
- Każdy typ AssemblyLoadContext, który można uzyskać za pośrednictwem AssemblyLoadContext.GetLoadContext(Assembly) funkcji .
Biorąc pod uwagę dwa obiekty a
i b
, ocena następujących elementów w debugerze będzie przydatna:
// In debugger look at each assembly's instance, Location, and FullName
a.GetType().Assembly
b.GetType().Assembly
// In debugger look at each AssemblyLoadContext's instance and name
System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(a.GetType().Assembly)
System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(b.GetType().Assembly)
Rozwiązywanie problemów z konwersją typów
Istnieją dwa wzorce projektowe do rozwiązywania tych problemów z konwersją typów.
Użyj typowych typów udostępnionych. Ten typ udostępniony może być typem pierwotnego środowiska uruchomieniowego lub może obejmować utworzenie nowego typu udostępnionego w zestawie udostępnionym. Często typ udostępniony jest interfejsem zdefiniowanym w zestawie aplikacji. Aby uzyskać więcej informacji, przeczytaj o sposobie udostępniania zależności.
Użyj technik marshallingu, aby przekonwertować z jednego typu na inny.