سؤال

ولدي الحالة التي أنا بحاجة إلى أن تكون قادرة على تحميل التجميعات في 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 بلدي، على ما يبدو لا يمكن تجاهله، وأحصل على FileNotFoundExceptions عند محاولة تحميل MyAssembly.

وفيما يلي سجل ملزمة التجمع عندما كان العنصر في بلدي app.config، وربط ينجح:

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.

وعلى النقيض من ذلك مع سجل عند تكوين بلدي في machine.config، وفشل ربط:

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.

والمشكلة يبدو أن السطر الرابع، "سياسة عدم تطبيق مرجع في هذا الوقت." ومع ذلك، لا أستطيع العثور على وثائق قليلة جدا عن ما تعنيه هذه الرسالة، أو كيفية معالجة ذلك.

وكيف يمكنني الحصول على إطار للاعتراف بلدي <وقت التشغيل> العنصر؟

ويرجع الفضل في ذلك مسبقا!

هل كانت مفيدة؟

المحلول

وسوف الإطار لا يقرأ التكوين qualifyAssebmly من machine.config، فإنه يقرأ فقط من ملف تكوين التطبيق الخاص بك.

والإطار لا تعترف عنصر runtim، ومع ذلك أنها لا تعترف العنصر qualifyAssembly.

نصائح أخرى

وعدت إلى هذه الحاجة في وقت لاحق من ذلك بكثير، كما لدينا بعض التجميعات في 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);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top