Пользовательский раздел конфигурации: не удалось загрузить файл или сборку

Мне очень трудно получить доступ к пользовательскому разделу конфигурации в моем файле конфигурации.

Файл конфигурации считывается из библиотеки .dll, которая загружается как подключаемый модуль. Я создал конфигурацию и необходимый код с помощью конструктора разделов конфигурации надстройки VS.

Пространство имен — «ImportConfiguration». Класс ConfigurationSection — «ImportWorkflows». Сборка ImportEPDMAddin.

XML:

  <configSections>
    <section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/>
  </configSections>

Всякий раз, когда я пытаюсь прочитать конфигурацию, я получаю сообщение об ошибке:

Произошла ошибка при создании обработчика раздела конфигурации для importWorkflows: не удалось загрузить файл или сборку «ImportEPDMAddin.dll» или одну из ее зависимостей. Система не может найти указанный файл.

dll не будет находиться в том же каталоге, что и исполняемый файл, поскольку программное обеспечение, загружающее плагин, помещает dll и его зависимости в свой собственный каталог. (Я не могу это контролировать.)

Я отредактировал код экземпляра singleton следующим образом:

string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase;
path = path.Replace("file:///", "");
System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(path);
return configuration.GetSection(ImportWorkflowsSectionName) as ImportConfiguration.ImportWorkflows;

Я также пытался использовать простой NameValueFileSectionHandler, но получаю исключение, говорящее, что он не может загрузить файл или сборку «Система».

Я прочитал множество сообщений и статей в блогах, и похоже, что можно прочитать файл конфигурации для dll, но я просто не могу заставить его работать. Есть идеи? Спасибо.


person ehcanadian    schedule 05.11.2009    source источник
comment
Вы тоже скопировали ImportEPDMAddin.dll.config в то же место?   -  person ephemient    schedule 05.11.2009
comment
Конфигурация точно есть, так как я пытался использовать DictionarySectionHandler из другого класса, и это работает.   -  person ehcanadian    schedule 05.11.2009


Ответы (7)


К сожалению, вам потребуется либо иметь сборку ImportEPDMAddin, находящуюся в той же папке, что и ваш исполняемый файл, либо в папке .Net framework, связанной с используемой вами платформой .Net (т. е. C:\Windows\Microsoft.NET\Framework\ v2.0.50727) или зарегистрирован в глобальном кэше сборок.

Единственный другой вариант: если вы знаете путь к сборке, содержащей класс, определяющий обработчик конфигурации, вы можете загрузить его без ссылки примерно так:

//Class global
private Assembly configurationDefiningAssembly;

protected TConfig GetCustomConfig<TConfig>(string configDefiningAssemblyPath, 
    string configFilePath, string sectionName) where TConfig : ConfigurationSection
{
    AppDomain.CurrentDomain.AssemblyResolve += new 
        ResolveEventHandler(ConfigResolveEventHandler);
    configurationDefiningAssembly = Assembly.LoadFrom(configDefiningAssemblyPath);
    var exeFileMap = new ExeConfigurationFileMap();
    exeFileMap.ExeConfigFilename = configFilePath;
    var customConfig = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap, 
        ConfigurationUserLevel.None);
    var returnConfig = customConfig.GetSection(sectionName) as TConfig;
    AppDomain.CurrentDomain.AssemblyResolve -= ConfigResolveEventHandler;
    return returnConfig;
}

protected Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
{
    return configurationDefiningAssembly;
}

Убедитесь, что вы обрабатываете событие AssemblyResolve, так как это приведет к возникновению исключения без него.

person AJ.    schedule 05.11.2009
comment
Это работало как шарм для доступа и приведения типа пользовательского раздела в файле T4. Спасибо! - person Matt; 23.10.2014
comment
@АДж. Извините, не могли бы вы объяснить мне, что такое configDefiningAssemblyPath.. это файл .exe? - person Ciccio; 20.11.2014
comment
Использование этого в ситуации модульного тестирования сработало. Спасибо - person stevethethread; 02.06.2016

