C#DLL配置文件


191

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

根据MusicGenesis在“ 将配置信息放入DLL ”中所述,这应该不是问题。所以很明显我做错了...

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

return ConfigurationManager.AppSettings["ConnectionString"];

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

有任何想法吗?


根据引用的帖子:如果dll的名称为MyDll.dll,则配置文件应为MyDLL.dll.config。因此,如果您从dll中读取配置设置,则应参考自己的配置,对吗?
MegaByte

11
不管是什么代码询问-它正在寻找为AppDomain指定的文件:AppDomain.CurrentDomain.SetupInformation.ConfigurationFile设置
Marc Gravell

注意:“将配置信息放入DLL”问题是关于将应用程序的配置代码分离到一个库中,以使其与主应用程序代码分离。这与单独的配置文件和专用于DLL的配置文件有很大不同。
克里斯·阿默曼

看到这个帖子[此处输入链接的描述] [1],是对我的解决方案[1]:stackoverflow.com/questions/2389290/...
dhailis

看到这个帖子[如何加载一个独立的应用程序设置与当前设置动态和合并文件?] [1]可能是helpfu [1]:stackoverflow.com/questions/2389290/...
dhailis

Answers:


277

有充分的理由为.DLL创建.NET配置文件并非易事。.NET配置机制具有很多内置功能,以方便对应用程序进行轻松升级/更新,并保护已安装的应用程序免于踩踏彼此的配置文件。

DLL的使用方式与应用程序的使用方式之间存在很大差异。您不太可能在同一台计算机上为同一用户安装多个应用程序副本。但是您很可能拥有100个不同的应用程序或库,它们都利用了某些.NET DLL。

虽然有很少需要单独跟踪设置一个用户配置文件中的应用程序的不同副本,这是非常不可能的,你想全部DLL来共享配置彼此的不同用法。因此,当您使用“常规”方法检索配置对象时,返回的对象将与您在其中执行的App Domain的配置绑定,而不是与特定程序集绑定。

App Domain绑定到根程序集,该根程序集加载了您的代码实际所在的程序集。在大多数情况下,这将是您的主.EXE的程序集,即加载了.DLL的程序集。可以在一个应用程序中启动其他应用程序域,但是您必须明确提供有关该应用程序域的根程序集的信息。

由于所有这些,创建特定于库的配置文件的过程并不那么方便。这与创建不绑定到任何特定程序集的任意可移植配置文件时所使用的过程相同,但是要使用.NET的XML模式,配置节和配置元素机制等,因此需要创建一个ExeConfigurationFileMap对象。 ,加载数据以标识配置文件的存储位置,然后调用ConfigurationManagerOpenMappedExeConfiguration将其打开到新Configuration实例中。这将使您脱离自动路径生成机制提供的版本保护。

从统计学上讲,您可能是在内部设置中使用此库的,并且不太可能在任何一台机器/用户中都拥有多个使用该库的应用程序。 但是,如果没有,您应该记住一些事情。如果您为DLL使用单个全局配置文件,则无论引用该应用程序是什么,都需要担心访问冲突。如果引用您的库的两个应用程序恰好同时运行,并且每个应用程序都Configuration打开了自己的对象,那么当其中一个保存更改时,下次您尝试在另一个应用程序中检索或保存数据时,它将导致异常。

解决此问题的最安全,最简单的方法是要求正在加载DLL的程序集还提供有关其自身的某些信息,或者通过检查引用程序集的App Domain进行检测。使用此方法创建某种文件夹结构,以为引用DLL的每个应用程序保留单独的用户配置文件。

如果确定无论在何处引用DLL,都希望对其进行全局设置,则需要确定其位置,而不是.NET自动确定合适的位置。您还需要积极主动地管理对文件的访问。您将需要尽可能多地缓存,Configuration仅在加载或保存实例之前就将其保持在附近,并在此之前立即打开并在之后立即进行处置。最后,您需要一种锁定机制来保护文件,而该文件正在由使用该库的一个应用程序进行编辑时。


