ASP.NET -AppDomain.CurrentDomain.GetAssemblies () - assemblages manquants après redémarrage du domaine D'application

j'ai un Bootstrapper qui regarde à travers tous les assemblages dans un ASP.NET MVC application pour trouver les types qui mettent en œuvre une interface IBootstrapperTask , puis les enregistre avec un Contraineur CIO. L'idée est que vous pouvez placer votre IBootstrapperTasks n'importe où et organiser vos projets comme bon vous semble.

Code pour Bootstrap:

public class Bootstrapper
{
    static Bootstrapper()
    {
        Type bootStrapperType = typeof(IBootstrapperTask);

        IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies();

        List<Type> tasks = new List<Type>();

        foreach (Assembly assembly in assemblies)
        {
            var types = from t in assembly.GetTypes()
                        where bootStrapperType.IsAssignableFrom(t)
                            && !t.IsInterface && !t.IsAbstract
                        select t;

            tasks.AddRange(types);
        }

        foreach (Type task in tasks)
        {
            if (!IocHelper.Container().Kernel.HasComponent(task.FullName))
            {
                IocHelper.Container().AddComponentLifeStyle(
                    task.FullName, task, LifestyleType.Transient);
            }
        }
    }

    public static void Run()
    {
        // Get all registered IBootstrapperTasks, call Execute() method
    }
}

après une construction complète, AppDomain.CurrentDomain.GetAssemblies() renvoie tous les assemblages dans ma solution (y compris tous les GAC one mais ça ne me dérange pas).

cependant, si L'Appdomaine est redémarré, ou je "rebond" le Web.Fichier de configuration (Ajout d'un espace et d'une économie), le constructeur statique est lancé à nouveau , mais quand AppDomain.CurrentDomain.GetAssemblies() est appelé, la plupart des assemblages sont manquants , y compris celui contenant mes types D'IBootstrapperTask.

comment contourner ce problème? Je suppose que je pourrais M'y habituer.IO le répertoire / bin et charger tous les DLLs dans là manuellement, mais préférerait éviter cela si possible, ou est-ce le seul moyen? Suis-je prendre la bonne approche générale à cela?

C'est un ASP.NET MVC 2.0 application tournant sur .NET 4.0, je reçois ce problème avec le Visual Studio 2010 Cassini Web server intégré, et avec IIS7.0 en mode Pipeline intégré sur Windows Server 2008.


Edit: je viens de tomber sur ce post DONC Différence entre AppDomain.Getassembllies et BuildManager.Getreferencedassembllies qui dit que L'Appdomaine ne charge les assemblages que lorsqu'ils sont nécessaires (par ex. lorsqu'une méthode/classe de cette Assemblée est d'abord appelée). Je suppose que cela expliquerait pourquoi les assemblages manquent sur AppDomain.CurrentDomain.GetAssemblies() car le Bootstrapper est lancé très tôt.

j'ai remarqué si j'ai placé un appel à 'quelque chose' de l'Assemblée manquante devant le Bootstrap eg:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        MyApp.MissingAssembly.SomeClass someClass =
            new MyApp.MissingAssembly.SomeClass();

        Bootstrapper.Run();
    }
}

...il semble résoudre le problème, mais c'est un peu un hack.

30
demandé sur Community 2010-08-24 02:34:30

2 réponses

j'ai regardé à travers la ASP.NET MVC 2.0, le code source et regardé comment AreaRegistration.RegisterAllAreas(); est mis en œuvre. Cette ligne est généralement mise dans le Global.la méthode ASAX Application_Start () et, en interne, elle scanne tous les assemblages pour trouver les types qui implémentent le type abstrait AreaRegistration. C'est un peu le comportement que je recherche.

Il semble RegisterAllAreas() effectue un appel à BuildManager.GetReferencedAssemblies() , eh bien, si c'est assez bon pour MVC, alors il est assez bon pour moi :-)

j'ai fait quelques expériences et J'ai construit Manager.GetReferencedAssemblies () récupérera même adhoc, DLL aléatoire tombé dans le dossier /bin, même sans aucune référence à des projets dans la solution Visual Studio. Il semble donc beaucoup plus fiable que AppDomain.Current.GetAssemblies() .

j'ai réécrit mon code de localisateur D'assemblage comme suit:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;

public static class AssemblyLocator
{
    private static readonly ReadOnlyCollection<Assembly> AllAssemblies;
    private static readonly ReadOnlyCollection<Assembly> BinAssemblies;

    static AssemblyLocator()
    {
        AllAssemblies = new ReadOnlyCollection<Assembly>(
            BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList());

        IList<Assembly> binAssemblies = new List<Assembly>();

        string binFolder = HttpRuntime.AppDomainAppPath + "bin\";
        IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll",
            SearchOption.TopDirectoryOnly).ToList();

        foreach (string dllFile in dllFiles)
        {
            AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllFile);

            Assembly locatedAssembly = AllAssemblies.FirstOrDefault(a =>
                AssemblyName.ReferenceMatchesDefinition(
                    a.GetName(), assemblyName));

            if (locatedAssembly != null)
            {
                binAssemblies.Add(locatedAssembly);
            }
        }

        BinAssemblies = new ReadOnlyCollection<Assembly>(binAssemblies);
    }

    public static ReadOnlyCollection<Assembly> GetAssemblies()
    {
        return AllAssemblies;
    }

    public static ReadOnlyCollection<Assembly> GetBinFolderAssemblies()
    {
        return BinAssemblies;
    }
}
50
répondu Sunday Ironfoot 2014-06-09 15:31:34

on dirait que tu as résolu ton propre problème.

éditer: personnellement, je voudrais effectivement énumérer les assemblées, un par un, les charger et chercher l'interface. Faites-le à partir de fichiers plutôt que de ce que fait L'Appdomaine.

1
répondu Nate Zaugg 2010-08-23 23:17:05