MISE À JOUR: J'ai maintenant une solution Je suis beaucoup plus heureux avec cela, tout en ne résolvant pas tous les problèmes que je pose, il laisse le champ libre pour le faire . J'ai mis à jour ma propre réponse pour refléter cela.Comment pré-charger tous les assemblys déployés pour un AppDomain
Original Question
Compte tenu d'un domaine App, il y a beaucoup d'endroits différents que la fusion (le chargeur d'assemblage .Net) sondera pour un ensemble donné. De toute évidence, nous prenons cette fonctionnalité pour acquis et, puisque le sondage semble être intégré dans l'exécution. Net (Assembly._nLoad
méthode interne semble être le point d'entrée lorsque Reflect-Loading - et je suppose que le chargement implicite est probablement couvert par le même algorithme sous-jacent), en tant que développeurs, nous ne semblons pas pouvoir accéder à ces chemins de recherche. Mon problème est que j'ai un composant qui fait beaucoup de résolution de type dynamique, et qui doit pouvoir s'assurer que tous les assemblys déployés par l'utilisateur pour un AppDomain donné sont préchargés avant qu'il ne commence son travail. Oui, il ralentit le démarrage - mais les avantages que nous obtenons de ce composant surpoids tout cela.
L'algorithme de chargement de base que j'ai déjà écrit est le suivant. Il scanne en profondeur un ensemble de dossiers pour tout fichier .dll (.exes sont exclus en ce moment), et utilise Assembly.LoadFrom pour charger la DLL si son AssemblyName est introuvable dans l'ensemble des assemblys déjà chargés dans AppDomain (ce qui est mis en œuvre inefficacement, mais il peut être optimisé plus tard):
void PreLoad(IEnumerable<string> paths)
{
foreach(path p in paths)
{
PreLoad(p);
}
}
void PreLoad(string p)
{
//all try/catch blocks are elided for brevity
string[] files = null;
files = Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories);
AssemblyName a = null;
foreach (var s in files)
{
a = AssemblyName.GetAssemblyName(s);
if (!AppDomain.CurrentDomain.GetAssemblies().Any(
assembly => AssemblyName.ReferenceMatchesDefinition(
assembly.GetName(), a)))
Assembly.LoadFrom(s);
}
}
LoadFrom est utilisé parce que je l'ai trouvé que l'utilisation Load() peut conduire à dupliquer des ensembles chargés par fusion si, quand il sonde pour elle , il n'en trouve pas un chargé d'où il s'attend à le trouver. Donc, avec ceci en place, tout ce que j'ai maintenant à faire est d'obtenir une liste dans l'ordre de priorité (du plus haut au plus bas) des chemins de recherche que Fusion va utiliser quand il cherche un assembly. Ensuite, je peux simplement itérer à travers eux. Le GAC est sans importance pour cela, et je ne suis pas intéressé par les chemins fixes basés sur l'environnement que Fusion pourrait utiliser - seulement les chemins qui peuvent être glanés à partir de l'AppDomain qui contiennent des assemblages expressément déployés pour l'application.
Ma première itération de ce AppDomain.BaseDirectory simplement utilisé. Cela fonctionne pour les services, les applications de formulaire et les applications de console. Toutefois, cela ne fonctionne pas pour un site Web Asp.Net, car il existe au moins deux emplacements principaux: AppDomain.DynamicDirectory (où Asp.Net place ses classes de pages générées dynamiquement et tous les assemblys que le code de page Aspx références), puis le dossier Bin du site - qui peut être découvert à partir de la propriété AppDomain.SetupInformation.PrivateBinPath. J'ai maintenant du code de travail pour les types d'applications les plus basiques (les AppDomains hébergés par Sql Server sont une autre histoire depuis que le système de fichiers est virtualisé) - mais j'ai rencontré un problème intéressant il y a quelques jours où ce code ne fonctionne pas: le coureur de test nUnit. Ceci utilise à la fois la copie fantôme (donc mon algorithme aurait besoin de les découvrir et de les charger depuis le dossier de dépôt de copie fantôme, pas du dossier bin) et il définit le PrivateBinPath comme étant relatif au répertoire de base.
Et bien sûr, il y a plein d'autres scénarios d'hébergement que je n'ai probablement pas envisagés; mais qui doit être valide, sinon Fusion fusionnerait lors du chargement des assemblages. Je veux arrêter de me sentir à l'aise et introduire le piratage pour accommoder ces nouveaux scénarios à mesure qu'ils surgissent - ce que je veux, étant donné un AppDomain et ses informations d'installation, la capacité de produire cette liste de dossiers que je devrais scanner afin de ramasser toutes les DLL qui vont être chargées; indépendamment de la façon dont le AppDomain est configuré. Si Fusion peut les voir tout de même, alors mon code devrait le devenir.
Bien sûr, je devrais peut-être modifier l'algorithme si .Net change ses internes - c'est juste une croix que je devrai supporter. De même, je suis heureux de considérer SQL Server et tous les autres environnements similaires comme des cas limites qui ne sont pas pris en charge pour l'instant.
Des idées!?
pas vraiment une réponse, mais très utile pour moi était ce MSDN-article: http://msdn.microsoft.com/en-us/library/yx7xezcf. aspx). Et le Fuslogvw.exe mentionné devrait aider à obtenir le "pourquoi est-ce que dll pas trouvé". –
ralf.w: Je me suis demandé si je pouvais tricher en obtenant un journal de fusion pour un assemblage inexistant et l'analyser pour voir tous les endroits qu'il recherche. Bien que cela fonctionne probablement, j'ai l'impression que je devrais activer Fusion Logging sur la case cible (= lent); plus le fait que je coderais par exception, ce qui est tout simplement faux! Merci pour le lien vers l'article - peut-être un peu plus MSDN minage pourrait me donner la réponse ... –
J'ai exactement ce problème, seriez-vous prêt à partager le code que vous êtes arrivé? merci :) –