현재로드 된 어셈블리를 어떻게 반복합니까?


120

ASP.NET 응용 프로그램에 데이터베이스 연결 확인, 현재 appSettings 및 ConnectionStrings 표시 등과 같은 작업을 수행하는 "진단"페이지가 있습니다.이 페이지의 섹션에는 전체적으로 사용되는 중요한 유형의 어셈블리 버전이 표시됩니다. ,하지만로드 된 모든 어셈블리의 버전을 효과적으로 표시하는 방법을 알아낼 수 없었습니다.

.NET 응용 프로그램에서 현재 참조 및 / 또는로드 된 어셈블리를 모두 파악하는 가장 효과적인 방법은 무엇입니까 ?

참고 : 특정 디렉터리에서 * .dll을 반복하는 것과 같은 파일 기반 방법에는 관심이 없습니다. 나는 응용 프로그램이 실제로 무엇에 관심이 사용하는 지금.

답변:


24

이 확장 메서드는 중첩 된 어셈블리를 포함하여 모든 참조 된 어셈블리를 재귀 적으로 가져옵니다.

를 사용할 ReflectionOnlyLoad때 별도의 AppDomain에 어셈블리를로드하므로 JIT 프로세스를 방해하지 않는다는 장점이 있습니다.

또한 MyGetMissingAssembliesRecursive. 이를 사용하여 참조되었지만 어떤 이유로 현재 디렉터리에 존재하지 않는 누락 된 어셈블리를 검색 할 수 있습니다. 이것은 MEF를 사용할 때 매우 유용합니다 . 반환 목록은 누락 된 어셈블리와 소유자 (부모)를 모두 제공합니다.

/// <summary>
///     Intent: Get referenced assemblies, either recursively or flat. Not thread safe, if running in a multi
///     threaded environment must use locks.
/// </summary>
public static class GetReferencedAssemblies
{
    static void Demo()
    {
        var referencedAssemblies = Assembly.GetEntryAssembly().MyGetReferencedAssembliesRecursive();
        var missingAssemblies = Assembly.GetEntryAssembly().MyGetMissingAssembliesRecursive();
        // Can use this within a class.
        //var referencedAssemblies = this.MyGetReferencedAssembliesRecursive();
    }

    public class MissingAssembly
    {
        public MissingAssembly(string missingAssemblyName, string missingAssemblyNameParent)
        {
            MissingAssemblyName = missingAssemblyName;
            MissingAssemblyNameParent = missingAssemblyNameParent;
        }

        public string MissingAssemblyName { get; set; }
        public string MissingAssemblyNameParent { get; set; }
    }

    private static Dictionary<string, Assembly> _dependentAssemblyList;
    private static List<MissingAssembly> _missingAssemblyList;

    /// <summary>
    ///     Intent: Get assemblies referenced by entry assembly. Not recursive.
    /// </summary>
    public static List<string> MyGetReferencedAssembliesFlat(this Type type)
    {
        var results = type.Assembly.GetReferencedAssemblies();
        return results.Select(o => o.FullName).OrderBy(o => o).ToList();
    }

    /// <summary>
    ///     Intent: Get assemblies currently dependent on entry assembly. Recursive.
    /// </summary>
    public static Dictionary<string, Assembly> MyGetReferencedAssembliesRecursive(this Assembly assembly)
    {
        _dependentAssemblyList = new Dictionary<string, Assembly>();
        _missingAssemblyList = new List<MissingAssembly>();

        InternalGetDependentAssembliesRecursive(assembly);

        // Only include assemblies that we wrote ourselves (ignore ones from GAC).
        var keysToRemove = _dependentAssemblyList.Values.Where(
            o => o.GlobalAssemblyCache == true).ToList();

        foreach (var k in keysToRemove)
        {
            _dependentAssemblyList.Remove(k.FullName.MyToName());
        }

        return _dependentAssemblyList;
    }

    /// <summary>
    ///     Intent: Get missing assemblies.
    /// </summary>
    public static List<MissingAssembly> MyGetMissingAssembliesRecursive(this Assembly assembly)
    {
        _dependentAssemblyList = new Dictionary<string, Assembly>();
        _missingAssemblyList = new List<MissingAssembly>();
        InternalGetDependentAssembliesRecursive(assembly);

        return _missingAssemblyList;
    }

