System.Runtime.Loader.AssemblyLoadContext 이해Understanding System.Runtime.Loader.AssemblyLoadContext

AssemblyLoadContext 클래스는 .NET Core에만 있습니다.The AssemblyLoadContext class is unique to .NET Core. 이 문서는 AssemblyLoadContext API 설명서를 개념 정보로 보완하기 위한 것입니다.This article attempts to supplement the AssemblyLoadContext API documentation with conceptual information.

이 문서는 동적 로딩을 구현하는 개발자, 특히 동적 로딩 프레임워크 개발자와 관련이 있습니다.This article is relevant to developers implementing dynamic loading, especially dynamic loading framework developers.

AssemblyLoadContext란?What is the AssemblyLoadContext?

모든 .NET Core 애플리케이션은 AssemblyLoadContext를 암시적으로 사용합니다.Every .NET Core application implicitly uses the AssemblyLoadContext. 종속성을 찾고 로드하는 런타임 공급자입니다.It's the runtime's provider for locating and loading dependencies. 종속성이 로드될 때마다 AssemblyLoadContext 인스턴스가 호출되어 해당 종속성을 찾습니다.Whenever a dependency is loaded, an AssemblyLoadContext instance is invoked to locate it.

  • 관리형 어셈블리 및 기타 종속성을 검색, 로드 및 캐시하는 서비스를 제공합니다.It provides a service of locating, loading, and caching managed assemblies and other dependencies.

  • 동적 코드 로드 및 언로드를 지원하기 위해 자체 AssemblyLoadContext 인스턴스에 코드와 해당 종속성을 로드하기 위한 격리된 컨텍스트를 만듭니다.To support dynamic code loading and unloading, it creates an isolated context for loading code and its dependencies in their own AssemblyLoadContext instance.

여러 AssemblyLoadContext 인스턴스가 필요한 경우는 언제인가요?When do you need multiple AssemblyLoadContext instances?

단일 AssemblyLoadContext 인스턴스는 단순 어셈블리 이름(AssemblyName.Name)당 정확히 하나의 Assembly 버전을 로드하는 것으로 제한됩니다.A single AssemblyLoadContext instance is limited to loading exactly one version of an Assembly per simple assembly name, AssemblyName.Name.

이 제한은 코드 모듈을 동적으로 로드할 때 문제가 될 수 있습니다.This restriction can become a problem when loading code modules dynamically. 각 모듈은 독립적으로 컴파일되며 Assembly의 다른 버전에 따라 달라질 수 있습니다.Each module is independently compiled and may depend on different versions of an Assembly. 이 문제는 일반적으로 여러 모듈이 일반적으로 사용되는 라이브러리의 서로 다른 버전에 따라 달라지는 경우에 발생합니다.This problem commonly occurs when different modules depend on different versions of a commonly used library.

동적으로 코드를 로드하도록 지원하기 위해 AssemblyLoadContext API는 동일한 애플리케이션에서 충돌하는 버전의 Assembly를 로드하는 기능을 제공합니다.To support dynamically loading code, the AssemblyLoadContext API provides for loading conflicting versions of an Assembly in the same application. AssemblyLoadContext 인스턴스는 각 AssemblyName.Name을 특정 Assembly 인스턴스에 매핑하는 고유한 사전을 제공합니다.Each AssemblyLoadContext instance provides a unique dictionary mapping each AssemblyName.Name to a specific Assembly instance.

또한 나중에 언로드하기 위해 코드 모듈과 관련된 종속성을 그룹화하는 편리한 메커니즘도 제공합니다.It also provides a convenient mechanism for grouping dependencies related to a code module for later unload.

AssemblyLoadContext.Default 인스턴스에 대한 특별한 사항은 무엇인가요?What is special about the AssemblyLoadContext.Default instance?

AssemblyLoadContext.Default 인스턴스는 시작 시 런타임에서 자동으로 채웁니다.The AssemblyLoadContext.Default instance is automatically populated by the runtime at startup. 기본 프로빙을 사용하여 모든 정적 종속성을 찾습니다.It uses default probing to locate and find all static dependencies.

가장 일반적인 종속성 로드 시나리오를 해결합니다.It solves the most common dependency loading scenarios.

AssemblyLoadContext에서 동적 종속성을 어떻게 지원하나요?How does AssemblyLoadContext support dynamic dependencies?

AssemblyLoadContext에는 재정의할 수 있는 다양한 이벤트와 가상 함수가 있습니다.AssemblyLoadContext has various events and virtual functions that can be overridden.

AssemblyLoadContext.Default 인스턴스는 이벤트 재정의만 지원합니다.The AssemblyLoadContext.Default instance only supports overriding the events.

