Najlepsze praktyki dotyczące ładowania zestawu

W tym artykule omówiono sposoby unikania problemów z tożsamością typu, które mogą prowadzić do InvalidCastExceptionbłędów , MissingMethodExceptioni innych. W tym artykule omówiono następujące zalecenia:

Pierwsze zalecenie, zrozumienie zalet i wad kontekstów ładowania, zawiera podstawowe informacje dotyczące innych zaleceń, ponieważ wszystkie one zależą od wiedzy kontekstów ładowania.

Omówienie zalet i wad kontekstów ładowania

W domenie aplikacji zestawy można załadować do jednego z trzech kontekstów lub można je załadować bez kontekstu:

  • Domyślny kontekst ładowania zawiera zestawy znalezione przez sondowanie globalnej pamięci podręcznej zestawów, magazynu zestawów hosta, jeśli środowisko uruchomieniowe jest hostowane (na przykład w programie SQL Server) i ApplicationBase domenie PrivateBinPath aplikacji. Większość przeciążeń metody ładuje zestawy Load do tego kontekstu.

  • Kontekst load-from zawiera zestawy ładowane z lokalizacji, które nie są przeszukiwane przez moduł ładujący. Na przykład dodatki mogą być zainstalowane w katalogu, który nie znajduje się w ścieżce aplikacji. Assembly.LoadFrom, AppDomain.CreateInstanceFromi AppDomain.ExecuteAssembly to przykłady metod, które ładują się według ścieżki.

  • Kontekst tylko odbicia zawiera zestawy załadowane za pomocą ReflectionOnlyLoad metod i ReflectionOnlyLoadFrom . Nie można wykonać kodu w tym kontekście, więc nie omówiono go tutaj. Aby uzyskać więcej informacji, zobacz How to: Load Assemblies into the Emocje ion-Only Context (Instrukcje: ładowanie zestawów do kontekstu tylko do Emocje iona).

  • W przypadku wygenerowania przejściowego zestawu dynamicznego przy użyciu emisji odbicia zestaw nie jest w żadnym kontekście. Ponadto większość zestawów ładowanych przy użyciu LoadFile metody jest ładowana bez kontekstu, a zestawy ładowane z tablic bajtowych są ładowane bez kontekstu, chyba że ich tożsamość (po zastosowaniu zasad) ustanawia je w globalnej pamięci podręcznej zestawów.

Konteksty wykonywania mają zalety i wady, jak opisano w poniższych sekcjach.

Domyślny kontekst ładowania

Gdy zestawy są ładowane do domyślnego kontekstu ładowania, ich zależności są ładowane automatycznie. Zależności, które są ładowane do domyślnego kontekstu ładowania, są automatycznie znajdowane dla zestawów w domyślnym kontekście ładowania lub z kontekstu. Ładowanie przez tożsamość zestawu zwiększa stabilność aplikacji, zapewniając, że nie są używane nieznane wersje zestawów (zobacz sekcję Unikanie wiązania w częściowych nazw zestawów).

Użycie domyślnego kontekstu ładowania ma następujące wady:

  • Zależności, które są ładowane do innych kontekstów, nie są dostępne.

  • Nie można załadować zestawów z lokalizacji spoza ścieżki sondowania do domyślnego kontekstu ładowania.

Ładowanie z kontekstu

Kontekst load-from umożliwia załadowanie zestawu ze ścieżki, która nie znajduje się pod ścieżką aplikacji, i dlatego nie jest uwzględniana w sondowaniu. Umożliwia ona zlokalizowanie i załadowanie zależności z tej ścieżki, ponieważ informacje o ścieżce są utrzymywane przez kontekst. Ponadto zestawy w tym kontekście mogą używać zależności załadowanych do domyślnego kontekstu ładowania.

Ładowanie zestawów przy użyciu Assembly.LoadFrom metody lub jednej z innych metod, które ładują się według ścieżki, ma następujące wady:

  • Jeśli zestaw z tą samą tożsamością jest już załadowany w kontekście ładowania, zwraca załadowany zestaw, LoadFrom nawet jeśli określono inną ścieżkę.

  • Jeśli zestaw jest ładowany z elementem LoadFrom, a później zestaw w domyślnym kontekście ładowania próbuje załadować ten sam zestaw według nazwy wyświetlanej, próba załadowania zakończy się niepowodzeniem. Taka sytuacja może wystąpić, gdy zestaw jest deserializowany.

  • Jeśli zestaw jest ładowany z elementem LoadFrom, a ścieżka sondowania zawiera zestaw z tą samą tożsamością, ale w innej lokalizacji może wystąpić nieoczekiwane zachowanie , InvalidCastExceptionMissingMethodExceptionlub inne.

  • LoadFromFileIOPermissionAccess.Read wymagania i FileIOPermissionAccess.PathDiscovery, lub WebPermissionw określonej ścieżce.

  • Jeśli dla zestawu istnieje obraz macierzysty, nie jest on używany.

  • Nie można załadować zestawu jako neutralnego pod względem domeny.

  • W programie .NET Framework w wersjach 1.0 i 1.1 zasady nie są stosowane.

