Làm thế nào để bạn lặp qua các hội đồng đang được tải?


120

Tôi có một trang "chẩn đoán" trong ứng dụng ASP.NET của mình, trang này thực hiện những việc như xác minh (các) kết nối cơ sở dữ liệu, hiển thị appSettings và ConnectionStrings hiện tại, v.v. Một phần của trang này hiển thị các phiên bản Assembly của các loại quan trọng được sử dụng xuyên suốt , nhưng tôi không thể tìm ra cách hiển thị hiệu quả các phiên bản của TẤT CẢ các cụm được tải.

Cách hiệu quả nhất để tìm ra tất cả các Assemblies hiện được tham chiếu và / hoặc đã tải trong ứng dụng .NET là gì?

Lưu ý: Tôi không quan tâm đến các phương thức dựa trên tệp, như lặp qua * .dll trong một thư mục cụ thể. Tôi quan tâm đến những gì ứng dụng thực sự đang sử dụng ngay bây giờ.

Câu trả lời:


24

Phương thức mở rộng này nhận tất cả các hợp ngữ được tham chiếu, một cách đệ quy, bao gồm cả các hợp ngữ lồng nhau.

Khi sử dụng ReflectionOnlyLoad, nó tải các tập hợp trong một AppDomain riêng biệt, có ưu điểm là không can thiệp vào quá trình JIT.

Bạn sẽ nhận thấy rằng cũng có một MyGetMissingAssembliesRecursive. Bạn có thể sử dụng điều này để phát hiện bất kỳ hội đồng bị thiếu nào được tham chiếu, nhưng không có trong thư mục hiện tại vì một số lý do. Điều này cực kỳ hữu ích khi sử dụng MEF . Danh sách trả về sẽ cung cấp cho bạn cả assembly bị thiếu và ai sở hữu nó (cha mẹ của nó).

/// <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];
    }
}

Cập nhật

Để làm cho chuỗi mã này an toàn, hãy đặt lockxung quanh nó. Nó hiện không an toàn cho chuỗi theo mặc định, vì nó tham chiếu đến một biến toàn cục tĩnh được chia sẻ để thực hiện điều kỳ diệu của nó.


Tôi chỉ viết lại điều này để an toàn cho luồng, vì vậy nó có thể được gọi từ nhiều luồng khác nhau đồng thời (không chắc tại sao bạn lại muốn điều đó, nhưng này, nó an toàn hơn). Hãy cho tôi biết nếu bạn muốn tôi đăng mã.
Contango

2
@Contango Bạn có thể đăng phiên bản an toàn cho Chủ đề của mình không, hoặc nếu bạn đã viết blog về nó, hãy đăng nó?
Robert

2
Cách đơn giản để làm cho chủ đề này an toàn là đặt lockxung quanh toàn bộ. Phương pháp khác mà tôi đã sử dụng đã loại bỏ sự phụ thuộc vào tĩnh toàn cục "_dependentAssemblyList", vì vậy nó trở nên an toàn cho luồng mà không cần a lock, có một số lợi thế về tốc độ nếu nhiều luồng đang cố gắng đồng thời xác định cụm nào bị thiếu (đây là một chút về một trường hợp góc).
Contango

3
thêm một locksẽ không thêm nhiều theo cách "an toàn chuỗi". Chắc chắn, điều đó làm cho khối mã đó chỉ thực thi từng khối một; nhưng các luồng khác có thể tải các tập hợp bất kỳ lúc nào họ thích và điều đó có thể gây ra sự cố với một số foreachvòng lặp.
Peter Ritchie

1
@Peter Ritchie Có một biến toàn cục tĩnh được chia sẻ được sử dụng trong quá trình đệ quy, vì vậy việc thêm một khóa xung quanh tất cả các truy cập vào đó sẽ làm cho luồng phần đó an toàn. Nó chỉ là thực hành lập trình tốt. Thông thường tất cả các hợp ngữ được yêu cầu đều được tải khi khởi động, trừ khi một cái gì đó như MEF được sử dụng, vì vậy an toàn luồng thực sự không phải là một vấn đề trong thực tế.
Contango

193

Nhận các cụm đã tải cho hiện tại AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

Bắt các hội đồng được tham chiếu bởi một hội đồng khác:

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

Lưu ý rằng nếu cụm A tham chiếu đến cụm B và cụm A được tải, điều đó không có nghĩa là cụm B cũng được tải. Assembly B sẽ chỉ được tải nếu và khi cần. Vì lý do đó, GetReferencedAssemblies()trả về các AssemblyNamephiên bản thay vì Assemblyphiên bản.


2
Tôi cần một cái gì đó như thế này - với một giải pháp .net, tôi muốn tìm hiểu tất cả các hội đồng tham chiếu trong tất cả các dự án. Ý tưởng?
Kumar Vaibhav

Xin lưu ý rằng cả hai phương pháp chỉ liệt kê các dll thực sự được sử dụng. Rõ ràng là không có ý nghĩa gì khi có tham chiếu trong các giải pháp không được sử dụng, nhưng điều này có thể gây nhầm lẫn khi ai đó đang cố gắng suy đoán quét qua TẤT CẢ các tập hợp. Tất cả các cụm có thể không hiển thị.
Pompair

3
OP hỏi các hội đồng được tải hiện tại không phải là các hội đồng tham chiếu. Điều này trả lời câu hỏi. Chính xác những gì tôi đang tìm kiếm.
MikeJansen

sự kiện cho biết khi nào Assembly B được tải?
Kiquenet
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.