C# 自定义配置节:无法加载文件或程序集

C# 自定义配置节:无法加载文件或程序集,c#,config,C#,Config,我很难访问配置文件中的自定义配置部分 正在从作为插件加载的.dll读取配置文件。我使用VS插件创建了配置和必要的代码 命名空间为“ImportConfiguration”。ConfigurationSection类是“ImportWorkflows”。该程序集已导入ADDIN xml: <configSections> <section name="importWorkflows" type="ImportConfiguration.ImportWorkflows,

我很难访问配置文件中的自定义配置部分

正在从作为插件加载的.dll读取配置文件。我使用VS插件创建了配置和必要的代码

命名空间为“ImportConfiguration”。ConfigurationSection类是“ImportWorkflows”。该程序集已导入ADDIN

xml:

  <configSections>
    <section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/>
  </configSections>
我也尝试过使用一个简单的NameValueFileSectionHandler,但我得到一个异常,它无法加载文件或程序集“System”


我读过很多博客文章和文章,听起来好像可以读入dll的配置文件,但我就是无法让它工作。有什么想法吗?谢谢。

您是否确保先加载DLL?可能使用
Assembly.LoadFile(“路径”)

如果无法使System.Configuration中的类正常工作,则始终可以使用XmlDocument手动解析配置文件。使用XPath可以更轻松地获取数据。例如(假设上面的path变量):


您能否验证探测路径是否在主机应用程序的配置文件中正确设置?您当前的应用程序域中可能没有加载所需的引用


不幸的是,您需要将
importedmaddin
程序集与您的可执行文件驻留在同一文件夹中,驻留在与您正在使用的.Net framework相关的.Net framework文件夹中(即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;
}
//类全局
专用装配配置定义装配;
受保护的TConfig GetCustomConfig(字符串配置定义AssemblyPath,
字符串configFilePath,字符串sectionName),其中TConfig:ConfigurationSection
{
AppDomain.CurrentDomain.AssemblyResolve+=新建
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)作为TConfig;
AppDomain.CurrentDomain.AssemblyResolve-=ConfigResolveEventHandler;
返回returnConfig;
}
受保护的程序集ConfigResolveEventHandler(对象发送方、ResolveEventArgs args args)
{
返回配置定义组件;
}

请确保处理AssemblyResolve事件,因为这将在没有异常的情况下引发异常。

在主应用程序配置文件中,添加以下内容(其中插件是要从中加载程序集的文件夹)。您可以使用分号分隔的多个路径



要扩展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");
}

我试着用AJ的答案和莱利怀特的补充品,但我发现这对我不起作用

在我的场景中,自定义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>
}
公共类CustomConfigurationSection:ConfigurationSection{
公共CustomConfigurationSection()
{
var reader=XmlReader.Create();
reader.ReadToDescendant(“CustomConfigurationSection”);
反序列化元素(reader,false);
}
// 
}

必须使用我的模块/插件程序集的完全限定类型字符串,它位于探测目录中,因此可以找到它

不正确:

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"

您是否也将
importedmaddin.dll.config
复制到了同一个位置?配置肯定在那里,因为我尝试使用另一个类中的DictionarySectionHandler,而且效果很好。此方法有效,但我希望继续使用配置类。如果所有其他方法都失败,我将不得不接受您的建议。这非常有效用于访问和强制转换T4文件中自定义节的类型。谢谢!@AJ。对不起,请您解释一下什么是
配置定义AssemblyPath
。是.exe文件吗?在单元测试情况下使用此文件非常有效。感谢使用探测来解决OP的问题仅在p中指定的插件文件夹时才有效rivatePath属性是应用程序根目录的子目录。请参阅
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"