관리형 어셈블리 로드 알고리즘, 위성 어셈블리 로드 알고리즘비관리형(네이티브) 라이브러리 로드 알고리즘 문서에서는 사용 가능한 모든 이벤트와 가상 함수를 참조합니다.The articles Managed assembly loading algorithm, Satellite assembly loading algorithm, and Unmanaged (native) library loading algorithm refer to all the available events and virtual functions. 이 문서에는 로드 알고리즘에서 각 이벤트와 함수의 상대적 위치가 나와 있습니다.The articles show each event and function's relative position in the loading algorithms. 여기서는 해당 정보를 재현하지 않습니다.This article doesn't reproduce that information.

이 섹션에서는 관련 이벤트 및 함수에 대한 일반 원칙에 대해 설명합니다.This section covers the general principles for the relevant events and functions.

  • 반복할 수 있습니다 .Be repeatable . 특정 종속성에 대한 쿼리는 항상 동일한 응답을 생성해야 합니다.A query for a specific dependency must always result in the same response. 로드된 동일한 종속성 인스턴스를 반환해야 합니다.The same loaded dependency instance must be returned. 이 요구 사항은 캐시 일관성을 위한 기본 사항입니다.This requirement is fundamental for cache consistency. 특히 관리형 어셈블리의 경우 Assembly 캐시를 만듭니다.For managed assemblies in particular, we're creating an Assembly cache. 캐시 키는 단순 어셈블리 이름(AssemblyName.Name)입니다.The cache key is a simple assembly name, AssemblyName.Name.
  • 일반적으로 throw하지 않습니다 .Typically don't throw . 요청된 종속성을 찾을 수 없는 경우 이러한 함수는 덤프 대신 null을 반환해야 합니다.It's expected that these functions return null rather than throw when unable to find the requested dependency. throw하면 검색을 조기에 종료하고 예외를 호출자에 전파합니다.Throwing will prematurely end the search and be propagate an exception to the caller. throw는 손상된 어셈블리 또는 메모리 부족 상태와 같은 예기치 않은 오류로 제한해야 합니다.Throwing should be restricted to unexpected errors like a corrupted assembly or an out of memory condition.
  • 재귀를 사용하지 않습니다 .Avoid recursion . 이러한 함수와 처리기는 종속성을 찾기 위한 로드 규칙을 구현합니다.Be aware that these functions and handlers implement the loading rules for locating dependencies. 구현에서 재귀를 트리거하는 API를 호출하지 않아야 합니다.Your implementation shouldn't call APIs that trigger recursion. 코드는 일반적으로 특정 경로 또는 메모리 참조 인수가 필요한 AssemblyLoadContext 로드 함수를 호출해야 합니다.Your code should typically call AssemblyLoadContext load functions that require a specific path or memory reference argument.
  • 올바른 AssemblyLoadContext에 로드합니다 .Load into the correct AssemblyLoadContext . 로드 종속성을 로드할 위치를 선택하는 것은 애플리케이션마다 다릅니다.The choice of where to load dependencies is application-specific. 선택은 이러한 이벤트와 함수를 통해 구현됩니다.The choice is implemented by these events and functions. 코드에서 AssemblyLoadContext load-by-path(경로별 로드) 함수를 호출하면 코드를 로드하려는 인스턴스에서 이 함수를 호출합니다.When your code calls AssemblyLoadContext load-by-path functions call them on the instance where you want the code loaded. 때로는 null을 반환하고 AssemblyLoadContext.Default에서 로드를 처리할 수 있도록 하는 것이 가장 간단한 옵션일 수 있습니다.Sometime returning null and letting the AssemblyLoadContext.Default handle the load may be the simplest option.
  • 스레드 경합에 주의합니다 .Be aware of thread races . 로딩은 여러 스레드에서 트리거할 수 있습니다.Loading can be triggered by multiple threads. AssemblyLoadContext는 어셈블리를 원자 단위로 캐시에 추가하여 스레드 경합을 처리합니다.The AssemblyLoadContext handles thread races by atomically adding assemblies to its cache. 경합 실패자의 인스턴스가 삭제됩니다.The race loser's instance is discarded. 구현 논리에서는 여러 스레드를 올바르게 처리하지 않는 추가 논리를 추가하지 않습니다.In your implementation logic, don't add extra logic that doesn't handle multiple threads properly.

동적 종속성은 어떻게 격리되나요?How are dynamic dependencies isolated?

AssemblyLoadContext 인스턴스는 Assembly 인스턴스와 Type 정의의 고유 범위를 나타냅니다.Each AssemblyLoadContext instance represents a unique scope for Assembly instances and Type definitions.

이러한 종속성 간에는 이진 격리가 없습니다.There's no binary isolation between these dependencies. 이름으로 서로를 찾지 못하여 격리될 뿐입니다.They're only isolated by not finding each other by name.

