C# DLL配置文件

202

我试图将一个app.config文件添加到我的DLL中,但所有尝试都失败了。

根据MusicGenesis在“将配置信息放入DLL”中的说法,这应该不是问题。所以显然我做错了什么...

以下代码应该从我的DLL中返回我的ConnectionString:

return ConfigurationManager.AppSettings["ConnectionString"];

然而,当我将app.config文件复制到我的控制台应用程序中时,它可以正常工作。

有任何想法吗?


根据参考帖子:如果dll的名称为MyDll.dll,则配置文件应该是MyDLL.dll.config。因此,如果您从dll内部读取配置设置,它应该引用自己的配置,对吗? - MegaByte
11
无论代码要求什么,它都会按照应用程序域指定的文件进行查找:即 AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 设置。 - Marc Gravell
一个注意事项:关于"将配置信息放在DLL中"问题,其涉及到将应用程序的配置代码分离成一个库,以保持其与主应用程序代码分离。这与仅针对DLL单独使用的配置文件是非常不同的。 - Chris Ammerman
查看这篇帖子链接描述在此,对我来说是解决方案。 - dhailis
查看这篇帖子如何动态加载单独的应用程序设置文件并与当前设置合并?可能会有所帮助。 - dhailis
可能是重复的问题:库(dll)的app.config等效项 - Ben
18个回答

286
创建一个.NET配置文件并不容易,这也是有充分理由的。.NET配置机制内置了许多功能,以便轻松升级/更新应用程序,并保护安装的应用程序不会破坏彼此的配置文件。
使用DLL和应用程序之间有很大的区别。对于同一用户在同一台计算机上安装多个应用程序的情况很少发生。但是您可能会有100个不同的应用程序或库都在使用某个.NET DLL。
虽然很少需要为一个用户配置文件中的不同应用程序副本单独跟踪设置,但是不太可能希望所有使用DLL的不同用途共享其配置文件。因此,当使用“正常”方法检索Configuration对象时,您得到的对象与执行所在的应用程序域的配置相关联,而不是特定程序集的配置。
应用程序域绑定到加载包含实际代码的程序集的程序集的根,通常情况下这将是主.EXE的程序集,它加载了.DLL。可以在应用程序中启动其他应用程序域,但必须显式提供有关该应用程序域的根程序集的信息。
由于所有这些原因,创建特定于库的配置文件的过程并不方便。这与创建不与任何特定程序集关联的任意便携式配置文件的过程相同,但您要使用.NET的XML模式、配置部分和配置元素机制等。这需要创建一个ExeConfigurationFileMap对象,加载标识配置文件存储位置的数据,然后调用ConfigurationManager.OpenMappedExeConfiguration将其打开到新的Configuration实例中。这将使您无法利用自动生成路径机制提供的版本保护。

从统计学的角度来看,你可能正在使用这个库在公司内部设置中,不太可能在同一台机器/用户上有多个应用程序在使用它。 但是如果不是这样的话,你需要记住一些事情。 如果你针对你的DLL使用单个全局配置文件,无论引用它的应用程序是什么,你都需要担心访问冲突。 如果两个引用你的库的应用程序恰好同时运行,并且每个应用程序都有自己打开的Configuration对象,那么当一个应用程序保存更改时,它将导致下次尝试在另一个应用程序中检索或保存数据时出现异常。

最安全、最简单的方法是要求加载你的DLL的程序集也提供一些关于它自己的信息,或者通过检查引用程序集的应用程序域来检测它。 使用这个信息来创建某种文件夹结构,为每个引用你的DLL的应用程序保留单独的用户配置文件。

如果你确信想要在任何地方引用DLL都有全局设置,那么你需要确定它的位置,而不是.NET自动找到适当的位置。 你还需要积极地管理文件访问权限。 尽可能地缓存,仅在加载或保存时保留Configuration实例,立即打开和释放。 最后,你需要一个锁定机制来保护在使用库的某个应用程序编辑文件时。


9
哎,我们的企业级应用程序是由不同时区的人编写主要 .exe 文件,并由各种 DLL 表示模块并通过自定义插件框架动态绑定。所有这些“你需要确保多个应用程序可以同时使用您的 DLL”之类的自命不凡都是完全错误的。 - JohnL4
此外,在我的职业生涯的很大一部分中,我看到这些可爱的通用共享对象机制被完全忽略,团队创建的 DLL(或 JAR)只能在一个上下文中使用(必须存在,否则应用程序将失败)。它们可能与静态绑定一样,但那已经过时了。 - JohnL4
@JohnL4 你在逗我吧?Chris 是指 dll.config 文件而不是 dll 文件本身。希望没有人会认真对待那些评论 -_- - user1567453
2
从统计学的角度来看,你可能正在内部环境中使用这个库,而且很少有多个应用程序在同一台机器/用户上使用它。理论和实践之间的差异有时会让我感到非常不爽。 - JohnL4
1
@Panzercrisis,Visual Studio的Settings.settings功能会自动为所有用户设置创建特定版本的路径。请参见:https://dev59.com/-ZTfa4cB1Zd3GeqPUrQI - Deantwo
显示剩余3条评论

