【问题标题】:C# DLL config fileC# DLL 配置文件
【发布时间】:2010-10-10 07:22:08
【问题描述】:

我正在尝试将 app.config 文件添加到我的 DLL,但所有尝试都失败了。

根据“Putting configuration information in a DLL”中的 MusicGenesis,这应该不是问题。所以很明显我做错了什么......

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

return ConfigurationManager.AppSettings["ConnectionString"];

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

有什么想法吗?

【问题讨论】:

  • 根据引用的帖子:如果dll的名称是MyDll.dll,那么配置文件应该是MyDLL.dll.config。所以如果你从 dll 中读取配置设置,它应该引用它自己的配置吧?
  • 不管代码要求什么 - 它正在寻找为 AppDomain 指定的文件:AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 设置
  • 注意:“将配置信息放入 DLL”问题是关于将应用的配置代码分离到一个库中,以使其与主应用代码分开。这与单独的、专用于 DLL 的配置文件非常不同。
  • 看到这篇文章 [在此处输入链接描述][1],这是我的解决方案 [1]:stackoverflow.com/questions/2389290/…
  • 查看这篇文章[如何动态加载单独的应用程序设置文件并与当前设置合并?][1] 可能会有帮助 [1]:stackoverflow.com/questions/2389290/…

标签: c# app-config


【解决方案1】:

为 .DLL 创建 .NET 配置文件并非易事,这是有充分理由的。 .NET 配置机制内置了许多功能,便于轻松升级/更新应用程序,并防止已安装的应用程序相互践踏其他配置文件。

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

虽然很少需要在一个用户配置文件中单独跟踪应用的不同副本的设置,但您非常不太可能希望 DLL 的所有不同用法都与其共享配置彼此。因此,当您使用“正常”方法检索配置对象时,您返回的对象与您正在执行的应用程序域的配置相关联,而不是特定的程序集。

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

由于所有这些,创建特定于库的配置文件的过程并不那么方便。这与创建任意可移植配置文件的过程相同,该文件不绑定到任何特定程序集,但您希望使用 .NET 的 XML 模式、配置部分和配置元素机制等。这需要创建一个 @ 987654322@ 对象,加载数据以确定配置文件的存储位置,然后调用ConfigurationManager.OpenMappedExeConfiguration 将其打开到新的Configuration 实例中。这将切断您自动路径生成机制提供的版本保护。

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

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

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

【讨论】:

  • 我认为同步机制应该是“命名事件”等,因为它是跨进程的
  • :/ 嗯。我们是一个怪物企业应用程序,其主要 .exe 由不同时区的人编写,模块由各种 DLL 表示,并通过自定义插件框架动态绑定。所有这些“您需要确保多个应用程序可以同时使用您的 DLL”的浮夸是完全错误的。
  • 此外,在我职业生涯的大部分时间里,我看到这些可爱的通用共享对象机制被完全忽略,团队创建只能在一种上下文中使用的 DLL(或 JAR)(以及必须存在,否则应用程序将失败)。它们也可能是静态绑定的,但已经过去了。
  • "从统计上讲,您可能在内部环境中使用此库,并且您不太可能在任何一台机器/用户中拥有多个应用程序使用它。"理论和实践之间的差异有时让我很暴躁。
  • @Panzercrisis,Visual Studio 的 Settings.settings 功能会自动为所有用户设置创建特定于版本的路径。见:stackoverflow.com/questions/35778528/…
【解决方案2】:

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

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

【讨论】:

  • 在 VS 2008 System::Configuration::Configuration 的托管 C++ 中^ appConfig = ConfigurationManager::OpenExeConfiguration(Assembly::GetExecutingAssembly()->Location); String^ name = appConfig->AppSettings->Settings["name"]->Value;
  • 谢谢,这真的解决了我的问题。我已经处理这个问题大约两天了,直到现在才开始工作。在调试测试时,ConfigurationManager 正在从 machine.config 中读取-我认为-,因为提取的连接字符串是关于 SQLExpress -连接字符串的,我没有列出-。
【解决方案3】:

我遇到了同样的问题,在网上搜索了几个小时,但找不到任何解决方案,所以我自己做了。我想知道为什么.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 调用我的新属性而不是基类。从列表中读出设置。

最后有如下代码:

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 输出目录复制到应用程序的输出目录。 也许有人会发现它很有用。

