Pregunta

Tengo una situación en la que necesito poder cargar ensamblados en el GAC en función de sus nombres parciales. Para hacer esto, he agregado lo siguiente a mi archivo 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>

Esto funciona exactamente como yo quiero. Sin embargo, si coloco el mismo elemento en mi archivo machine.config, parece ser ignorado, y obtengo FileNotFoundExceptions cuando intento cargar MyAssembly.

El siguiente es el registro de enlace de ensamblaje cuando el elemento está en mi app.config, y el enlace tiene éxito:

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.

Compare eso con el registro cuando mi configuración está en machine.config, y el enlace falla:

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.

El problema parece ser la cuarta línea, " La política no se aplica a la referencia en este momento. " Sin embargo, puedo encontrar muy poca documentación sobre lo que significa este mensaje o cómo abordarlo.

¿Cómo puedo hacer que el framework reconozca mi < runtime > elemento?

¡Gracias de antemano!

¿Fue útil?

Solución

El marco no leerá la configuración de qualAssebmly de machine.config, solo la leerá del archivo de configuración de la aplicación.

El marco reconoce su elemento runtim, sin embargo, no reconoce el elemento qualifyAssembly.

Otros consejos

Volví a esta necesidad mucho más tarde, ya que tenemos algunos ensamblajes en el GAC y cosas como NHibernate necesitaban resolverlos. En lugar de agregar todos estos elementos de qualAsAsmbly a cada app.config, el siguiente código nos permite agregarlos a nuestra 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);
        }

    }
}

En la mayoría de los casos, limitamos su alcance, usándolo así:

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

También se puede usar en el código de inicio de la aplicación o global.asax, y persiste durante toda la vida útil de la aplicación, así:

AssemblyResolver.Attach();
//
// or
//
AssemblyResolver.Attach(AppDomain.CurrentDomain);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top