Question

J'ai parfois besoin de pouvoir charger des assemblys dans le GAC en fonction de leurs noms partiels. Pour ce faire, j'ai ajouté les éléments suivants à mon fichier app.config:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <qualifyAssembly partialName="MyAssembly"
                     fullName= "MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef"/>
  </assemblyBinding>
</runtime>

Cela fonctionne exactement comme je le souhaite. Cependant, si je place le même élément dans mon fichier machine.config, il semble être ignoré et j'obtiens FileNotFoundExceptions lors de la tentative de chargement de MyAssembly.

Voici le journal de liaison d'assembly lorsque l'élément se trouve dans mon app.config et que la liaison aboutit:

LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Documents and Settings\jon_scheiding\My Documents\Source\Testing\Test Projects 1\Cmd\bin\Debug\Testers.Cmd.vshost.exe.config
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Partial reference qualified from config file. New reference: MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef.
LOG: Post-policy reference: MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef
LOG: Found assembly by looking in the GAC.
LOG: Binding succeeds. Returns assembly from C:\WINDOWS\assembly\GAC_MSIL\MyAssembly\1.0.0.0__b20f4683c1030dbd\MyAssembly.dll.
LOG: Assembly is loaded in default load context.

Comparez cela avec le journal lorsque ma configuration est dans machine.config et que la liaison échoue:

LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Documents and Settings\jon_scheiding\My Documents\Source\Testing\Test Projects 1\Cmd\bin\Debug\Testers.Cmd.vshost.exe.config
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly.DLL.
LOG: Attempting download of new URL file:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly/MyAssembly.DLL.
LOG: Attempting download of new URL file:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly.EXE.
LOG: Attempting download of new URL file:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly/MyAssembly.EXE.
LOG: All probing URLs attempted and failed.

Le problème semble être la quatrième ligne, & "La stratégie n'est pas appliquée pour faire référence à l'heure actuelle. &"; Cependant, je ne trouve que très peu de documentation sur la signification de ce message et sur la manière de le traiter.

Comment puis-je obtenir que le framework reconnaisse mon < runtime > élément?

Merci d'avance!

Était-ce utile?

La solution

Le framework ne lira pas la configuration qualificative de Assebmly à partir de machine.config, il le lira uniquement à partir du fichier de configuration de votre application.

Le cadre reconnaît votre élément runtim, mais il ne reconnaît pas l’élément qualAssembly.

Autres conseils

Je suis revenu sur ce besoin beaucoup plus tard, car nous avons des assemblées au sein du GAC et des choses comme NHibernate sont nécessaires pour les résoudre. Plutôt que d'ajouter tous ces éléments qualificatifs à chaque app.config, le code suivant nous permet de les ajouter à notre machine.config.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;

namespace MyNamespace {

    /// <summary>
    /// Implements <see cref="IDisposable"/> to provide a scope for resolving
    /// assemblies described in the machine.config file with 
    /// &lt;qualifyAssembly&gt; elements.
    /// </summary>
    /// <remarks>
    /// Because the framework only respects &lt;qualifyAssembly&gt; at the
    /// application configuration level, this class provides similar
    /// functionality for this element at the machine configuration level.
    /// You can wrap a new instance of this class in a <b>using</b> statement
    /// to get resolution within a specific scope; or, you can call the 
    /// <see cref="AssemblyResolver.Attach()"/> method to get 
    /// resolution for the lifetime of the current <see cref="AppDomain"/>.
    /// </remarks>
    public sealed class AssemblyResolver : IDisposable {

        #region Private fields and implementation

        private static Dictionary<string, string> _qualifiedNames;

        private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) {
            if(_qualifiedNames == null) {
                //
                // Lazily initialize short/long name mappings.
                //
                _qualifiedNames = BuildQualifiedNameList();
            }

            if(!_qualifiedNames.ContainsKey(args.Name)) {
                return null;
            }

            try {
                return Assembly.Load(_qualifiedNames[args.Name]);
            }
            catch(FileNotFoundException) {
                //
                // TODO: Should this exception be propogated?
                // It probably should not be hidden from the consumer
                // since it likely indicates a configuration error.
                //
                return null;
            }
        }

        private static Dictionary<string, string> BuildQualifiedNameList() {
            var dict = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

            //
            // Get runtime XML data
            //
            var xml = GetConfigXml(GetMachineRuntimeSection());
            if(xml == null) {
                return dict;
            }

            //
            // Iterate the qualifyAssembly elements and register in the list
            //
            var navigator = xml.CreateNavigator();
            foreach(XPathNavigator qualifyAssembly in navigator.Select("runtime/asm:assemblyBinding/asm:qualifyAssembly", CreateNamespaceManager())) {
                dict.Add(
                    qualifyAssembly.GetAttribute("partialName", string.Empty),
                    qualifyAssembly.GetAttribute("fullName", string.Empty));
            }

            return dict;
        }

        private static ConfigurationSection GetMachineRuntimeSection() {
            System.Configuration.Configuration machineConfig = ConfigurationManager.OpenMachineConfiguration();
            return machineConfig.GetSection("runtime") as ConfigurationSection;
        }

        private static IXPathNavigable GetConfigXml(ConfigurationSection runtimeSection) {
            var ignoreSection = runtimeSection as IgnoreSection;
            if(ignoreSection == null) {
                return null;
            }

            //
            // Cheat via Reflection to get the XML content of the config
            // section.
            //
            FieldInfo field = typeof(IgnoreSection).GetField("_rawXml", BindingFlags.Instance | BindingFlags.NonPublic);
            string rawXml = (string)field.GetValue(ignoreSection);
            return new XPathDocument(new StringReader(rawXml));
        }
        private static IXmlNamespaceResolver CreateNamespaceManager() {
            var nsmgr = new XmlNamespaceManager(new NameTable());
            nsmgr.AddNamespace("asm", "urn:schemas-microsoft-com:asm.v1");
            return nsmgr;
        }

        #endregion

        /// <summary>
        /// Creates a new <see cref="AssemblyResolver"/>.
        /// </summary>
        public AssemblyResolver() {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
        }

        /// <summary>
        /// Removes the current <see cref="AssemblyResolver"/>
        /// from the <see cref="AppDomain"/>.
        /// </summary>
        public void Dispose() {
            AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(ResolveAssembly);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Provides qualified assembly resolution for the lifetime of the
        /// current <see cref="AppDomain"/>.
        /// </summary>
        public static void Attach() {
            Attach(AppDomain.CurrentDomain);
        }

        /// <summary>
        /// Provides qualified assembly resolution for the lifetime of an
        /// <see cref="AppDomain"/>.
        /// </summary>
        /// <param name="appDomain">
        /// The <see cref="AppDomain"/> to service.
        /// </param>
        public static void Attach(AppDomain appDomain) {
            appDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
        }

    }
}

Dans la plupart des cas, nous en limitons la portée, en l'utilisant comme suit:

using(new AssemblyResolver()) {
    //
    // NHibernate initialization or whatever
    //
}

Il peut également être utilisé dans le code de démarrage de l'application ou dans le fichier global.asax et persister pendant toute la durée de vie de l'application, comme suit:

AssemblyResolver.Attach();
//
// or
//
AssemblyResolver.Attach(AppDomain.CurrentDomain);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top