В файле конфигурации вашего основного приложения добавьте следующее (где plugins — это папка для загрузки вашей сборки. Вы можете использовать несколько путей, разделенных точкой с запятой.

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath=".;.\Plugins"/>
    </assemblyBinding>
</runtime>

Взято с http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx

person RichardP    schedule 09.01.2013
comment
Обратите внимание, что использование зондирования для решения проблемы OP будет работать только в том случае, если папка плагинов, указанная в атрибуте privatePath, является подкаталогом корневого каталога приложения. См. документацию msdn. - person BitMask777; 06.01.2015

Чтобы расширить отличный ответ AJ, вот пользовательский класс, который поможет с накладными расходами на регистрацию и удаление глобального события.

public sealed class AddinCustomConfigResolveHelper : IDisposable
{
    public AddinCustomConfigResolveHelper(
        Assembly addinAssemblyContainingConfigSectionDefinition)
    {
        Contract.Assert(addinAssemblyContainingConfigSectionDefinition != null);

        this.AddinAssemblyContainingConfigSectionDefinition =
            addinAssemblyContainingConfigSectionDefinition;

        AppDomain.CurrentDomain.AssemblyResolve +=
            this.ConfigResolveEventHandler;
    }

    ~AddinCustomConfigResolveHelper()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool isDisposing)
    {
        AppDomain.CurrentDomain.AssemblyResolve -= this.ConfigResolveEventHandler;
    }

    private Assembly AddinAssemblyContainingConfigSectionDefinition { get; set; }

    private Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
    {
        // often the name provided is partial...this will match full or partial naming
        if (this.AddinAssemblyContainingConfigSectionDefinition.FullName.Contains(args.Name))
        {
            return this.AddinAssemblyContainingConfigSectionDefinition;
        }

        return null;
    }
}

Я бы предложил создать экземпляр в операторе using, например:

// you'll need to populate these two variables
var configuration = GetConfiguration();
var assembly = GetAssemblyContainingConfig();

using(new AddinCustomConfigResolveHelper(assembly))
{
    return (MyConfigSection)configuration.GetSection("myConfigSection");
}
person rileywhite    schedule 28.11.2013

Вы убедились, что DLL загружается первой? Возможно с Assembly.LoadFile("PATH")?

Если вы не можете заставить классы в System.Configuration работать должным образом, вы всегда можете вернуться к использованию XmlDocument для ручного анализа файла конфигурации. Используйте XPaths, чтобы упростить получение данных. Например (при условии, что ваша переменная пути указана выше):

var document = new XmlDocument();
document.Load(path);
var node = document.SelectSingleNode("configuration/importWorkflows/add[@name='KEY']");
// Do whatever with node
person Community    schedule 05.11.2009
comment
Этот метод работает, но я надеялся придерживаться классов конфигурации. Если ничего не поможет, мне придется согласиться с вашим предложением. - person ehcanadian; 05.11.2009

Не могли бы вы убедиться, что пути проверки правильно настроены в файле конфигурации вашего хост-приложения? Возможно, необходимая ссылка не загружается в ваш текущий домен приложения.

Привязка сборки -> Проверка

person Mark Coleman    schedule 05.11.2009

Пришлось использовать полную строку типа моей сборки модуля/плагина, которая находится в каталоге зондирования, чтобы ее можно было найти. Использование EntityFramework в качестве примера...

Неверно:

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"

Верно

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
person wintondeshong    schedule 06.03.2016

Я попробовал ответ AJ с дополнением rileywhite, но обнаружил, что это не работает для меня.

В моем сценарии пользовательский класс ConfigurationSection уже был в текущей сборке, и попытка загрузить его вызывает переполнение стека. Я также не хотел помещать его в GAC, хотя это решило проблему, как сообщает OP.

В конце концов, я обнаружил, что это работает достаточно хорошо для моей цели. Может другим будет полезно:

public class CustomConfigurationSection : ConfigurationSection {
  public CustomConfigurationSection()
  {
    var reader = XmlReader.Create(<path to my dll.config>);
    reader.ReadToDescendant("CustomConfigurationSection");
    base.DeserializeElement(reader,false);
  }

  // <rest of code>
}
person this    schedule 23.05.2014