Brak kontekstu

Ładowanie bez kontekstu jest jedyną opcją dla zestawów przejściowych generowanych z emitem odbicia. Ładowanie bez kontekstu jest jedynym sposobem ładowania wielu zestawów, które mają tę samą tożsamość w jednej domenie aplikacji. Unika się kosztów sondowania.

Zestawy ładowane z tablic bajtowych są ładowane bez kontekstu, chyba że tożsamość zestawu, która jest ustanawiana podczas stosowania zasad, pasuje do tożsamości zestawu w globalnej pamięci podręcznej zestawów; w takim przypadku zestaw jest ładowany z globalnej pamięci podręcznej zestawów.

Ładowanie zestawów bez kontekstu ma następujące wady:

  • Inne zestawy nie mogą wiązać się z zestawami, które są ładowane bez kontekstu, chyba że obsłużysz AppDomain.AssemblyResolve zdarzenie.

  • Zależności nie są ładowane automatycznie. Można je wstępnie załadować bez kontekstu, wstępnie załadować do domyślnego kontekstu ładowania lub załadować je, obsługując AppDomain.AssemblyResolve zdarzenie.

  • Ładowanie wielu zestawów z tą samą tożsamością bez kontekstu może spowodować problemy z tożsamością typu podobne do tych, które są spowodowane ładowaniem zestawów z tą samą tożsamością do wielu kontekstów. Zobacz Unikanie ładowania zestawu do wielu kontekstów.

  • Jeśli dla zestawu istnieje obraz macierzysty, nie jest on używany.

  • Nie można załadować zestawu jako neutralnego pod względem domeny.

  • W programie .NET Framework w wersjach 1.0 i 1.1 zasady nie są stosowane.

Unikaj wiązania w przypadku częściowych nazw zestawów

Powiązanie nazwy częściowej występuje po określeniu tylko części nazwy wyświetlanej zestawu (FullName) podczas ładowania zestawu. Można na przykład wywołać metodę Assembly.Load z tylko prostą nazwą zestawu, pomijając wersję, kulturę i token klucza publicznego. Możesz też wywołać metodę Assembly.LoadWithPartialName , która najpierw wywołuje Assembly.Load metodę i, jeśli nie uda się zlokalizować zestawu, przeszukuje globalną pamięć podręczną zestawów i ładuje najnowszą dostępną wersję zestawu.

Powiązanie częściowej nazwy może powodować wiele problemów, w tym następujące:

  • Metoda Assembly.LoadWithPartialName może załadować inny zestaw o tej samej prostej nazwie. Na przykład dwie aplikacje mogą instalować dwa zupełnie różne zestawy, które mają prostą nazwę GraphicsLibrary w globalnej pamięci podręcznej zestawów.

  • Zestaw, który jest faktycznie załadowany, może nie być zgodny z poprzednimi wersjami. Na przykład nie określenie wersji może spowodować załadowanie znacznie nowszej wersji niż wersja, z jaką program został pierwotnie napisany do użycia. Zmiany w nowszej wersji mogą powodować błędy w aplikacji.

  • Zestaw, który jest faktycznie załadowany, może nie być zgodny z usługą forward. Na przykład aplikacja mogła zostać skompilowana i przetestowana przy użyciu najnowszej wersji zestawu, ale częściowe powiązanie może załadować znacznie wcześniejszą wersję, która nie zawiera funkcji używanych przez aplikację.

  • Instalowanie nowych aplikacji może przerwać istniejące aplikacje. Aplikacja korzystająca z LoadWithPartialName metody może zostać uszkodzona przez zainstalowanie nowszej, niezgodnej wersji zestawu udostępnionego.

  • Może wystąpić nieoczekiwane ładowanie zależności. Ładowanie dwóch zestawów współużytkujących zależność, ładowanie ich z częściowym powiązaniem może spowodować utworzenie jednego zestawu przy użyciu składnika, z którym nie został skompilowany lub przetestowany.