【讨论】:

    【解决方案4】:

    当使用 ConfigurationManager 时,我很确定它正在加载进程/AppDomain 配置文件(app.config / web.config)。如果要加载特定的配置文件,则必须按名称专门询问该文件...

    你可以试试:

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

    【讨论】:

    • 根据引用的帖子:如果dll的名称是MyDll.dll,那么配置文件应该是MyDLL.dll.config。所以如果你从 dll 中读取配置设置,它应该引用它自己的配置吧?
    • 不...我不这么认为。 “来自 dll” 没有任何可能性;默认情况下,它正在查看为 AppDomain 定义的配置文件:my.exe.config
    • 特别是 AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 设置。
    • 注意:我尝试了 OpenExeConfiguration,但我也不确定它是否有效。也许只是将配置与 app.config 合并?
    • 它可以完成...但没有与 EXE 的 app.config 文件相同的支持和安全性。看我的回答。
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

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

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

      【讨论】:

      • 我也使用这种方法,并且对目前的结果感到满意。
      【解决方案7】:

      你是对的,你可以读取一个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>
      

      我发现我的配置文件缺少&lt;appSettings&gt; 标签,所以环顾四周,您的问题可能与我的不同。

      【讨论】:

        【解决方案8】:

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

        1) 创建一个应用配置文件并将其命名为“yourDllName.dll.config”
        2)在VS解决方案资源管理器中右键单击上面创建的配置文件,单击属性
        ---设置“构建操作”=内容
        ---设置“复制到输出目录”=始终
        3) 将 appSettings 部分添加到配置文件 (yourDllName.dll.config) 中,其中包含您的 yourKeyName 和 yourKeyValue

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

        4) 将 System.Configuration 添加到您的 dll/类/项目引用
        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 添加到您的实际应用程序时。

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

          • 可以使用“Assembly.GetExecutingAssembly().Location”代替“Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name)”
          【解决方案10】:

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

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

          或者在 PowerShell 中:

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

          IMO 这种技术是一种代码味道,实际上只适用于临时脚本。如果您发现自己想在生产代码中执行此操作,也许是时候进行架构审查了。

          不建议使用以下内容:
          作为技术好奇心,这里有一个主题的变体。您可以在 DLL 中的一个类中创建一个静态构造函数,然后从那里进行此调用。我不建议这样做,除非作为最后的手段。

          【讨论】:

            【解决方案11】:

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

            【讨论】:

              【解决方案12】:

              我发现似乎是解决此问题的好方法。我正在使用 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;
              

              文章中提到了一些注意事项。高温

              【讨论】:

                【解决方案13】:

                正如 Marc 所说,这是不可能的(尽管 Visual Studio 允许您在类库项目中添加应用程序配置文件)。

                您可能想查看AssemblySettings 类,它似乎使汇编配置文件成为可能。

                【讨论】:

                  【解决方案14】:

                  模拟一个“真实的”应用程序配置文件令人困惑。我建议你自己动手,因为使用例如解析 XML 文件非常容易。 LINQ。

                  例如,创建一个如下所示的 XML 文件 MyDll.config 并将其与 DLL 一起复制。为了使其保持最新状态,请将其在 Visual Studio 中的属性设置为“复制到输出目录”

                  <?xml version="1.0" encoding="utf-8" ?>
                   <configuration>
                    <setting key="KeyboardEmulation" value="Off"></setting>
                   </configuration>
                  

                  在你的代码中这样读:

                      XDocument config = XDocument.Load("MyDll.config");
                      var settings = config.Descendants("setting").Select(s => new { Key = s.Attribute("key").Value, Value = s.Attribute("value").Value });
                      bool keyboardEmulation = settings.First(s => s.Key == "KeyboardEmulation").Value == "On";
                  

                  【讨论】:

                    【解决方案15】:

                    在这篇文章中讨论了一个类似的问题并解决了我的问题How to load a separate Application Settings file dynamically and merge with current settings? 可能会有所帮助

                    【讨论】:

                    • 虽然这在理论上可以回答这个问题,it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。
                    【解决方案16】:

                    对于 dll,它不应依赖于配置,因为配置属于应用程序而不是 dll。

                    这在here进行了解释

                    【讨论】:

                      【解决方案17】:

                      您可以使用此代码:

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

                      【讨论】:

                        猜你喜欢
                        • 2014-10-26
                        • 2023-03-25
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-03-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-10-30
                        • 2010-09-26
                        相关资源
                        最近更新 更多