111

如果你想从DLL的配置文件中读取设置,但不想从根应用程序的web.config或app.config中读取,请使用以下代码来读取dll中的配置。

var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
string dllConfigData = appConfig.AppSettings.Settings["dllConfigData"].Value;

1
在VS 2008的托管C++中,System::Configuration::Configuration^ appConfig = ConfigurationManager::OpenExeConfiguration(Assembly::GetExecutingAssembly()->Location); String^ name = appConfig->AppSettings->Settings["name"]->Value; - Hans
1
谢谢,这真的解决了我的问题。我已经处理这个问题大约两天了,直到现在才让它工作。在调试测试时,ConfigurationManager正在读取机器配置文件(我认为),因为提取的连接字符串是关于SQLExpress的 - 连接字符串我没有列出的。 - dotnetspark
这个对我也起作用。我之前把完整的文件名(例如 myDll.dll.config)传递给 OpenExeConfiguration,但没有起作用。而使用 Assembly.GetExecutingAssembly().Location 返回的是名称和文件扩展名(例如 myDll.dll),它则起作用了。谢谢。 - Mick

23
我也遇到了同样的问题,在网上搜索了几个小时,但是找不到任何解决方法,所以我自己想了一个。我想知道为什么.NET配置系统这么不灵活。
背景:我想让我的DAL.dll有自己的配置文件,用于数据库和DAL设置。我还需要Enterprise Library的app.config和其自己的配置。所以我需要app.config和dll.config。
我不想做的是将每个属性/设置从应用程序传递到我的DAL层!
因为我需要它来实现常规app.config行为,所以无法弯曲“AppDomain.CurrentDomain.SetupInformation.ConfigurationFile”。
我的要求/观点是:
- 不手动复制任何内容,从ClassLibrary1.dll.config到WindowsFormsApplication1.exe.config,因为这对其他开发人员来说是不可重现的。 - 保留使用强类型“Properties.Settings.Default.NameOfValue”(设置行为),因为我认为这是一个重要的特性,我不想失去它。 - 我发现ApplicationSettingsBase缺乏注入自己/自定义配置文件或管理的功能(这些类中所有必要的字段都是私有的) - “configSource”文件重定向的使用不可能,因为我们需要复制/重写ClassLibrary1.dll.config,并为几个部分提供几个XML文件(我也不喜欢这种方式) - 我不喜欢为此编写自己的SettingsProvider,因为MSDN建议这太多了 - 我只需要配置文件中的应用程序设置和连接字符串部分
我修改了Settings.cs文件,并实现了一个方法,打开ClassLibrary1.dll.config并在私有字段中读取节信息。然后,我重写了“this[string propertyName]”,所以生成的Settings.Desginer.cs调用我的新属性而不是基类。在那里,从列表中读取设置。
最后,以下是代码:
internal sealed partial class Settings
{
    private List<ConfigurationElement> list;

    /// <summary>
    /// Initializes a new instance of the <see cref="Settings"/> class.
    /// </summary>
    public Settings()
    {
        this.OpenAndStoreConfiguration();
    }

    /// <summary>
    /// Opens the dll.config file and reads its sections into a private List of ConfigurationElement.
    /// </summary>
    private void OpenAndStoreConfiguration()
    {
        string codebase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
        Uri p = new Uri(codebase);
        string localPath = p.LocalPath;
        string executingFilename = System.IO.Path.GetFileNameWithoutExtension(localPath);
        string sectionGroupName = "applicationSettings";
        string sectionName = executingFilename + ".Properties.Settings";
        string configName = localPath + ".config";
        ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
        fileMap.ExeConfigFilename = configName;
        Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

        // read section of properties
        var sectionGroup = config.GetSectionGroup(sectionGroupName);
        var settingsSection = (ClientSettingsSection)sectionGroup.Sections[sectionName];
        list = settingsSection.Settings.OfType<ConfigurationElement>().ToList();

        // read section of Connectionstrings
        var sections = config.Sections.OfType<ConfigurationSection>();
        var connSection = (from section in sections
                           where section.GetType() == typeof(ConnectionStringsSection)
                           select section).FirstOrDefault() as ConnectionStringsSection;
        if (connSection != null)
        {
            list.AddRange(connSection.ConnectionStrings.Cast<ConfigurationElement>());
        }
    }

