简体   繁体   English

C# DLL 配置文件

[英]C# DLL config file

Im trying to add an app.config file to my DLL, but all attempts have failed.我试图将 app.config 文件添加到我的 DLL,但所有尝试都失败了。

According to MusicGenesis in ' Putting configuration information in a DLL ' this should not be a problem.根据“ 将配置信息放入 DLL 中中的MusicGenesis,这应该不是问题。 So obviously I'm doing something wrong...所以很明显我做错了什么......

The following code should return my ConnectionString from my DLL:下面的代码应该从我的 DLL 返回我的 ConnectionString:

return ConfigurationManager.AppSettings["ConnectionString"];

However, when I copy the app.config file to my console application, it works fine.但是,当我将 app.config 文件复制到我的控制台应用程序时,它工作正常。

Any ideas?有任何想法吗?

It is not trivial to create a .NET configuration file for a .DLL, and for good reason.为 .DLL 创建 .NET 配置文件并非易事,这是有充分理由的。 The .NET configuration mechanism has a lot of features built into it to facilitate easy upgrading/updating of the app, and to protect installed apps from trampling each others configuration files. .NET 配置机制内置了许多功能,以方便应用程序的轻松升级/更新,并保护已安装的应用程序免于相互践踏配置文件。

There is a big difference between how a DLL is used and how an application is used. DLL 的使用方式和应用程序的使用方式之间存在很大差异。 You are unlikely to have multiple copies of an application installed on the same machine for the same user.您不太可能在同一台计算机上为同一用户安装多个应用程序副本。 But you may very well have 100 different apps or libraries all making use of some .NET DLL.但是您很可能拥有 100 个不同的应用程序或库,它们都在使用某些 .NET DLL。

Whereas there is rarely a need to track settings separately for different copies of an app within one user profile, it's very unlikely that you would want all of the different usages of a DLL to share configuration with each other.虽然有很少需要单独跟踪设置一个用户配置文件中的应用程序的不同副本,这是非常不可能的,你想全部DLL来共享配置彼此的不同用法。 For this reason, when you retrieve a Configuration object using the "normal" method, the object you get back is tied to the configuration of the App Domain you are executing in, rather than the particular assembly.出于这个原因,当您使用“正常”方法检索 Configuration 对象时,您返回的对象与您正在执行的应用程序域的配置相关联,而不是特定的程序集。

The App Domain is bound to the root assembly which loaded the assembly which your code is actually in. In most cases this will be the assembly of your main .EXE, which is what loaded up the .DLL. App Domain 绑定到根程序集,该程序集加载了代码实际所在的程序集。在大多数情况下,这将是主 .EXE 的程序集,也就是加载 .DLL 的程序集。 It is possible to spin up other app domains within an application, but you must explicitly provide information on what the root assembly of that app domain is.可以在应用程序中启动其他应用程序域,但您必须明确提供有关该应用程序域的根程序集是什么的信息。

Because of all this, the procedure for creating a library-specific config file is not so convenient.因此,创建特定于库的配置文件的过程并不那么方便。 It is the same process you would use for creating an arbitrary portable config file not tied to any particular assembly, but for which you want to make use of .NET's XML schema, config section and config element mechanisms, etc. This entails creating an ExeConfigurationFileMap object, loading in the data to identify where the config file will be stored, and then calling ConfigurationManager .它与您用于创建不绑定到任何特定程序集的任意可移植配置文件的过程相同,但您希望使用 .NET 的 XML 架构、配置部分和配置元素机制等。这需要创建一个ExeConfigurationFileMap对象,加载数据以识别配置文件的存储位置,然后调用ConfigurationManager OpenMappedExeConfiguration to open it up into a new Configuration instance. OpenMappedExeConfiguration将其打开到一个新的Configuration实例中。 This will cut you off from the version protection offered by the automatic path generation mechanism.将使您脱离自动路径生成机制提供的版本保护。

Statistically speaking, you're probably using this library in an in-house setting, and it's unlikely you'll have multiple apps making use of it within any one machine/user.从统计上讲,您可能正在内部环境中使用此库,并且您不太可能在任何一台机器/用户中拥有多个应用程序使用它。 But if not, there is something you should keep in mind.如果没有,您应该记住一些事情。 If you use a single global config file for your DLL, regardless of the app that is referencing it, you need to worry about access conflicts.如果您为 DLL 使用单个全局配置文件,无论引用它的应用程序如何,您都需要担心访问冲突。 If two apps referencing your library happen to be running at the same time, each with their own Configuration object open, then when one saves changes, it will cause an exception next time you try to retrieve or save data in the other app.如果引用您的库的两个应用程序碰巧同时运行,每个应用程序都打开了自己的Configuration对象,那么当一个应用程序保存更改时,下次您尝试在另一个应用程序中检索或保存数据时将导致异常。

