Domanda

Ho una situazione in cui devo poter caricare gli assembly nel GAC in base ai loro nomi parziali. Per fare ciò ho aggiunto quanto segue al mio file 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>

Funziona esattamente come voglio. Tuttavia, se inserisco lo stesso elemento nel mio file machine.config, sembra essere ignorato e ottengo FileNotFoundExceptions quando provo a caricare MyAssembly.

Di seguito è riportato il registro dell'associazione dell'assembly quando l'elemento si trova in my app.config e il collegamento ha esito positivo:

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.

Contrasta quello con il registro quando la mia configurazione è in machine.config e il bind fallisce:

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.

Il problema sembra essere la quarta riga, " la politica non viene applicata al riferimento in questo momento. " Tuttavia, posso trovare pochissima documentazione sul significato di questo messaggio o su come affrontarlo.

Come posso ottenere il framework per riconoscere il mio < runtime > elemento?

Grazie in anticipo!

È stato utile?

Soluzione

Il framework non legge la configurazione qualifyAssebmly da machine.config, la legge solo dal file di configurazione dell'applicazione.

Il framework riconosce il tuo elemento runtim, tuttavia non riconosce l'elemento qualifyAssembly.

Altri suggerimenti

Sono tornato su questa esigenza molto più tardi, poiché abbiamo alcune assemblee nel GAC e cose come NHibernate dovevano risolverle. Invece di aggiungere tutti questi elementi qualifyAssembly a ogni app.config, il codice seguente ci consente di aggiungerli al nostro 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);
        }

    }
}

Nella maggior parte dei casi, ne limitiamo l'ambito, utilizzandolo in questo modo:

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

Può anche essere utilizzato nel codice di avvio dell'app o global.asax e persistere per la durata dell'applicazione, in questo modo:

AssemblyResolver.Attach();
//
// or
//
AssemblyResolver.Attach(AppDomain.CurrentDomain);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top