web-dev-qa-db-fra.com

Existe-t-il un moyen de forcer le chargement de tous les assemblys référencés dans le domaine d'application?

Mes projets sont mis en place comme ceci:

  • Définition de projet"
  • La mise en œuvre du projet"
  • Projet "Consommateur"

Le projet "Consommateur" fait référence à la fois à "Définition" et à "Implémentation", mais ne fait référence à aucun type statique dans "Implémentation".

Lorsque l'application démarre, le projet "Consommateur" appelle une méthode statique dans "Définition", qui doit rechercher des types dans "Implémentation".

Existe-t-il un moyen de forcer le chargement de tout assemblage référencé dans le domaine d'application sans connaître le chemin ou le nom, et de préférence sans avoir à utiliser un cadre IOC complet?

66
Daniel Schaffer

Cela semblait faire l'affaire:

        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
        var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();

        var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
        var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
        toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

Comme Jon l'a noté, la solution idéale aurait besoin de rentrer dans les dépendances de chacun des assemblys chargés, mais dans mon scénario spécifique, je n'ai pas à m'en soucier.


Mise à jour: Le cadre d'extensibilité géré (System.ComponentModel) inclus dans .NET 4 offre de bien meilleures installations pour réaliser de telles tâches.

77
Daniel Schaffer

Vous pouvez utiliser Assembly.GetReferencedAssemblies pour obtenir un AssemblyName[], puis appeler Assembly.Load(AssemblyName) sur chacun d’eux. Bien sûr, vous aurez besoin de recurse - mais gardez de préférence une trace des assemblages que vous avez déjà chargés :)

50
Jon Skeet

je voulais juste partager un exemple récursif. J'appelle la méthode LoadReferencedAssembly dans ma routine de démarrage comme ceci:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    this.LoadReferencedAssembly(Assembly);
}

C'est la méthode récursive:

private void LoadReferencedAssembly(Assembly assembly)
{
    foreach (AssemblyName name in Assembly.GetReferencedAssemblies())
    {
        if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
        {
            this.LoadReferencedAssembly(Assembly.Load(name));
        }
    }
}
16
jmelhus

Si vous utilisez Fody.Costura ou toute autre solution de fusion d’Assembly, la réponse acceptée ne fonctionnera pas. 

Ce qui suit charge les assemblages référencés de tout assemblage actuellement chargé. La récursivité vous est laissée. 

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

loadedAssemblies
    .SelectMany(x => x.GetReferencedAssemblies())
    .Distinct()
    .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
    .ToList()
    .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));
12
Meirion Hughes

Encore une autre version (basée sur Daniel Schaffer answer) est le cas où vous n’avez pas besoin de charger toutes les Assemblées, mais un nombre prédéfini d’entre elles:

var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" };

// First trying to get all in above list, however this might not 
// load all of them, because CLR will exclude the ones 
// which are not used in the code
List<Assembly> dataAssembliesNames =
   AppDomain.CurrentDomain.GetAssemblies()
            .Where(Assembly => AssembliesToLoad.Any(a => Assembly.GetName().Name == a))
            .ToList();

var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray();

var compareConfig = StringComparison.InvariantCultureIgnoreCase;
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Where(f =>
    {
       // filtering the ones which are in above list
       var lastIndexOf = f.LastIndexOf("\\", compareConfig);
       var dllIndex = f.LastIndexOf(".dll", compareConfig);

       if (-1 == lastIndexOf || -1 == dllIndex)
       {
          return false;
       }

       return AssembliesToLoad.Any(aName => aName == 
          f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1));
     });

var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();

toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

if (dataAssembliesNames.Count() != AssembliesToLoad.Length)
{
   throw new Exception("Not all assemblies were loaded into the  project!");
}
0

Voyant que je devais charger une assemblée + dépendances à partir d'un chemin spécifique aujourd'hui, j'ai écrit cette classe pour le faire.

public static class AssemblyLoader
{
    private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();

    static AssemblyLoader()
    {
        AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;

    }

    public static Assembly LoadWithDependencies(string assemblyPath)
    {
        AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
        return Assembly.LoadFile(assemblyPath);
    }

    private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
        List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();

        foreach (string directoryToScan in directoriesToScan)
        {
            string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
            if (File.Exists(dependentAssemblyPath))
                return LoadWithDependencies(dependentAssemblyPath);
        }
        return null;
    }

    private static string GetExecutingAssemblyDirectory()
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        var uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}
0
Peter Morris