2010-04-03 3 views
8

J'utilise un cadre personnalisé qui utilise la réflexion pour faire un GetTypeByName(string fullName) sur le nom de type complet qu'il obtient de la base de données, pour créer une instance de ce type et l'ajouter à la page, résultant en un type modulaire standard de chose.L'assembly n'est pas disponible après le changement Web.config

GetTypeByName est une fonction d'utilité de la mienne qui itère simplement par Thread.GetDomain().GetAssemblies(), puis exécute un assembly.GetType(fullName) pour trouver le type approprié. Évidemment, ce résultat est mis en cache pour référence future et vitesse. Cependant, je rencontre des problèmes avec la mise à jour du fichier web.config (et dans certains cas plus effrayants si le pool d'applications est recyclé), il perd alors toute connaissance de certains assemblages, ce qui entraîne l'impossibilité de rendre une instance du type de module. Le débogage montre que l'assembly manquant n'existe pas littéralement dans la liste des assemblages de threads en cours. Pour contourner cela, j'ai ajouté une deuxième vérification qui est un peu sale mais récursif à travers les DLL du répertoire/bin/et vérifie que chacun existe dans la liste des assemblys. Si ce n'est pas le cas, il le charge en utilisant Assembly.Load et en corrigeant le problème de contexte grâce à 'Solving the Assembly Load Context Problem'. Cela fonctionnerait, mais il semble que (et je suis conscient que cela ne devrait pas être possible) certains projets ont toujours accès à l'assemblage manquant, par exemple mon projet Web réel plutôt que le cadre lui-même - et il se plaint que des références en double ont été ajoutées!

Quelqu'un a-t-il jamais entendu parler de ce genre de choses, ou a-t-il des idées sur la raison pour laquelle un assemblage disparaîtrait tout simplement d'un changement de configuration? A moins d'une solution, quelle est la solution la plus élégante pour obtenir tous les assemblages dans la poubelle à recharger? Il doit être tout en un "hit" afin que les visiteurs du site ne voient pas de différence autre qu'un petit retard, donc un fichier app_offline.htm est hors de question. Renommer par programme un DLL dans le chutier puis le renommer fonctionne, mais nécessite des autorisations "modify" pour le compte d'utilisateur IIS, qui est fou.

Merci pour les conseils que la communauté peut rassembler!

+0

@ tags2k Il est possible de placer un lien pour télécharger le fichier dll qui fait le problème, pour le vérifier avec réflecteur, sur le point de l'erreur (aka sur GetTypeByName). – Aristos

Répondre

2

En règle générale, vous devriez éviter de vous fier aux assemblages actuellement chargés dans un domaine d'application, car cela se produit de manière dynamique. Au lieu de cela, appelez simplement System.Web.Compilation.BuildManager.GetType() au lieu de Type.GetType() ou Assembly.GetType(). Cela devrait juste faire la bonne chose pour vous, et ne pas être affecté par les cycles appdomain.

+0

Merci David, je n'ai jamais entendu parler de cette classe auparavant, mais ça m'a aidé à faire un gros coup! Merci beaucoup - voici votre prime! : D – tags2k

+0

Content de pouvoir aider! Je suppose que nous n'avons pas fait un très bon travail de documentation des différentes méthodes de BuildManager. J'essaierai de bloguer un peu. :) –

+0

Bon conseil, mais qu'allez-vous faire si ce que vous voulez est une implémentation d'une interface donnée et non pas un type codé dur spécifique? Par exemple: 'typeof (IStuff) .IsAssignableFrom (type) &&! Type.IsInterface'. –

1

Je n'ai jamais entendu parler de ce problème auparavant. Je ne suis pas sûr si cela fonctionnera, comme je l'ai lu récemment tout en recherchant des solutions de contournement aux dépendances ODAC, mais en spécifiant le chemin de sondage pour les assemblys peut résoudre votre problème.

voir: http://msdn.microsoft.com/en-us/library/823z9h8w(VS.80).aspx

échantillon:

<configuration> 
    <runtime> 
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     <probing privatePath="bin;bin2\subbin;bin3"/> 
     </assemblyBinding> 
    </runtime> 
</configuration> 
+0

Merci pour l'idée Jim, mais malheureusement, cela n'a pas aidé. J'ai même essayé de faire une copie de la poubelle et de lui dire de regarder là mais en vain! – tags2k

1

J'ai un problème similaire, lorsque je mets à jour 2-5 fichiers, web.config éther, d'autres fichiers, et asp.net doit recréer les fichiers en cours d'exécution, puis parfois ne trouve pas certaines classes/fonctions qui existent sur les fichiers dll, et mon système ne fonctionne pas - lancer des erreurs comme le vôtre.

Ma solution à cela est que je place le app_offline.htm à la racine, faire mes mises à jour, puis renommer/supprimer le app_offline.htm et mon système fonctionne très bien.

Je suis sûr que cela a quelque chose à voir avec les fichiers compilés mis en cache, mais je n'ai pas vraiment cherché quoi exactement.

[quelle est la solution la plus élégante pour obtenir toutes les assemblées dans le bac à rechargent]

