現在読み込まれているアセンブリをどのようにループしますか?


120

データベース接続の確認、現在のappSettingsおよびConnectionStringsの表示などを行うASP.NETアプリケーションに「診断」ページがあります。このページのセクションには、全体で使用される重要なタイプのアセンブリバージョンが表示されます、しかし、読み込まれたすべてのアセンブリのバージョンを効果的に表示する方法を理解できませんでした。

.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 2014年

2
@Contangoスレッドセーフバージョンを投稿していただけますか。それについてブログを書いている場合は、それを投稿してください。
Robert

2
このスレッドを安全にする素朴な方法はlock、全体をアラウンドすることです。私が使用したもう1つの方法は、グローバル静的 "_dependentAssemblyList"への依存を排除​​したためlock、を必要とせずにスレッドセーフになります。コーナーケース)。
コンタンゴ

3
lock「スレッドセーフ」の方法でa を追加してもあまり追加されません。もちろん、そのコードブロックは一度に1つだけ実行されます。しかし、他のスレッドは好きなときにいつでもアセンブリをロードできるため、一部のforeachループで問題が発生する可能性があります。
Peter Ritchie

1
@Peter Ritchie再帰中に使用される静的な共有グローバル変数があるため、そのすべてのアクセスにロックを追加すると、その部分のスレッドが安全になります。これはプログラミングの実践にすぎません。MEFのようなものが使用されない限り、通常、必要なすべてのアセンブリは起動時に読み込まれるため、スレッドの安全性は実際には問題にはなりません。
Contango 2015年

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

アセンブリBがいつ読み込まれるかを知るためのイベント?
Kiquenet
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.