自定义配置部分:无法加载文件或程序集

发布于 2024-08-09 22:40:50 字数 1160 浏览 2 评论 0原文

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

配置文件是从作为插件加载的 .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 的配置文件,但我就是无法让它工作。有什么想法吗?谢谢。

I'm having a very hard time trying to access a custom configuration section in my config file.

The config file is being read from a .dll that is loaded as a plug-in. I created the Configuration and necessary code using the Configuration Section Designer VS addin.

The namespace is 'ImportConfiguration'. The ConfigurationSection class is 'ImportWorkflows'. The assembly is ImportEPDMAddin.

The xml:

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

Whenever I try to read in the config, I get the error:

An error occurred creating the configuration section handler for importWorkflows: Could not load file or assembly 'ImportEPDMAddin.dll' or one of its dependencies. The system cannot find the file specified.

The dll will not reside in the same directory as the executable as the software that loads the plugin places the dll and it's dependencies in it's own directory. (I can't control that.)

I edited the code for the singleton instance to the following:

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;

I have also tried using a simple NameValueFileSectionHandler as well, but I get an exception saying that it can't load file or assembly 'System'.

I have read numerous blog posts and articles and it sounds like it is possible to read a config file in for a dll, but I just can't get it to work. Any ideas? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

画中仙 2024-08-16 22:40:50

不幸的是,您需要将 ImportEPDMAddin 程序集驻留在与可执行文件相同的文件夹中,驻留在与您正在使用的 .Net 框架相关的 .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;
}

确保处理 AssemblyResolve 事件,因为如果没有它,这将引发异常。

Unfortunately, you will need to either have the ImportEPDMAddin assembly residing in the same folder as your executable, residing in the .Net framework folder related to the .Net framework you are using (i.e., C:\Windows\Microsoft.NET\Framework\v2.0.50727), or registered in the Global Assembly Cache.

The only other option is, if you know the path to the assembly that contains the configuration handler's defining class, you can load it without a reference with something like this:

//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;
}

Make sure you handle the AssemblyResolve event, as this will throw an exception without it.

余生共白头 2024-08-16 22:40:50

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

<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

In your main applications config file, add the following (where plugins is the folder for your assembly to load from. You can use multiple paths semi-colon separated.

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

Taken from http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx

随遇而安 2024-08-16 22:40:50

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

To expand on AJ's excellent answer, here is a custom class to assist with the overhead of registering and removing the global event.

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;
    }
}

I would suggest creating an instance in a using statement, like so:

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

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

你确定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

Have you made sure that DLL is loaded first? Perhaps with Assembly.LoadFile("PATH")?

If you can't get the classes in System.Configuration to work properly, you can always fall back on using XmlDocument to manually parse the configuration file. Use XPaths to make getting the data easier. For example (assuming your path variable above):

var document = new XmlDocument();
document.Load(path);
var node = document.SelectSingleNode("configuration/importWorkflows/add[@name='KEY']");
// Do whatever with node
我乃一代侩神 2024-08-16 22:40:50

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

程序集绑定 -> 探测

Could you verify that the probing paths are setup correctly in your Host application's config file? It is possible that a needed reference is not being loaded in your current application domain.

Assembly Binding ->Probing

埋葬我深情 2024-08-16 22:40:50

必须使用我的模块/插件程序集的完全限定类型字符串,该程序集位于探测目录中,以便可以找到它。以 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"

Had to use the fully qualified type string of my module/plugin assembly, which is in a probing directory, so it could be located. Using EntityFramework as an example...

Incorrect:

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

Correct

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
菊凝晚露 2024-08-16 22:40:50

我尝试了 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>
}

I tried AJ's answer, with rileywhite's supplement but I found that did not work for me.

In my scenario, the custom ConfigurationSection class was already in the currently-executing assembly, and trying to load it causes a stack overflow. I also did not want to put it in GAC even though it did resolve the problem as reported by the OP.

In end, I found this to work well enough for my purpose. Maybe others will find it useful:

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>
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文