Maintenant, quelle est la solution de contournement la plus élégante est d'appeler le HttpRuntime.UnloadAppDomain et faire réellement votre application à arrêter et recommence.

http://msdn.microsoft.com/en-us/library/system.web.httpruntime.unloadappdomain(VS.80).aspx

Je ne sais pas si cela résout votre problème, vous devez faire des tests.

probablement Global.asax faire quelque chose comme ça

void Application_Error(object sender, EventArgs e) 
{ 
    Exception ex = Server.GetLastError().GetBaseException(); 
    ...if ex is your error, and you get more than 2 ... 
    { 
    HttpRuntime.UnloadAppDomain(); 
    } 
} 
+0

Merci Aristos! C'est bon d'entendre que je ne suis pas le seul!En termes de solution, j'ai essayé de l'implémenter avant avec une redirection juste avant l'appel afin que le visiteur qui déclenche le rechargement ne remarque rien sauf un petit délai pendant le démarrage de l'application. Cependant, je ne suis pas sûr si l'appel UnloadAppDomain a fait le travail car j'ai obtenu une page "Objet déplacé vers ici", sans chargement de mon site. Je vais donner une autre chance à cette méthode. – tags2k

+0

oui s'il vous plaît faire un test sur cette méthode. J'ai mis en œuvre cette méthode moi-même et son fonctionnement, mais je ne sais pas ce qui se passe dans votre cas. – Aristos

+0

Salut Aristos, j'ai essayé à nouveau mais j'ai eu le même problème - sans redirection j'ai l'erreur; Si je redirige l'utilisateur, je reçois une page "Objet déplacé vers". Merci pour votre contribution. – tags2k

2

Comme vous le savez évidemment, there are many situations where the current appdomain is unloaded and reloaded. Après chaque rechargement, tous les assemblys sont déchargés et l'application entière commence à fonctionner "à partir de zéro".

Les assemblys sont chargés par défaut à la demande. C'est généralement le cas lorsque le JIT tombe sur une référence. En conséquence, un rechargement d'appdomain effacera les assemblages dans le domaine d'application et ils n'apparaîtront plus que lorsque le JIT les chargera.

En tant que solution, je reviendrai à utiliser la méthode statique Type.GetType() et fournir un nom qualifié d'assembly (par exemple un nom de type avec le nom de l'assembly inclus). C'est la même chose que le framework utilise lors du chargement des types spécifiés dans le fichier de configuration, et il s'assurera que l'assembly requis est recherché et chargé à la demande sans utiliser de trucs. Voir la section remarques de la méthode ci-dessus (le nom de la méthode au dessus du nom est un lien).

Cela nécessitera des mises à jour de votre base de données pour contenir des noms qualifiés d'assembly au lieu de noms de type «uniquement» complets. Cependant, il s'assure également que vous ne rencontrez pas de collisions de noms lorsque deux assemblages fournissent un type différent avec le même nom, donc c'est une bonne idée de toute façon je pense.

+0

Mon problème avec l'utilisation de 'Type.GetType()' (et la raison pour laquelle je ne l'ai pas décidé dans la phase de conception initiale) était que si je stocke le nom de type de la version 1.1.0.0 d'un assembly et ensuite mise à niveau 1.1.0.1, il ne me reste plus aucun des modules précédents. Quand je ne peux plus charger le type parce que c'est une version différente, je dois employer des trucs encore plus sales pour re-référencer les modules à la bonne version. Existe-t-il un moyen de stocker l'espace de noms de manière à autoriser toute version future de l'assemblage? – tags2k

+0

Oui, laissez simplement la version. Cela fonctionnera au moins pour les assemblées qui n'ont pas de nom fort. Ou vous pouvez ajouter des redirections de version d'assembly via la stratégie. http://msdn.microsoft.com/en-us/library/7wd6ex19.aspx – Lucero

+0

Dans la mesure où les informations de version vont .net se lie à la version d'assembly d'une DLL où, comme Windows et MSI utilisent des versions de fichier. Vous pouvez incrémenter la version du fichier indépendamment de la version de l'assembly afin que la liaison .net ne se brise pas après chaque nouvelle génération. Par exemple: votre numéro de version d'assembly peut rester à 1.1.0.0 et votre numéro de version de fichier peut incrémenter un numéro de build après chaque génération. –

0

Je voudrais essayer de créer une classe de base à partir de laquelle assemblage, ce qui est intéressant pour vous sans réflexion sur Application Start pour s'assurer qu'il est chargé. E.g.

var temp = new BaseModuleBuilder(); 

Cela ne semble pas intelligent, mais il est très simple et asp.net devrait faire tout pour vous. Dans le cas où votre liste est trop dynamique, il pourrait être quelque chose comme

var temp = Activator.CreateInstance(Type.GetType("BaseModuleBuilder, Modules.dll")); 

Assurez-vous de toujours spécifier DLL lorsque vous travaillez avec des classes dynacmically chargées.

+0

Merci Sergey, mais ce n'est pas possible car cela nécessiterait une référence d'assemblage circulaire! – tags2k

Questions connexes