我认为同步机制应该是“命名事件”等,因为它是跨进程的
Jacob

8
:/嗯。我们的是一个怪物企业应用程序,带有由不同时区的人编写的主要.exe以及由各种DLL表示并通过自定义插件框架动态绑定的模块。所有这些“您需要确保多个应用程序可以同时使用您的DLL”,这是完全错误的。
JohnL4 2013年

此外,在我的职业生涯的大部分时间里,我已经看到这些可爱的通用共享对象机制被完全忽略了,团队创建的DLL(或JAR)只能在一个上下文中使用(并且必须存在,否则应用程序会失败) )。它们也可能是静态绑定的,但这已经过去了。
JohnL4 2013年

1
“从统计学上讲,您可能是在内部设置中使用此库,并且在任何一台机器/用户中都不可能有多个应用程序使用它。” 理论与实践之间的差异有时使我变得脾气暴躁。
JohnL4

1
@ Panzercrisis,Visual Studio的Settings.settings功能自动为所有用户设置创建特定于版本的路径。参见:stackoverflow.com/questions/35778528/…–
Deantwo,

101

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

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

在VS 2008的托管C ++中,System :: Configuration :: Configuration ^ appConfig = ConfigurationManager :: OpenExeConfiguration(Assembly :: GetExecutingAssembly()-> Location); 字符串^ name = appConfig-> AppSettings-> Settings [“ name”]-> Value;
汉斯(Hans)

谢谢,这真的解决了我的问题。我已经处理了大约两天了,直到现在才让它起作用。在调试测试时,ConfigurationManager正在从machine.config中读取-我认为-,因为拉出的连接字符串与SQLExpress-我未列出的连接字符串有关。
yopez83

19

我遇到了同样的问题,并在网上搜索了几个小时,但找不到任何解决方案,因此我自己制定了解决方案。我想知道为什么.net配置系统如此僵化。

背景:我想让我的DAL.dll具有自己的数据库和DAL设置配置文件。我还需要企业库的app.config及其自身的配置。所以我需要app.config和dll.config。

我不想做的是将每个属性/设置从应用程序传递到我的DAL层!

弯曲“ AppDomain.CurrentDomain.SetupInformation.ConfigurationFile”是不可能的,因为正常的app.config行为需要它。

我的要求/观点是:

  • 没有从ClassLibrary1.dll.config到WindowsFormsApplication1.exe.config的任何手动副本,因为这对于其他开发人员来说是不可复制的。
  • 保留强类型“ Properties.Settings.Default.NameOfValue”(设置行为)的用法,因为我认为这是一个主要功能,并且我不想失去它
  • 我发现缺少ApplicationSettingsBase来注入您自己的/自定义配置文件或管理(所有必需的字段在这些类中都是私有的)
  • 无法使用“ configSource”文件重定向,因为我们将不得不复制/重写ClassLibrary1.dll.config并为多个部分提供多个XML文件(我也不喜欢这样)
  • 我不想像MSDN所建议的那样为这个简单的任务编写自己的SettingsProvider,因为我认为那简直太过分了
  • 我只需要配置文件中的applicationSettings和connectionStrings部分

我想出了修改Settings.cs文件的方法,并实现了一种方法,该方法可打开ClassLibrary1.dll.config并读取私有字段中的节信息。之后,我覆盖了“ this [string propertyName]”,因此生成的Settings.Desginer.cs调用了我的新Property而不是基类。在那里从列表中读出设置。

最后有以下代码:

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
不,我不这么认为。“来自dll”不算什么;默认情况下,它正在查看为AppDomain定义的配置文件:my.exe.config
Marc Gravell

1
特别是AppDomain.CurrentDomain.SetupInformation.ConfigurationFile设置。
马克·格雷韦尔

