Пользовательский раздел конфигурации: не удалось загрузить файл или сборку
Мне очень тяжело пытаться получить доступ к настраиваемому разделу конфигурации в моем файле конфигурации.
Файл конфигурации считывается из .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, но я просто не могу заставить его работать. Есть идеи? Спасибо.
Ответы
Ответ 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"