0

我有一种情况,我需要能够根据它们的部分名称在 GAC 中加载程序集。为此,我在 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>

这正是我想要的方式。但是,如果我在 machine.config 文件中放置相同的元素,它似乎会被忽略,并且在尝试加载 MyAssembly 时会出现 FileNotFoundExceptions。

以下是元素在我的app.config中时的程序集绑定日志,并且绑定成功:

LOG:此绑定在默认加载上下文中开始。
LOG:使用应用程序配置文件:C:\Documents and Settings\jon_scheiding\My Documents\Source\Testing\Test Projects 1\Cmd\bin\Debug\Testers.Cmd.vshost.exe.config
LOG:使用 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config 中的机器配置文件。
LOG:部分引用来自配置文件。新参考:MyAssembly,Version=1.0.0.0,Culture=neutral,PublicKeyToken=0123456789abcdef。
日志:政策后参考:MyAssembly,Version=1.0.0.0,Culture=neutral,PublicKeyToken=0123456789abcdef
日志:通过查看 GAC 找到程序集。
LOG:绑定成功。从 C:\WINDOWS\assembly\GAC_MSIL\MyAssembly\1.0.0.0__b20f4683c1030dbd\MyAssembly.dll 返回程序集。
LOG:程序集在默认加载上下文中加载。

将其与我的配置在 machine.config 中时的日志进行对比,并且绑定失败:

LOG:此绑定在默认加载上下文中开始。
LOG:使用应用程序配置文件:C:\Documents and Settings\jon_scheiding\My Documents\Source\Testing\Test Projects 1\Cmd\bin\Debug\Testers.Cmd.vshost.exe.config
LOG:使用 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config 中的机器配置文件。
LOG:此时未将策略应用于引用(私有、自定义、部分或基于位置的程序集绑定)。
日志:正在尝试下载新的 URL 文件:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly.DLL。
日志:正在尝试下载新的 URL 文件:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly/MyAssembly.DLL。
日志:尝试下载新的 URL 文件:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly.EXE。
日志:尝试下载新的 URL 文件:///C:/Documents and Settings/jon_scheiding/My Documents/Source/Testing/Test Projects 1/Cmd/bin/Debug/MyAssembly/MyAssembly.EXE。
LOG:所有探测 URL 都已尝试并失败。

问题似乎是第四行,“Policy not being applied to reference at this time”。但是,关于此消息的含义或如何解决它,我几乎找不到文档。

如何让框架识别我的 <runtime> 元素?

提前致谢!

4

2 回答 2

2

该框架不会从 machine.config 中读取qualifyAssebmly 配置,它只会从您的应用程序配置文件中读取它。

该框架确实可以识别您的运行时元素,但是它不能识别qualifyAssembly 元素。

于 2008-12-03T07:17:01.107 回答
2

我很晚才回到这个需求,因为我们在 GAC 中有一些程序集,而 NHibernate 之类的东西需要解决它们。不是将所有这些qualifyAssembly 元素添加到每个app.config,下面的代码允许我们将它们添加到我们的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);
        }

    }
}

在大多数情况下,我们通过像这样使用它来限制它的范围:

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

它也可以在应用程序启动代码或 global.asax 中使用,并在应用程序的生命周期内持续存在,如下所示:

AssemblyResolver.Attach();
//
// or
//
AssemblyResolver.Attach(AppDomain.CurrentDomain);
于 2011-02-04T16:03:54.733 回答