Ze względu na problemy, które może spowodować, LoadWithPartialName metoda została oznaczona jako przestarzała. Zalecamy zamiast tego użycie Assembly.Load metody i określenie pełnych nazw wyświetlanych zestawów. Zobacz Omówienie zalet i wad kontekstów ładowania i rozważ przełączenie do domyślnego kontekstu ładowania.

Jeśli chcesz użyć LoadWithPartialName metody , ponieważ ułatwia ładowanie zestawu, rozważ, że niepowodzenie aplikacji z komunikatem o błędzie, który identyfikuje brakujący zestaw, może zapewnić lepsze środowisko użytkownika niż automatyczne użycie nieznanej wersji zestawu, co może spowodować nieprzewidywalne zachowanie i luki bezpieczeństwa.

Unikaj ładowania zestawu do wielu kontekstów

Ładowanie zestawu do wielu kontekstów może powodować problemy z tożsamością typu. Jeśli ten sam typ jest ładowany z tego samego zestawu do dwóch różnych kontekstów, jest tak, jakby załadowano dwa różne typy o tej samej nazwie. Jeśli InvalidCastException spróbujesz rzutować jeden typ na drugi, zostanie zgłoszony komunikat z mylącym komunikatem, że nie można rzutować typu MyType na typ MyType.

Załóżmy na przykład, że ICommunicate interfejs jest zadeklarowany w zestawie o nazwie Utility, do którego odwołuje się program, a także przez inne zestawy ładowane przez program. Te inne zestawy zawierają typy, które implementują ICommunicate interfejs, co pozwala programowi na ich użycie.

Teraz zastanów się, co się stanie po uruchomieniu programu. Zestawy, do których odwołuje się program, są ładowane do domyślnego kontekstu ładowania. Jeśli załadujesz zestaw docelowy według jego tożsamości, używając Load metody , będzie on w domyślnym kontekście ładowania i tak będzie jego zależności. Zarówno program, jak i zestaw docelowy będą używać tego samego Utility zestawu.

Załóżmy jednak, że załadujesz zestaw docelowy według jego ścieżki pliku przy użyciu LoadFile metody . Zestaw jest ładowany bez żadnego kontekstu, więc jego zależności nie są ładowane automatycznie. Może istnieć procedura obsługi dla AppDomain.AssemblyResolve zdarzenia w celu dostarczenia zależności i może załadować Utility zestaw bez kontekstu przy użyciu LoadFile metody . Teraz, gdy utworzysz wystąpienie typu zawartego w zestawie docelowym i spróbujesz przypisać je do zmiennej typu ICommunicate, zgłaszany jest błąd , InvalidCastException ponieważ środowisko uruchomieniowe uwzględnia ICommunicate interfejsy w dwóch kopiach Utility zestawu jako różne typy.

Istnieje wiele innych scenariuszy, w których zestaw można załadować do wielu kontekstów. Najlepszym rozwiązaniem jest uniknięcie konfliktów przez przeniesienie zestawu docelowego w ścieżce aplikacji i użycie Load metody z pełną nazwą wyświetlaną. Zestaw jest następnie ładowany do domyślnego kontekstu ładowania, a oba zestawy używają tego samego Utility zestawu.

Jeśli zestaw docelowy musi pozostać poza ścieżką aplikacji, możesz użyć LoadFrom metody , aby załadować go do kontekstu ładowania. Jeśli zestaw docelowy został skompilowany przy użyciu odwołania do zestawu aplikacji Utility , użyje Utility zestawu załadowanego przez aplikację do domyślnego kontekstu ładowania. Należy pamiętać, że problemy mogą wystąpić, jeśli zestaw docelowy ma zależność od kopii zestawu znajdującego Utility się poza ścieżką aplikacji. Jeśli zestaw zostanie załadowany do kontekstu ładowania przed załadowaniem Utility zestawu przez aplikację, ładowanie aplikacji zakończy się niepowodzeniem.

W sekcji Rozważ przełączenie do domyślnego kontekstu ładowania omówiono alternatywy dla ładowania ścieżek plików, takich jak LoadFile i LoadFrom.

Unikaj ładowania wielu wersji zestawu do tego samego kontekstu

Ładowanie wielu wersji zestawu do jednego kontekstu ładowania może powodować problemy z tożsamością typu. Jeśli ten sam typ jest ładowany z dwóch wersji tego samego zestawu, oznacza to, że załadowano dwa różne typy o tej samej nazwie. Jeśli InvalidCastException spróbujesz rzutować jeden typ na drugi, zostanie zgłoszony komunikat z mylącym komunikatem, że nie można rzutować typu MyType na typ MyType.

