Подтвердить что ты не робот

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

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

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

Пространство имен - "Импортконфигурация". Класс 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, но я просто не могу заставить его работать. Есть идеи? Спасибо.

4b9b3361

Ответ 1

К сожалению, вам понадобится либо сборка 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, так как это приведет к исключению без него.

Ответ 2

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

<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

Ответ 3

Чтобы расширить 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");
}

Ответ 4

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

Сборка привязки → Исследование

Ответ 5

Вы уверены, что сначала загружена 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

Ответ 6

Я попробовал ответить 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>
}

Ответ 7

Пришлось использовать полностью квалифицированную строку типа моей сборки модуля/плагина, которая находится в каталоге зондирования, чтобы она могла быть расположена. Использование 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"