각각의 AssemblyLoadContext에서 다음을 수행할 수 있습니다.In each AssemblyLoadContext:

종속성은 어떻게 공유되나요?How are dependencies shared?

종속성은 AssemblyLoadContext 인스턴스 간에 쉽게 공유할 수 있습니다.Dependencies can easily be shared between AssemblyLoadContext instances. 일반 모델은 하나의 AssemblyLoadContext에서 종속성을 로드하는 데 사용할 수 있습니다.The general model is for one AssemblyLoadContext to load a dependency. 다른 하나는 로드된 어셈블리에 대한 참조를 사용하여 종속성을 공유합니다.The other shares the dependency by using a reference to the loaded assembly.

이 공유는 런타임 어셈블리에 필요합니다.This sharing is required of the runtime assemblies. 이러한 어셈블리는 AssemblyLoadContext.Default에만 로드할 수 있습니다.These assemblies can only be loaded into the AssemblyLoadContext.Default. ASP.NET, WPF 또는 WinForms와 같은 프레임워크에도 동일하게 필요합니다.The same is required for frameworks like ASP.NET, WPF, or WinForms.

공유 종속성을 AssemblyLoadContext.Default에 로드하는 것이 좋습니다.It's recommended that shared dependencies should be loaded into AssemblyLoadContext.Default. 이 공유는 일반적인 디자인 패턴입니다.This sharing is the common design pattern.

공유는 사용자 지정 AssemblyLoadContext 인스턴스의 코딩에서 구현됩니다.Sharing is implemented in the coding of the custom AssemblyLoadContext instance. AssemblyLoadContext에는 재정의할 수 있는 다양한 이벤트와 가상 함수가 있습니다.AssemblyLoadContext has various events and virtual functions that can be overridden. 이러한 함수 중 하나에서 다른 AssemblyLoadContext 인스턴스에 로드된 Assembly 인스턴스에 대한 참조를 반환하면 Assembly 인스턴스가 공유됩니다.When any of these functions return a reference to an Assembly instance that was loaded in another AssemblyLoadContext instance, the Assembly instance is shared. 표준 로드 알고리즘은 일반 공유 패턴을 간소화하기 위해 AssemblyLoadContext.Default의 로드를 지연시킵니다.The standard load algorithm defers to AssemblyLoadContext.Default for loading to simplify the common sharing pattern. 관리형 어셈블리 로드 알고리즘을 참조하세요.See Managed assembly loading algorithm.

컴플리케이션Complications

형식 변환 문제Type conversion issues

AssemblyLoadContext 인스턴스에 동일한 name의 형식 정의가 포함되는 경우 동일한 형식이 아닙니다.When two AssemblyLoadContext instances contain type definitions with the same name, they're not the same type. 이러한 형식은 동일한 Assembly 인스턴스에서 온 경우에만 동일합니다.They're the same type if and only if they come from the same Assembly instance.

복잡한 문제에서 이러한 일치하지 않는 형식에 대한 예외 메시지는 혼동될 수 있습니다.To complicate matters, exception messages about these mismatched types can be confusing. 형식은 예외 메시지에서 단순 형식 이름으로 참조됩니다.The types are referred to in the exception messages by their simple type names. 이 경우 일반적인 예외 메시지는 다음과 같은 형식입니다.The common exception message in this case would be of the form:

'IsolatedType' 형식의 개체를 'IsolatedType' 형식으로 변환할 수 없습니다.Object of type 'IsolatedType' cannot be converted to type 'IsolatedType'.

형식 변환 문제 디버깅Debugging type conversion issues

일치하지 않는 형식 쌍을 지정하는 경우 다음 사항도 알고 있어야 합니다.Given a pair of mismatched types it's important to also know:

ab의 두 개체를 지정하는 경우 디버거에서 다음을 평가하는 것이 좋습니다.Given two objects a and b, evaluating the following in the debugger will be helpful:

// 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)

형식 변환 문제 해결Resolving type conversion issues

이러한 형식 변환 문제를 해결하기 위한 두 가지 디자인 패턴이 있습니다.There are two design patterns for solving these type conversion issues.

  1. 공용 공유 형식을 사용합니다.Use common shared types. 이 공유 형식은 기본 런타임 형식이거나 새 공유 형식을 공유 어셈블리에 만들 수 있습니다.This shared type can either be a primitive runtime type, or it can involve creating a new shared type in a shared assembly. 공유 형식은 애플리케이션 어셈블리에 정의된 인터페이스인 경우가 많습니다.Often the shared type is an interface defined in an application assembly. 참고 항목: 종속성은 어떻게 공유되나요?See also: How are dependencies shared?.

  2. 마샬링 기술을 사용하여 한 형식에서 다른 형식으로 변환합니다.Use marshaling techniques to convert from one type to another.