Na przykład program może załadować jedną wersję Utility zestawu bezpośrednio, a później załadować inny zestaw, który ładuje inną wersję Utility zestawu. Lub błąd kodowania może spowodować załadowanie różnych wersji zestawu dwóch różnych ścieżek kodu w aplikacji.

W domyślnym kontekście ładowania ten problem może wystąpić w przypadku użycia Assembly.Load metody i określenia pełnych nazw wyświetlanych zestawów, które zawierają różne numery wersji. W przypadku zestawów, które są ładowane bez kontekstu, problem może być spowodowany użyciem Assembly.LoadFile metody ładowania tego samego zestawu z różnych ścieżek. Środowisko uruchomieniowe uwzględnia dwa zestawy, które są ładowane z różnych ścieżek do różnych zestawów, nawet jeśli ich tożsamości są takie same.

Oprócz problemów z tożsamością typu wiele wersji zestawu może spowodować MissingMethodException , że typ załadowany z jednej wersji zestawu jest przekazywany do kodu, który oczekuje tego typu z innej wersji. Na przykład kod może oczekiwać metody, która została dodana do nowszej wersji.

Bardziej subtelne błędy mogą wystąpić, jeśli zachowanie typu zmieniło się między wersjami. Na przykład metoda może zgłosić nieoczekiwany wyjątek lub zwrócić nieoczekiwaną wartość.

Dokładnie przejrzyj kod, aby upewnić się, że załadowano tylko jedną wersję zestawu. Za pomocą AppDomain.GetAssemblies metody można określić, które zestawy są ładowane w danym momencie.

Rozważ przełączenie się do domyślnego kontekstu ładowania

Sprawdź wzorce ładowania i wdrażania zestawu aplikacji. Czy można wyeliminować zestawy ładowane z tablic bajtowych? Czy można przenieść zestawy do ścieżki sondowania? Jeśli zestawy znajdują się w globalnej pamięci podręcznej zestawów lub w ścieżce sondowania domeny aplikacji (czyli jej ApplicationBase i PrivateBinPath), można załadować zestaw według jego tożsamości.

Jeśli nie można umieścić wszystkich zestawów w ścieżce sondowania, rozważ alternatywy, takie jak użycie modelu dodatku .NET Framework, umieszczenie zestawów w globalnej pamięci podręcznej zestawów lub utworzenie domen aplikacji.

Rozważ użycie modelu dodatku .NET Framework

Jeśli używasz kontekstu ładowania z do implementowania dodatków, które zwykle nie są zainstalowane w bazie aplikacji, użyj modelu dodatku .NET Framework. Ten model zapewnia izolację na poziomie domeny aplikacji lub procesu bez konieczności samodzielnego zarządzania domenami aplikacji. Aby uzyskać informacje na temat modelu dodatku, zobacz Dodatki i rozszerzalność.

Rozważ użycie globalnej pamięci podręcznej zestawów

Umieść zestawy w globalnej pamięci podręcznej zestawów, aby uzyskać korzyści ze współużytkowanej ścieżki zestawu znajdującej się poza bazą aplikacji, bez utraty zalet domyślnego kontekstu ładowania lub stosowania wad innych kontekstów.

Rozważ użycie domen aplikacji

Jeśli ustalisz, że niektórych zestawów nie można wdrożyć w ścieżce sondowania aplikacji, rozważ utworzenie nowej domeny aplikacji dla tych zestawów. Użyj elementu AppDomainSetup , aby utworzyć nową domenę aplikacji i użyć AppDomainSetup.ApplicationBase właściwości , aby określić ścieżkę zawierającą zestawy, które chcesz załadować. Jeśli masz wiele katalogów do sondowania, możesz ustawić ApplicationBase element na katalog główny i użyć AppDomainSetup.PrivateBinPath właściwości w celu zidentyfikowania podkatalogów do sondowania. Alternatywnie można utworzyć wiele domen aplikacji i ustawić ApplicationBase każdą domenę aplikacji na odpowiednią ścieżkę dla jej zestawów.

Pamiętaj, że możesz użyć Assembly.LoadFrom metody , aby załadować te zestawy. Ponieważ znajdują się one teraz w ścieżce sondowania, zostaną załadowane do domyślnego kontekstu ładowania zamiast ładowania z kontekstu. Zalecamy jednak przełączenie się do Assembly.Load metody i podanie pełnych nazw wyświetlanych zestawów w celu zapewnienia, że prawidłowe wersje są zawsze używane.

Zobacz też