C# DLL config file
我试图将app.config文件添加到我的dll,但所有尝试都失败了。
根据MusicGenesis在"将配置信息放入DLL"中的说法,这不应该是一个问题。所以很明显我做错了什么…
以下代码应从我的dll返回connectionString:
1 | return ConfigurationManager.AppSettings["ConnectionString"]; |
但是,当我将app.config文件复制到控制台应用程序时,它可以正常工作。
有什么想法吗?
为.dll创建一个.NET配置文件并不是一件容易的事情,这是有充分理由的。.NET配置机制内置了许多功能,以方便升级/更新应用程序,并保护已安装的应用程序不被他人践踏配置文件。
DLL的使用方式和应用程序的使用方式有很大的区别。您不太可能在同一台计算机上为同一用户安装多个应用程序副本。但是你很可能有100个不同的应用程序或库都在使用一些.NET DLL。
虽然很少需要在一个用户配置文件中单独跟踪应用程序的不同副本的设置,但您不太可能希望DLL的所有不同用法彼此共享配置。因此,当使用"normal"方法检索配置对象时,返回的对象与正在执行的应用程序域的配置绑定,而不是与特定程序集绑定。
应用程序域绑定到根程序集,根程序集加载了代码实际所在的程序集。在大多数情况下,这将是主.exe的程序集,加载了.dll。可以在应用程序中旋转其他应用程序域,但必须明确提供有关该应用程序域的根程序集是什么的信息。
因此,创建特定于库的配置文件的过程并不那么方便。这与创建不绑定到任何特定程序集但要使用.NET的XML架构、配置节和配置元素机制等的任意可移植配置文件的过程相同。这需要创建一个
从统计上讲,您可能在内部设置中使用这个库,并且您不太可能在任何一台机器/用户中使用多个应用程序。但如果没有,你应该记住一些事情。如果您为您的DLL使用一个全局配置文件,不管引用它的应用程序是什么,您需要担心访问冲突。如果同时运行引用库的两个应用程序,每个应用程序都打开自己的
解决此问题的最安全和最简单的方法是要求加载DLL的程序集也提供有关其自身的一些信息,或者通过检查引用程序集的应用程序域来检测它。使用它可以创建某种文件夹结构,以便为引用DLL的每个应用程序保留单独的用户配置文件。
如果您确定无论在何处引用DLL,都要对其进行全局设置,则需要确定其位置,而不是.NET自动确定合适的位置。您还需要积极地管理对文件的访问。您将需要尽可能多地缓存,仅在加载或保存所需的时间内保留
如果要从dll的配置文件而不是根应用程序web.config或app.config读取设置,请使用下面的代码读取dll中的配置。
1 2 | var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location); string dllConfigData = appConfig.AppSettings.Settings["dllConfigData"].Value; |
我也遇到了同样的问题,在网上搜索了几个小时,但是我找不到任何解决方案,所以我自己做了。我想知道为什么.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"(设置行为)的用法,因为我认为这是一个主要功能,我不想丢失它
- 我发现缺乏应用程序设置库来插入您自己的/自定义配置文件或管理(所有必需的字段在这些类中都是私有的)。
- 使用"configsource"文件重定向是不可能的,因为我们必须复制/重写ClassLibrary1.dll.config并为几个部分提供几个XML文件(我也不喜欢这样)。
- 我不想像msdn建议的那样为这个简单的任务编写自己的settingsProvider,因为我认为这太简单了
- 我只需要配置文件中的部分applicationsettings和connectionstrings
我修改了settings.cs文件,实现了一个方法,打开classlibrary1.dll.config并在一个私有字段中读取部分信息。之后,我重写了"this[string propertyname]",因此生成的settings.designer.cs将调用我的新属性而不是基类。在那里,将从列表中读取设置。
最后是以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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输出目录复制到应用程序的输出目录。也许有人会发现它很有用。
当使用configurationmanager时,我很确定它正在加载进程/
你可以试试:
1 2 | var config = ConfigurationManager.OpenExeConfiguration("foo.dll"); config.ConnectionStrings. [etc] |
configurationmanager.appsettings返回为应用程序定义的设置,而不是特定的dll,您可以访问它们,但将返回的是应用程序设置。
如果您使用的是来自另一个应用程序的dll,那么connectionString应该在应用程序的app.settings中。
我知道参加聚会已经晚了,不过我想我会和大家分享我用于DLL的解决方案。
我更像是K.I.S.S.的思想流派,所以当我有一个.NET DLL,它想要存储外部数据点来控制它的工作方式或去向等。我只创建一个"config"类,它只有公共属性来存储它需要的所有数据点,并且我希望能够控制DLL外部来防止重新编译以进行更改。然后,我使用.NET的XML序列化将类的对象表示形式保存并加载到文件中。
有很多方法可以处理读取和访问它,从单例、静态实用程序类到扩展方法等。这取决于DLL的结构以及最适合您的DLL的方法。
正确,可以读取dll的配置文件。我为此挣扎了一天,直到我发现我的配置文件是问题所在。请参阅下面的代码。它能跑。
1 2 3 4 5 | 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); |
我的
1 2 3 4 5 6 7 | <?xml version="1.0" encoding="utf-8" ?> <configuration> </appSettings> </configuration> |
我发现我的配置文件缺少
似乎当配置文件的行为从开发环境变为部署时,这些配置文件确实令人困惑。显然,一个DLL可以有自己的配置文件,但是一旦您复制并粘贴到其他地方的DLL(连同它们的配置文件),整个过程就停止了。唯一的解决方案是手动将app.config文件合并到单个文件中,该文件仅由exec使用。例如,myapp.exe将有一个myapp.exe.config文件,其中包含myapp.exe使用的所有dll的所有设置。我正在使用vs 2008。
由于程序集驻留在临时缓存中,因此应组合路径以获取dll的配置:
1 2 | var appConfig = ConfigurationManager.OpenExeConfiguration( Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name)); |
如果您使用的库在后台查找大量配置,例如wcf,则可以考虑这样做:
1 | AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE","MyWcfClientWrapper.dll.config"); |
或在PowerShell中:
2IMO这种技术是一种代码味道,实际上只适用于临时脚本。如果您发现自己想在生产代码中实现这一点,那么也许是时候进行架构审查了。
不建议使用以下内容:
作为一种技术上的好奇心,这里有一个主题的变化。可以在dll中包含的某个类内创建静态构造函数,并从中进行此调用。我不建议这样做,除非作为最后的手段。
我找到了解决这个问题的好办法。我使用的是vs 2008 c。我的解决方案涉及在多个配置文件之间使用不同的名称空间。我已经在我的博客上发布了解决方案:http://tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html。
例如:
此命名空间读/写DLL设置:
2此命名空间读取/写入exe设置:
1 2 | company.exeservice.Properties.Settings.Default.SettingName = value; var x = company.exeservice.Properties.Settings.Default.SettingName; |
文章中提到了一些注意事项。高温高压
完整的解决方案往往不是在一个地方找到的…1)创建一个app config文件并将其命名为"yourdllname.dll.config"2)右键单击上面在vs解决方案资源管理器中创建的配置文件,单击属性---设置"构建操作"=content---设置"复制到输出目录"=always。3)将appsettings节添加到配置文件(yourdllname.dll.config)中,并使用yourkeyname和yourkeyvalue
1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8" ?> <configuration> </appSettings> </configuration> |
4)将system.configuration添加到您的dll/class/project引用中5)在您想要访问配置设置的代码中添加using语句。
1 2 | using System.Configuration; using System.Reflection; |
6)访问值
27)高兴,它起作用了。
imho,这只能在开发新的dll/库时使用。
1 2 3 4 5 | #if (DEBUG && !FINALTESTING) string keyValue = ConfigurationManager.OpenExeConfiguration...(see 6 above) #else string keyValue = ConfigurationManager.AppSettings["yourKeyName"]; #endif |
配置文件最终是一个很好的引用,当您将dll的appsettings添加到实际应用程序时。
正如马克所说,这是不可能的(尽管Visual Studio允许您在类库项目中添加应用程序配置文件)。
您可能需要签出assemblysettings类,该类似乎使程序集配置文件成为可能。
在本文中,我们讨论了一个类似的问题,并解决了我的问题:如何动态加载单独的应用程序设置文件并与当前设置合并?也许是有用的
对于一个dll,它不应该依赖于配置,因为配置由应用程序而不是由dll拥有。
这里有解释
您可以使用此代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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); } } } |