The safest and simplest way of getting around this is to require that the assembly which is loading your DLL also provide some information about itself, or to detect it by examining the App Domain of the referencing assembly.解决这个问题的最安全和最简单的方法是要求加载 DLL 的程序集也提供一些关于自身的信息,或者通过检查引用程序集的应用程序域来检测它。 Use this to create some sort of folder structure for keeping separate user config files for each app referencing your DLL.使用它来创建某种文件夹结构,以便为引用您的 DLL 的每个应用程序保留单独的用户配置文件。

If you are certain you want to have global settings for your DLL no matter where it is referenced, you'll need to determine your location for it rather than .NET figuring out an appropriate one automatically.如果您确定要为 DLL 进行全局设置,无论它在哪里被引用,您都需要确定它的位置,而不是 .NET 自动找出合适的位置。 You'll also need to be aggressive about managing access to the file.您还需要积极管理对文件的访问。 You'll need to cache as much as possible, keeping the Configuration instance around ONLY as long as it takes to load or to save, opening immediately before and disposing immediately after.您需要尽可能多地缓存,仅在加载或保存所需的时间内保留Configuration实例,在此之前立即打开并在之后立即处理。 And finally, you'll need a lock mechanism to protect the file while it's being edited by one of the apps that use the library.最后,您需要一种锁定机制来保护正在由使用该库的应用程序之一编辑的文件。

if you want to read settings from the DLL's config file but not from the the root applications web.config or app.config use below code to read configuration in the dll.如果您想从 DLL 的配置文件中读取设置,而不是从根应用程序 web.config 或 app.config 中读取设置,请使用以下代码读取 dll 中的配置。

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

I had the same problem and searched the web for several hours but I couldn't find any solution so I made my own.我遇到了同样的问题并在网上搜索了几个小时,但找不到任何解决方案,所以我自己做了。 I wondered why the .net configuration system is so inflexible.我想知道为什么 .net 配置系统如此不灵活。

Background: I want to have my DAL.dll to have its own config file for database and DAL settings.背景:我想让我的 DAL.dll 拥有自己的数据库和 DAL 设置配置文件。 I also need the app.config for Enterprise Library and its own configurations.我还需要企业库的 app.config 及其自己的配置。 So I need both the app.config and dll.config.所以我需要 app.config 和 dll.config。

What I did not wanted to do is pass-through every property/setting from the app to my DAL layer!我不想做的是将每个属性/设置从应用程序传递到我的 DAL 层!

to bend the "AppDomain.CurrentDomain.SetupInformation.ConfigurationFile" is not possible because I need it for the normal app.config behavior.弯曲“AppDomain.CurrentDomain.SetupInformation.ConfigurationFile”是不可能的,因为我需要它来实现正常的 app.config 行为。