    /// <summary>
    /// Gets or sets the <see cref="System.Object"/> with the specified property name.
    /// </summary>
    /// <value></value>
    public override object this[string propertyName]
    {
        get
        {
            var result = (from item in list
                         where Convert.ToString(item.ElementInformation.Properties["name"].Value) == propertyName
                         select item).FirstOrDefault();
            if (result != null)
            {
                if (result.ElementInformation.Type == typeof(ConnectionStringSettings))
                {
                    return result.ElementInformation.Properties["connectionString"].Value;
                }
                else if (result.ElementInformation.Type == typeof(SettingElement))
                {
                    return result.ElementInformation.Properties["value"].Value;
                }
            }
            return null;
        }
        // ignore
        set
        {
            base[propertyName] = value;
        }
    }

您只需要将ClassLibrary1.dll.config从ClassLibrary1输出目录复制到应用程序的输出目录。

也许有人会觉得这很有用。


14

使用 ConfigurationManager 时,我相信它会加载进程/ AppDomain 配置文件(app.config / web.config)。 如果您想加载特定的配置文件,您需要明确使用该文件的名称进行请求...

您可以尝试:

var config = ConfigurationManager.OpenExeConfiguration("foo.dll");
config.ConnectionStrings. [etc]

根据引用的帖子:如果 dll 的名称为 MyDll.dll,则配置文件应命名为 MyDLL.dll.config。因此,如果你从 dll 中读取配置设置,它应该引用自己的配置对吧? - MegaByte
1
不...我认为不是这样。 "from with the dll" 没有任何影响;默认情况下,它正在查看为 AppDomain 定义的配置文件:my.exe.config。 - Marc Gravell
1
特别是AppDomain.CurrentDomain.SetupInformation.ConfigurationFile设置。 - Marc Gravell
注意:我尝试了OpenExeConfiguration,但我不确定它是否有效。也许只需将配置与app.config合并? - Marc Gravell
可以做到...但不像EXE文件的app.config文件那样具有相同的支持和安全性。请查看我的回答。 - Chris Ammerman

13

ConfigurationManager.AppSettings返回应用程序定义的设置,而不是特定DLL的设置,可以访问它们,但将返回应用程序设置。

如果您从另一个应用程序使用dll,则ConnectionString应该在应用程序的app.settings中。


7
我知道现在说这个可能有点晚了,但是我想分享一下我用于DLL的解决方案。
我更倾向于K.I.S.S.的思维方式,所以当我有一个.NET DLL想要存储外部数据点来控制它如何工作或者去哪里等等,我就简单地创建一个“config”类,它只有公共属性来存储所有需要的数据点,并且我希望能够在DLL外部进行控制,以防止重新编译来进行更改。然后我使用.Net的XML序列化将类的对象表示保存和加载到文件中。
然后可以采用很多不同的方法来处理读取和访问,比如单例、静态实用程序类、扩展方法等等。这取决于你的DLL的结构和什么方法最适合你的DLL。

我也使用这种方法,并且对它目前的运作方式感到满意。 - Dave

5
完整的解决方案通常不会在一个地方找到...

1)创建一个应用程序配置文件并将其命名为“yourDllName.dll.config”
2)在VS Solution Explorer中右键单击上面创建的配置文件,单击属性
--- 设置“Build Action”= Content
--- 设置“Copy To Output Directory”= Always
3)向配置文件(yourDllName.dll.config)添加一个appSettings部分,其中包含您的yourKeyName和yourKeyValue
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="yourKeyName" value="yourKeyValue"/>
  </appSettings>
</configuration>

4) 将 System.Configuration 添加到您的 dll/class/project 引用中
5) 在您打算访问配置设置的代码中添加 using 语句

using System.Configuration;
using System.Reflection;

6) 访问值

string keyValue = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["yourKeyName"].Value;

7) 喜大普奔,它能正常工作了

我认为,这只应该在开发新的dll/库时使用。

#if (DEBUG && !FINALTESTING)
   string keyValue = ConfigurationManager.OpenExeConfiguration...(see 6 above)
#else
   string keyValue = ConfigurationManager.AppSettings["yourKeyName"];
#endif

配置文件最终成为一个很好的参考,当您将dll的appSettings添加到实际应用程序时。

4

你是正确的,你可以读取dll文件的配置文件。我曾经为此苦苦挣扎了一天,直到发现我的配置文件有问题。看看下面的代码,它能够运行。

        ExeConfigurationFileMap map = new ExeConfigurationFileMap();
        map.ExeConfigFilename = Assembly.GetExecutingAssembly().Location + ".config";
        Configuration libConfig = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        AppSettingsSection section = (libConfig.GetSection("appSettings") as AppSettingsSection);
        Console.WriteLine(section.Settings["dnd_shortcodes"].Value);

我的Plugin1.dll.config文件如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <appSettings>
  <add key="cmd_location" value="http://..."/>
  <add key="dnd_shortcodes" value="142,145,146,157,165,167,168,171,173,176,178,404,40"/>
 </appSettings>
</configuration>

我发现我的配置文件缺少了<appSettings>标签,所以请仔细查找,你的问题可能不同于我,但也不会差太远。

3

如果你正在使用查找大量配置的库,比如WCF,你可以考虑做以下操作:

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config");

或者在PowerShell中:

[AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config")

在IT技术中,我认为这种技巧是一种“代码异味”,只适合在临时脚本中使用。如果你发现自己想在生产代码中使用它,也许是时候进行架构审查了。

不推荐以下做法:
作为一种技术上的好奇心,这里有一个变体。你可以在DLL中的一个类中创建一个静态构造函数,并从那里调用它。除非万不得已,否则我不建议这样做。


3

由于程序集驻留在临时缓存中,因此您应将路径组合起来获取dll的配置:

var appConfig = ConfigurationManager.OpenExeConfiguration(
    Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));

1
你可以使用 "Assembly.GetExecutingAssembly().Location" 代替 "Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name)"。 - Cadburry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接