21

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

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

命名空间是“ImportConfiguration”。ConfigurationSection 类是“ImportWorkflows”。程序集是 ImportEPDMAddin。

xml:

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

每当我尝试读取配置时,都会收到错误消息:

为 importWorkflows 创建配置节处理程序时出错:无法加载文件或程序集“ImportEPDMAddin.dll”或其依赖项之一。该系统找不到指定的文件。

dll 不会与可执行文件位于同一目录中,因为加载插件的软件会将 dll 及其依赖项放在它自己的目录中。(我无法控制。)

我将单例实例的代码编辑为以下内容:

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 读取配置文件,但我就是无法让它工作。有任何想法吗?谢谢。

4

7 回答 7

36

不幸的是,您需要将ImportEPDMAddin程序集与可执行文件位于同一文件夹中,位于与您正在使用的 .Net 框架相关的 .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 事件,因为没有它会引发异常。

于 2009-11-05T19:30:42.207 回答
6

在您的主应用程序配置文件中,添加以下内容(其中 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

于 2013-01-09T09:17:55.243 回答
4

为了扩展 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");
}
于 2013-11-28T09:30:10.377 回答
1

您是否确保首先加载 DLL?也许与Assembly.LoadFile("PATH")

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

var document = new XmlDocument();
document.Load(path);
var node = document.SelectSingleNode("configuration/importWorkflows/add[@name='KEY']");
// Do whatever with node
于 2009-11-05T18:54:45.757 回答
1

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

装配绑定 -> 探测

于 2009-11-05T19:28:06.987 回答
1

必须使用我的模块/插件程序集的完全限定类型字符串,它位于探测目录中,因此可以找到它。以 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"
于 2016-03-06T14:28:40.043 回答
0

我尝试了 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>
}
于 2014-05-23T18:17:21.380 回答