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

时间:2009-11-05 18:42:26

标签: c# config

我在配置文件中尝试访问自定义配置部分非常困难。

正在从作为插件加载的.dll中读取配置文件。我使用Configuration Section Designer 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,但我得到一个例外,说它无法加载文件或程序集'System'。

我已经阅读了大量的博文和文章,听起来有可能为dll读取配置文件,但我无法让它工作。有任何想法吗?感谢。

7 个答案:

答案 0 :(得分: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事件,因为这会在没有它的情况下抛出异常。

答案 1 :(得分:6)

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

<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

答案 2 :(得分: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");
}

答案 3 :(得分:1)

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

Assembly Binding ->Probing

答案 4 :(得分:0)

您是否确定首先加载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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

<强>不正确:

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    fstream myfile;
    string str1;
    myfile.open("H:/input_file.txt");
    myfile<<"test1 writing files"<<" ";
    myfile>>str1;
    cout<<str1<<endl;
    myfile<<"test2 append writing files"<<" ";
    myfile>>str1;
    cout<<str1<<endl;
    myfile.close();

    return 0;

}

<强>正确

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