My requirements/point of views were:我的要求/观点是:

  • NO manual copy of anything from ClassLibrary1.dll.config to WindowsFormsApplication1.exe.config because this is unreproducible for other developers.没有手动复制从 ClassLibrary1.dll.config 到 WindowsFormsApplication1.exe.config 的任何内容,因为其他开发人员无法复制。
  • retain the usage of strong typing "Properties.Settings.Default.NameOfValue" (Settings behavior) because I think this is a major feature and I didn't want to lose it保留强类型“Properties.Settings.Default.NameOfValue”(设置行为)的用法,因为我认为这是一个主要功能,我不想失去它
  • I found out the lack of ApplicationSettingsBase to inject your own/custom config file or management (all necessary fields are private in these classes)我发现缺少 ApplicationSettingsBase 来注入您自己的/自定义配置文件或管理(所有必需的字段在这些类中都是私有的)
  • the usage of "configSource" file redirection is not possible because we would have to copy/rewrite the ClassLibrary1.dll.config and provide several XML files for several sections (I also didn't like this) “configSource”文件重定向的使用是不可能的,因为我们必须复制/重写 ClassLibrary1.dll.config 并为几个部分提供几个 XML 文件(我也不喜欢这样)
  • I didn't like to write my own SettingsProvider for this simple task as MSDN suggests because I thought that simply would be too much我不喜欢像 MSDN 建议的那样为这个简单的任务编写自己的 SettingsProvider,因为我认为这太过分了
  • I only need sections applicationSettings and connectionStrings from the config file我只需要配置文件中的 applicationSettings 和 connectionStrings 部分

I came up with modifying the Settings.cs file and implemented a method that opens the ClassLibrary1.dll.config and reads the section information in a private field.我想出了修改 Settings.cs 文件并实现了打开 ClassLibrary1.dll.config 并读取私有字段中的节信息的方法。 After that, I've overriden "this[string propertyName]" so the generated Settings.Desginer.cs calls into my new Property instead of the base class.之后,我覆盖了“this[string propertyName]”,因此生成的 Settings.Desginer.cs 会调用我的新属性而不是基类。 There the setting is read out of the List.在那里从列表中读出设置。

Finally there is the following code:最后有以下代码:

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

You just will have to copy your ClassLibrary1.dll.config from the ClassLibrary1 output directory to your application's output directory.您只需将 ClassLibrary1.dll.config 从 ClassLibrary1 输出目录复制到应用程序的输出目录。 Perhaps someone will find it useful.也许有人会发现它很有用。

When using ConfigurationManager, I'm pretty sure it is loading the process/ AppDomain configuration file (app.config / web.config).使用 ConfigurationManager 时,我很确定它正在加载 process/ AppDomain配置文件 (app.config/web.config)。 If you want to load a specific config file, you'll have to specifically ask for that file by name...如果要加载特定的配置文件,则必须按名称专门询问该文件...

You could try:你可以试试:

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

ConfigurationManager.AppSettings returns the settings defined for the application, not for the specific DLL, you can access them but it's the application settings that will be returned. ConfigurationManager.AppSettings 返回为应用程序定义的设置,而不是为特定的 DLL,您可以访问它们,但将返回应用程序设置。

If you're using you dll from another application then the ConnectionString shall be in the app.settings of the application.如果您使用来自另一个应用程序的 dll,则 ConnectionString 应位于应用程序的 app.settings 中。

I know this is late to the party, however I thought I would share the solution I use for DLL's.我知道这已经晚了,但是我想我会分享我用于 DLL 的解决方案。

I am more of the KISS school of thinking, so when I have a .NET DLL that wants to store external data points that control how it works or where it goes, etc. I simply create a "config" class that has only public properties that store all the data points it needs and that I would like to be able to have controlled external to the DLL to prevent recompiling it to make the changes.我更喜欢 KISS 的思想流派,所以当我有一个 .NET DLL 想要存储控制它如何工作或它去哪里等的外部数据点时。我只是创建一个只有公共属性的“配置”类存储它需要的所有数据点,并且我希望能够在 DLL 外部进行控制,以防止重新编译它以进行更改。 Then I use .Net's XML Serializing to save and load the object representation of the class to a file.然后我使用 .Net 的 XML Serializing 将类的对象表示保存并加载到文件中。

There are a lot of ways then to handle reading it and accessing it, from a Singleton, a static utility class, to extension methods, etc. This depends on how your DLL is structured and what method will fit your DLL best.有很多方法可以处理读取和访问它,从单例、静态实用程序类到扩展方法等。这取决于您的 DLL 的结构方式以及哪种方法最适合您的 DLL。

you are correct, you can read the config file of a dll.你是对的,你可以读取一个dll的配置文件。 I struggled with this for a day until i found out that the my config file was the issue.我为此苦苦挣扎了一天,直到我发现我的配置文件是问题所在。 See my code below.请参阅下面的我的代码。 it was able to run.它能够运行。

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

my Plugin1.dll.config looked as below;我的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>

I found out that my config file lacked the <appSettings> tag, so look around, your issue could have been different but not so far from mine.我发现我的配置文件缺少<appSettings>标签,所以环顾四周,你的问题可能有所不同,但与我的问题相去甚远。

The full solution is not often found in one place ...完整的解决方案通常不会在一个地方找到......

1) Create an app config file and name it "yourDllName.dll.config" 1)创建一个应用程序配置文件并将其命名为“yourDllName.dll.config”
2) Right click on the config file created above in VS Solution Explorer, click properties 2)在VS解决方案资源管理器中右键单击上面创建的配置文件,单击属性
--- set "Build Action" = Content --- 设置“构建操作”= 内容
--- set "Copy To Output Directory" = Always --- 设置“复制到输出目录”= 始终
3) Add an appSettings section to the configuration file (yourDllName.dll.config) with your yourKeyName and yourKeyValue 3) 使用 yourKeyName 和 yourKeyValue 在配置文件 (yourDllName.dll.config) 中添加一个 appSettings 部分

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

4) Add System.Configuration to your dll/class/project references 4) 将 System.Configuration 添加到您的 dll/class/project 引用
5) Add the using statements to your code where you intend to access the config setting 5) 将 using 语句添加到您打算访问配置设置的代码中

using System.Configuration;
using System.Reflection;

6) To access the value 6) 访问值

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

7) rejoice, it works 7)高兴,它起作用了