注意:我尝试了OpenExeConfiguration,但也不确定是否可行。也许只是将配置与app.config合并?
马克·格雷韦尔

可以做......但与同样的支持和安全作为一个EXE app.config文件。看我的答案。
克里斯·阿默曼

13

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

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


6

我知道聚会晚了,但是我想我会分享我用于DLL的解决方案。

我更喜欢KISS的流派,所以当我有一个.NET DLL想要存储控制其工作方式或去向的外部数据点时,等等。我只是创建一个仅具有公共属性的“ config”类它存储了所需的所有数据点,并且我希望能够在DLL外部进行控制,以防止重新编译它以进行更改。然后,我使用.Net的XML序列化将类的对象表示形式保存并加载到文件中。

从Singleton,静态实用程序类到扩展方法等,有很多方法可以读取和访问它。这取决于DLL的结构以及最适合DLL的方法。


我也使用这种方法,并且对到目前为止的方法感到满意。
戴夫

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

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

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

代替“ Path.Combine(Environment.CurrentDirectory,Assembly.GetExecutingAssembly()。ManifestModule.Name)”,您可以使用“ Assembly.GetExecutingAssembly()。Location”
Cadburry

3

如果您正在使用在后台查找大量配置的库(例如WCF),则可以考虑这样做:

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

或在PowerShell中:

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

IMO这项技术是一种代码气味,实际​​上仅适用于临时脚本。如果您发现自己想在生产代码中执行此操作,那么也许是时候进行体系结构审查了。

不建议以下内容:
由于技术上的好奇,这里是主题的变体。您可以在DLL所包含的类之一中创建一个静态构造函数,然后从那里进行此调用。除非万不得已,否则我不建议您这样做。


3

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

1)创建一个应用程序配置文件并将其命名为“ yourDllName.dll.config”
2)右键单击在VS Solution Explorer中上面创建的配置文件,单击属性
-设置“构建操作” =内容
---设置“复制到输出目录” =始终
3)使用yourKeyName和yourKeyValue将appSettings部分添加到配置文件(yourDllName.dll.config)中

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="yourKeyName" value="yourKeyValue"/>
  </appSettings>
</configuration>

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

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添加到实际应用程序时,该配置文件最终是一个很好的参考。


3

当配置文件的行为从开发环境更改为部署时,似乎确实令人困惑。显然,DLL可以具有其自己的配置文件,但是一旦您将DLL(及其配置文件)复制并粘贴到其他位置,整个事情就停止了工作。唯一的解决方案是将app.config文件手动合并到一个文件中,该文件将仅由exec使用。例如,myapp.exe将具有一个myapp.exe.config文件,其中包含myapp.exe使用的所有dll的所有设置。我正在使用VS 2008。


2

我发现似乎可以很好地解决此问题。我正在使用VS 2008 C#。我的解决方案涉及在多个配置文件之间使用不同的名称空间。我已将解决方案发布在我的博客上:http : //tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html

例如:

此名称空间读取/写入dll设置:

var x = company.dlllibrary.Properties.Settings.Default.SettingName;
company.dlllibrary.Properties.Settings.Default.SettingName = value;

此名称空间读取/写入exe设置:

company.exeservice.Properties.Settings.Default.SettingName = value;
var x = company.exeservice.Properties.Settings.Default.SettingName;

文章中提到了一些警告。高温超导




0

对于dll,它不应该依赖于配置,因为配置是应用程序而非dll所拥有。

这里解释


0

您可以使用以下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace GClass1
{
[Guid("D6F88E95-8A27-4ae6-B6DE-0542A0FC7039")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface _GesGasConnect
{
    [DispId(1)]
    int SetClass1Ver(string version);


}

[Guid("13FE32AD-4BF8-495f-AB4D-6C61BD463EA4")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("InterfacesSMS.Setting")]
public class Class1 : _Class1
{
    public Class1() { }


    public int SetClass1(string version)
    {
        return (DateTime.Today.Day);
    }
}
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.