我试图将 app.config 文件添加到我的 DLL,但所有尝试都失败了。
根据“Putting configuration information in a DLL”中的 MusicGenesis,这应该不是问题。所以很明显我做错了什么......
以下代码应从我的 DLL 中返回我的 ConnectionString:
return ConfigurationManager.AppSettings["ConnectionString"];
但是,当我将 app.config 文件复制到我的控制台应用程序时,它工作正常。
有任何想法吗?
为 .DLL 创建 .NET 配置文件并非易事,而且有充分的理由。 .NET 配置机制内置了许多功能,以便于轻松升级/更新应用程序,并防止已安装的应用程序相互践踏其他配置文件。
DLL 的使用方式和应用程序的使用方式之间存在很大差异。您不太可能为同一用户在同一台计算机上安装应用程序的多个副本。但是您很可能拥有 100 个不同的应用程序或库,它们都使用了一些 .NET DLL。
尽管很少需要在一个用户配置文件中单独跟踪应用程序的不同副本的设置,但您不太可能希望 DLL 的所有不同用法相互共享配置。出于这个原因,当您使用“正常”方法检索配置对象时,您返回的对象与您正在执行的应用程序域的配置相关联,而不是特定的程序集。
应用程序域绑定到加载代码实际所在程序集的根程序集。在大多数情况下,这将是主 .EXE 的程序集,它是加载 .DLL 的内容。可以在应用程序中启动其他应用程序域,但您必须明确提供有关该应用程序域的根程序集的信息。
因此,创建特定于库的配置文件的过程并不那么方便。这与创建任意可移植配置文件的过程相同,该文件不绑定到任何特定程序集,但您希望使用 .NET 的 XML 模式、配置部分和配置元素机制等。这需要创建一个 { 1} 对象,加载数据以确定配置文件的存储位置,然后调用 ConfigurationManager
.OpenMappedExeConfiguration
将其打开到新的 Configuration
实例中。这将切断自动路径生成机制提供的版本保护。
从统计上讲,您可能在内部环境中使用此库,并且您不太可能在任何一台机器/用户中拥有多个应用程序使用它。 但是如果没有,您应该记住一些事情。如果您为 DLL 使用单个全局配置文件,无论应用程序引用它,您都需要担心访问冲突。如果引用您的库的两个应用程序碰巧同时运行,每个应用程序都打开了自己的 Configuration
对象,那么当一个应用程序保存更改时,它会在您下次尝试在另一个应用程序中检索或保存数据时引发异常。
解决这个问题的最安全和最简单的方法是要求加载 DLL 的程序集也提供一些关于它自己的信息,或者通过检查引用程序集的应用程序域来检测它。使用它来创建某种文件夹结构,以便为引用您的 DLL 的每个应用程序保留单独的用户配置文件。
如果您确定想要对 DLL 进行全局设置,无论它在何处被引用,您都需要确定它的位置,而不是 .NET 自动找出合适的位置。您还需要积极管理对文件的访问。您需要尽可能多地缓存,仅在加载或保存时保留 Configuration
实例,在之前立即打开并在之后立即处理。最后,您需要一种锁定机制来保护文件,同时它正在被使用该库的应用程序之一编辑。
如果您想从 DLL 的配置文件中读取设置,而不是从根应用程序 web.config 或 app.config 中读取设置,请使用以下代码读取 dll 中的配置。
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”(设置行为)的用法,因为我认为这是一个主要功能,我不想失去它
我发现缺少 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 输出目录复制到应用程序的输出目录。也许有人会发现它很有用。
使用 ConfigurationManager 时,我很确定它正在加载进程/AppDomain
配置文件 (app.config / web.config)。如果要加载特定的配置文件,则必须按名称专门询问该文件...
你可以试试:
var config = ConfigurationManager.OpenExeConfiguration("foo.dll");
config.ConnectionStrings. [etc]
ConfigurationManager.AppSettings 返回为应用程序定义的设置,而不是为特定的 DLL,您可以访问它们,但将返回的是应用程序设置。
如果您使用来自另一个应用程序的 dll,则 ConnectionString 应位于应用程序的 app.settings 中。
我知道这晚了,但是我想我会分享我用于 DLL 的解决方案。
我更像是 KISS 学派,所以当我有一个 .NET DLL 想要存储控制其工作方式或去向等的外部数据点时。我只需创建一个只有公共属性的“配置”类存储它需要的所有数据点,并且我希望能够在 DLL 外部进行控制,以防止重新编译它以进行更改。然后我使用 .Net 的 XML 序列化将类的对象表示保存并加载到文件中。
然后有很多方法可以处理读取和访问它,从单例、静态实用程序类到扩展方法等。这取决于您的 DLL 的结构以及最适合您的 DLL 的方法。
你是对的,你可以读取一个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>
标记,所以环顾四周,您的问题可能有所不同,但与我的问题相差不远。
完整的解决方案通常不会在一个地方找到...... 1)创建一个应用程序配置文件并将其命名为“yourDllName.dll.config” 2)在VS解决方案资源管理器中右键单击上面创建的配置文件,单击属性---设置“构建操作”= 内容 --- 设置“复制到输出目录”= 始终 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/类/项目引用 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 添加到您的实际应用程序时。
由于程序集驻留在临时缓存中,您应该结合路径来获取 dll 的配置:
var appConfig = ConfigurationManager.OpenExeConfiguration(
Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));
如果您使用的是在后台查找大量配置的库,例如 WCF,您可以考虑这样做:
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config");
或在 PowerShell 中:
[AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config")
IMO 这种技术是一种代码味道,实际上只适用于临时脚本。如果您发现自己想在生产代码中执行此操作,也许是时候进行架构审查了。
不推荐以下内容:作为技术好奇心,这里是主题的变体。您可以在 DLL 中的一个类中创建一个静态构造函数,然后从那里进行此调用。我不建议这样做,除非作为最后的手段。
似乎这个配置文件真的很混乱,因为它们的行为从开发环境到部署的变化。显然,一个 DLL 可以有自己的配置文件,但是一旦你将 dll(连同它们的配置文件)复制并粘贴到其他地方,整个事情就停止了工作。唯一的解决方案是手动将 app.config 文件合并到一个文件中,该文件仅由 exec 使用。例如,myapp.exe 将有一个 myapp.exe.config 文件,其中包含 myapp.exe 使用的所有 dll 的所有设置。我正在使用 VS 2008。
我发现似乎是解决此问题的好方法。我正在使用 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;
文章中提到了一些注意事项。高温高压
模拟一个“真实的”应用程序配置文件令人困惑。我建议你自己动手,因为使用 LINQ 解析 XML 文件非常容易。
例如,创建一个如下所示的 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";
在这篇文章中讨论了一个类似的问题并解决了我的问题 How to load a separate Application Settings file dynamically and merge with current settings? 可能会有所帮助
您可以使用以下代码:
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);
}
}
}