IMHO, this should only be used when developing a new dll/library.恕我直言,这应该只在开发新的 dll/库时使用。

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

The config file ends up being a great reference, for when you add the dll's appSettings to your actual application.配置文件最终成为一个很好的参考,因为当您将 dll 的 appSettings 添加到您的实际应用程序时。

Seems like this config files are really confusing to clarify as their behaviour changes from the dev environment to deployment.似乎这个配置文件真的很令人困惑,因为它们的行为从开发环境到部署都发生了变化。 Apparently a DLL can have its own config file, but once you copy and paste the dll (together with their config file) elsewhere, the whole thing stopped working.显然,DLL 可以拥有自己的配置文件,但是一旦您将 dll(连同它们的配置文件)复制并粘贴到其他地方,整个过程就停止了。 The only solution is to manually merge the app.config files into a single file, which will only be used by the exec.唯一的解决方案是手动将 app.config 文件合并为一个文件,该文件仅供 exec 使用。 For eg myapp.exe will have a myapp.exe.config file that contains all settings for all dlls used by myapp.exe.例如,myapp.exe 将有一个 myapp.exe.config 文件,其中包含 myapp.exe 使用的所有 dll 的所有设置。 I'm using VS 2008.我正在使用 VS 2008。

Since the assembly resides in a temporary cache, you should combine the path to get the dll's config:由于程序集驻留在临时缓存中,您应该组合路径以获取 dll 的配置:

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

If you're using libraries that look up a large amount of configation behind-the-scenes, such as WCF, you might consider doing this:如果您正在使用在幕后查找大量配置的库,例如 WCF,您可以考虑这样做:

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

Or in PowerShell:或者在 PowerShell 中:

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

IMO this technique is a code smell and is really only suitable for use in ad hoc scripting. IMO 这种技术是一种代码异味,实际上仅适用于临时脚本。 If you find yourself wanting to do this in production code, maybe it's time for an architectural review.如果您发现自己想在生产代码中执行此操作,也许是时候进行架构审查了。

The following is NOT recommended:不推荐以下内容:
As a technical curiosity, here's a variation on the theme.作为技术上的好奇心,这里有一个主题的变体。 You can create a static constructor inside one of the classes housed in the DLL, and make this call from there.您可以在 DLL 中的一个类中创建一个静态构造函数,并从那里进行此调用。 I wouldn't recommend doing this except as a last resort.我不建议这样做,除非作为最后的手段。

I've found what seems like a good solution to this issue.我找到了解决这个问题的好办法。 I am using VS 2008 C#.我正在使用 VS 2008 C#。 My solution involves the use of distinct namespaces between multiple configuration files.我的解决方案涉及在多个配置文件之间使用不同的命名空间。 I've posted the solution on my blog: http://tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html .我已经在我的博客上发布了解决方案: http : //tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html

For example:例如:

This namespace read/writes dll settings:此命名空间读/写 dll 设置:

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

This namespace read/writes the exe settings:此命名空间读取/写入 exe 设置:

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

There are some caveats mentioned in the article.文章中提到了一些注意事项。 HTH HTH

As Marc says, this is not possible (although Visual Studio allows you to add an application configuration file in a class library project).正如 Marc 所说,这是不可能的(尽管 Visual Studio 允许您在类库项目中添加应用程序配置文件)。

You might want to check out the AssemblySettings class which seems to make assembly config files possible.您可能想查看AssemblySettings类,它似乎使程序集配置文件成为可能。

It confusing to mock a "real" application configuration file.模拟“真实”的应用程序配置文件会令人困惑。 I suggest you roll your own because it is quite easy to parse an XML file using eg LINQ.我建议您自己动手,因为使用例如 LINQ 解析 XML 文件非常容易。

For example create an XML file MyDll.config like below and copy it alongside the DLL.例如,创建一个如下所示的 XML 文件 MyDll.config 并将其与 DLL 一起复制。 To Keep it up to date set its property in Visual Studio to "Copy to Output Directory"为了使其保持最新,将其在 Visual Studio 中的属性设置为“复制到输出目录”

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

In your Code read it like this:在你的代码中,像这样阅读:

    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";

For a dll, it should not depend on configuration as configuration is owned by application and not by dll.对于 dll,它不应该依赖于配置,因为配置由应用程序拥有,而不是由 dll 拥有。

This is explained at here这在这里解释

you can use this code:您可以使用此代码:

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

In this post a similar problem was discussed and solve my problem How to load a separate Application Settings file dynamically and merge with current settings?在这篇文章中讨论了一个类似的问题并解决了我的问题如何动态加载单独的应用程序设置文件并与当前设置合并? might be helpfu可能有帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM