Şu anda yüklü olan derlemelerde nasıl döngü yaparsınız?


120

ASP.NET uygulamamda veritabanı bağlantılarını doğrulamak, geçerli appSettings ve ConnectionStrings'i görüntülemek gibi şeyler yapan bir "tanılama" sayfası var. Bu sayfanın bir bölümü, tüm süreçte kullanılan önemli türlerin Assembly sürümlerini görüntüler , ancak yüklenen derlemelerin TÜM sürümlerinin nasıl etkili bir şekilde gösterileceğini çözemedim.

Bir .NET uygulamasında halihazırda başvurulan ve / veya yüklenmiş tüm Montajları bulmanın en etkili yolu nedir ?

Not: Belirli bir dizinde * .dll ile yineleme gibi dosya tabanlı yöntemlerle ilgilenmiyorum. Başvuru aslında ne ilgileniyorum kullanarak hemen şimdi.

Yanıtlar:


24

Bu uzantı yöntemi, iç içe geçmiş derlemeler dahil olmak üzere tüm başvurulan derlemeleri özyinelemeli olarak alır.

Kullandığı olarak ReflectionOnlyLoad, bu tam zamanında sürecine müdahale avantajına sahiptir, ayrı bir uygulama etki alanı, derlemeleri yükler.

Ayrıca bir MyGetMissingAssembliesRecursive. Bunu, başvurulan, ancak herhangi bir nedenle geçerli dizinde bulunmayan eksik derlemeleri tespit etmek için kullanabilirsiniz. Bu, MEF kullanırken inanılmaz derecede faydalıdır . İade listesi size hem eksik montajı hem de ona sahip olanı (üst öğesi) verecektir.

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

Güncelleme

Bu kod iş parçacığını güvenli hale getirmek için locketrafına bir koyun. Sihrini yapmak için paylaşılan bir statik global değişkene başvurduğundan, şu anda varsayılan olarak iş parçacığı güvenli değildir.


Bunu iş parçacığı güvenli olacak şekilde yeniden yazdım, böylece aynı anda birçok farklı iş parçacığından çağrılabilir (neden bunu istediğinizden emin değilim, ama hey, daha güvenli). Kodu göndermemi istiyorsan bana haber ver.
Contango

2
@Contango İş Parçacığı güvenli sürümünüzü gönderebilir misiniz veya bunun hakkında bir blog yazdıysanız, bunu yayınlayabilir misiniz?
Robert

2
Bu konuyu güvenli hale getirmenin saf yolu lock, her şeyin etrafına bir göz atmaktır. Bir ihtiyaç duymadan iş parçacığı güvenli hale yüzden, "_dependentAssemblyList" küresel statik bağımlılığı ortadan kullanılan diğer bir yöntem lockbirden çok iş parçacığı aynı anda meclisleri bunun biraz (ne eksik belirlemeye çalışıyor eğer bazı küçük hız avantajları vardır ki, bir köşe çantası).
Contango

3
a lockeklemek, "iş parçacığı güvenli" yolunda pek bir şey eklemeyecektir. Elbette, bu kod bloğunun aynı anda yalnızca birini çalıştırmasını sağlar; ancak diğer iş parçacıkları grupları istedikleri zaman yükleyebilir ve bu bazı foreachdöngülerde sorunlara neden olabilir .
Peter Ritchie

1
@Peter Ritchie Özyineleme sırasında kullanılan paylaşılan bir statik küresel değişken vardır, bu nedenle buna tüm erişimlerin etrafına bir kilit eklemek, bu kısmı iş parçacığını güvenli hale getirecektir. Bu sadece iyi bir programlama uygulamasıdır. MEF gibi bir şey kullanılmadıkça, genellikle gerekli tüm derlemeler başlangıçta yüklenir, bu nedenle iş parçacığı güvenliği pratikte gerçekten bir sorun değildir.
Contango

193

Akım için yüklenen derlemeleri alma AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

Başka bir derlemenin referans verdiği derlemeleri alma:

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

Montaj A montaj B'ye ve A montajına başvurursa, bu montaj B'nin de yüklendiğini göstermez. Montaj B yalnızca gerektiğinde ve gerektiğinde yüklenecektir. Bu nedenle, örnekler yerine örnekleri GetReferencedAssemblies()döndürür .AssemblyNameAssembly


2
Bunun gibi bir şeye ihtiyacım var - bir .net çözümü verildiğinde, tüm projelerde başvurulan tüm derlemeleri bulmak istiyorum. Fikirler?
Kumar Vaibhav

Lütfen her iki yöntemin de yalnızca gerçekten kullanılan dll'leri listelediğini unutmayın. Açıkçası, kullanılmayan çözümlerde referanslara sahip olmanın bir anlamı yoktur, ancak birisi spekülatif olarak TÜM derlemeleri taramaya çalıştığında bu kafa karıştırıcı olabilir. Tüm meclisler görünmeyebilir.
Pompair

3
OP, referans alınmayan derlemelere şu anda yüklenmiş derlemeleri sorar . Bu soruyu yanıtlıyor. Tam olarak aradığım şey.
MikeJansen

Montaj B'nin ne zaman yüklendiğini bilmek için olay?
Kiquenet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.