    /// <summary>
    ///     Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of
    ///     dependent assemblies, etc.
    /// </summary>
    private static void InternalGetDependentAssembliesRecursive(Assembly assembly)
    {
        // Load assemblies with newest versions first. Omitting the ordering results in false positives on
        // _missingAssemblyList.
        var referencedAssemblies = assembly.GetReferencedAssemblies()
            .OrderByDescending(o => o.Version);

        foreach (var r in referencedAssemblies)
        {
            if (String.IsNullOrEmpty(assembly.FullName))
            {
                continue;
            }

            if (_dependentAssemblyList.ContainsKey(r.FullName.MyToName()) == false)
            {
                try
                {
                    var a = Assembly.ReflectionOnlyLoad(r.FullName);
                    _dependentAssemblyList[a.FullName.MyToName()] = a;
                    InternalGetDependentAssembliesRecursive(a);
                }
                catch (Exception ex)
                {
                    _missingAssemblyList.Add(new MissingAssembly(r.FullName.Split(',')[0], assembly.FullName.MyToName()));
                }
            }
        }
    }

    private static string MyToName(this string fullName)
    {
        return fullName.Split(',')[0];
    }
}

최신 정보

이 코드 스레드를 안전하게 만들려면 lock주위에 두십시오. 공유 정적 전역 변수를 참조하여 마법을 수행하기 때문에 현재 기본적으로 스레드로부터 안전하지 않습니다.


스레드 안전을 위해 다시 작성 했으므로 동시에 여러 다른 스레드에서 호출 할 수 있습니다 (원하는 이유는 확실하지 않지만 더 안전합니다). 코드 게시를 원하시면 알려주세요.
Contango

2
@Contango 스레드 안전 버전을 게시 할 수 있습니까? 아니면 블로그를 작성한 경우 게시 할 수 있습니까?
로버트

2
이 스레드를 안전하게 만드는 순진한 방법 lock은 전체를 둘러 보는 것입니다. 내가 사용한 다른 방법은 전역 정적 "_dependentAssemblyList"에 대한 종속성을 제거했기 때문에 lock여러 스레드가 누락 된 어셈블리를 동시에 확인하려고 할 때 약간의 속도 이점이있는. 코너 케이스).
Contango

3
추가하는 lock것은 "스레드 안전"방식에 많은 것을 추가하지 않습니다. 물론,이 코드 블록은 한 번에 하나만 실행됩니다. 그러나 다른 스레드는 언제든지 어셈블리를로드 할 수 있으며 이로 인해 일부 foreach루프에 문제가 발생할 수 있습니다 .
Peter Ritchie

1
@Peter Ritchie 재귀 중에 사용되는 공유 정적 전역 변수가 있으므로 모든 액세스에 잠금을 추가하면 해당 부분이 스레드로부터 안전 해집니다. 좋은 프로그래밍 연습 일뿐입니다. 일반적으로 MEF와 같은 것을 사용하지 않는 한 모든 필수 어셈블리는 시작시로드되므로 실제로 스레드 안전성은 실제로 문제가되지 않습니다.
Contango

193

현재로드 된 어셈블리 가져 오기 AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

다른 어셈블리에서 참조하는 어셈블리 가져 오기 :

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

어셈블리 A가 어셈블리 B를 참조하고 어셈블리 A가로드 된 경우 어셈블리 B도로드된다는 의미는 아닙니다. 어셈블리 B는 필요한 경우에만로드됩니다. 따라서 인스턴스가 아닌 인스턴스를 GetReferencedAssemblies()반환 합니다.AssemblyNameAssembly


2
.net 솔루션이 주어지면 모든 프로젝트에서 참조되는 모든 어셈블리를 찾고 싶습니다. 아이디어?
Kumar Vaibhav 2013

두 방법 모두 실제로 사용되는 dll 만 나열합니다. 분명히 사용되지 않는 솔루션에 참조가있는 것은 말이되지 않지만 누군가가 모든 어셈블리를 추측 적으로 스캔하려고 할 때 혼란 스러울 수 있습니다. 모든 어셈블리가 표시되지 않을 수 있습니다.
Pompair 2013

3
OP는 참조 어셈블리가 아닌 현재로드 된 어셈블리를 요청 합니다. 이것은 질문에 대한 답입니다. 내가 찾던 바로 그것.
MikeJansen 2016 년

어셈블리 B가로드되는시기를 알기위한 이벤트?
Kiquenet
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.