C # DLL конфігураційний файл


191

Я намагаюся додати файл app.config до моєї DLL, але всі спроби не вдалися.

Згідно з MusicGenesis у " Внесення інформації про конфігурацію в DLL " це не повинно бути проблемою. Тож явно я щось не так роблю ...

Наступний код повинен повернути мій ConnectionString з моєї DLL:

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

Відповіді:


277

Створити файл конфігурації .NET для .DLL не є тривіальним і з поважної причини. Механізм конфігурації .NET має безліч функцій, вбудованих у нього, щоб полегшити оновлення / оновлення програми та захистити встановлені програми від витоптування файлів конфігурації.

Існує велика різниця між тим, як використовується DLL, і тим, як використовується програма. Ви навряд чи маєте кілька копій програми, встановлених на одній машині для одного користувача. Але у вас може бути 100 різних додатків або бібліотек, які використовують деякі .NET DLL.

Оскільки рідко виникає необхідність відстежувати налаштування окремо для різних копій програми в межах одного профілю користувача, дуже малоймовірно, що ви хочете, щоб усі різні звички DLL мали спільний доступ до конфігурації. З цієї причини, коли ви отримуєте об’єкт Конфігурація за допомогою "звичайного" методу, об'єкт, який ви отримуєте назад, прив'язується до конфігурації Домену додатків, який ви виконуєте, а не до конкретної збірки.

Домен додатка прив’язаний до кореневої збірки, яка завантажила збірку, на якій насправді знаходиться ваш код. У більшості випадків це збірка вашого основного .EXE, яка завантажувала .DLL. Можна додавати інші домени додатків у програмі, але ви повинні чітко надати інформацію про те, що таке коренева збірка цього домену додатка.

Через все це процедура створення конфігураційного файлу для бібліотеки не така зручна. Це той самий процес, який ви використовували б для створення довільного портативного конфігураційного файлу, не прив'язаного до якоїсь конкретної збірки, але для якого ви хочете використовувати XML-схему .NET, розділ конфігурації та механізми конфігураційних елементів тощо. Це тягне за собою створення ExeConfigurationFileMapоб'єкта , завантаження в дані, щоб визначити, де буде зберігатися конфігураційний файл, а потім виклик ConfigurationManager. OpenMappedExeConfigurationщоб відкрити його в новий Configurationекземпляр. Це дозволить вам відключити захист версії, яку пропонує механізм автоматичного генерування шляху.

Статистично кажучи, ви, ймовірно, використовуєте цю бібліотеку в внутрішніх умовах, і навряд чи ви матимете кілька додатків, які використовують її у будь-якій машині / користувачі. Але якщо ні, то слід пам’ятати про щось. Якщо ви використовуєте єдиний глобальний конфігураційний файл для DLL, незалежно від програми, що посилається на нього, вам потрібно потурбуватися про конфлікти доступу. Якщо два програми, що посилаються на вашу бібліотеку, одночасно запускаються, у кожного з Configurationвідкритим власним об'єктом, тоді, коли один зберігає зміни, наступний раз, коли ви спробуєте отримати або зберегти дані в іншому додатку, це стане винятком.

Найбезпечніший і найпростіший спосіб подолати це - вимагати, щоб збірка, що завантажує вашу DLL, також надала деяку інформацію про себе, або виявити її, вивчивши Домен додатка референтної збірки. Використовуйте це, щоб створити якусь структуру папок для зберігання окремих файлів конфігурації користувача для кожного додатка, що посилається на вашу DLL.

Якщо ви впевнені, що хочете мати глобальні налаштування для DLL незалежно від місця посилання, вам потрібно визначити своє місцезнаходження для цього, а не .NET автоматично визначити відповідний. Вам також потрібно бути агресивними щодо управління доступом до файлу. Вам потрібно буде кешувати якнайбільше, зберігаючи Configurationекземпляр навколо ТОЛЬКО стільки часу, скільки потрібно для завантаження чи збереження, відкриваючись безпосередньо перед цим і розпоряджаючись відразу після. І нарешті, вам знадобиться механізм блокування для захисту файлу під час його редагування одним із програм, які використовують бібліотеку.


я думаю, що механізм синхронізації повинен бути "названою подією" і т. д., тому що це поперечні процеси
Якоб

8
: / Мех. Наша - це монстр-корпоративне додаток з основним .exe, написане хлопцями в іншому часовому поясі та модулях, представлених різними DLL-файлами та динамічно пов'язаними через власну рамку плагінів. Все це "вам потрібно, щоб кілька додатків могли одночасно використовувати вашу DLL", помпезність - це абсолютно неправильно.
JohnL4

Крім того, у значній частині своєї кар’єри я бачив ці прекрасні загальні механізми спільного використання об'єктів, повністю ігноровані: команди створюють DLL (або JAR), які можна використовувати лише в одному контексті (і повинні бути присутніми, або програма виходить з ладу ). Вони також можуть бути статично пов'язаними, але це проходить.
JohnL4

1
"Статистично кажучи, ви, ймовірно, використовуєте цю бібліотеку в внутрішніх умовах, і навряд чи ви матимете кілька додатків, які використовують її в межах однієї машини / користувача." Різниця між теорією та практикою часом мене дуже бурчить.
JohnL4

1
@Panzercrisis, функція Settings.settings Visual Studio автоматично створює конкретні версії шляхів для всіх налаштувань користувача. Див: 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;

У керованій C ++ для системи VS 2008 :: Конфігурація :: Конфігурація ^ appConfig = ConfigurationManager :: OpenExeConfiguration (Assembly :: GetExecutingAssembly () -> Location); Рядок ^ ім'я = appConfig-> AppSettings-> Налаштування ["ім'я"] -> Значення;
Ганс

Дякую, це справді вирішило мою проблему. Я займався цим питанням близько двох днів, і не змусив його працювати лише зараз. Під час налагодження тесту ConfigurationManager читав з machine.config -Я думаю-, оскільки витягнуті рядки з'єднання були приблизно в SQLExpress-рядку з'єднання, який я не перераховував-
yopez83

19

У мене була та сама проблема і кілька годин шукала в Інтернеті, але я не змогла знайти жодного рішення, тому зробила своє. Мені було цікаво, чому система налаштування .net така негнучка.

Передумови: Я хочу, щоб мій DAL.dll мав власний конфігураційний файл для налаштувань бази даних та DAL. Мені також потрібна app.config для Enterprise Library та її власних конфігурацій. Тому мені потрібні і 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-файлів для декількох розділів (мені це також не сподобалось)
  • Мені не подобалося писати власні SettingsProvider для цього простого завдання, як пропонує MSDN, тому що я вважав, що просто буде занадто багато
  • Мені потрібні лише розділи applicationSettings and connectionStrings з конфігураційного файлу

Я придумав змінити файл Settings.cs і реалізував метод, який відкриває ClassLibrary1.dll.config і читає інформацію про розділи в приватному полі. Після цього я змінив "це [рядок властивостіName]", тому створений 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 у вихідний каталог програми. Можливо, комусь це стане в нагоді.


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?
Марк Гравелл

Це можна зробити ... але не з такою ж підтримкою та безпекою, як файл app.config для EXE. Дивіться мою відповідь.
Кріс Амерман

13

ConfigurationManager.AppSettings повертає налаштування, визначені для програми, а не для конкретної DLL, ви можете отримати доступ до них, але це параметри програми, які будуть повернуті.

Якщо ви використовуєте dll з іншої програми, то ConnectionString має бути в наборі програм.


6

Я знаю, що це спізнилося на вечірку, проте я подумав, що поділюсь рішенням, яке використовую для DLL.

Я більше з школи мислення KISS, тому коли у мене є .NET DLL, який хоче зберігати зовнішні точки даних, які керують тим, як він працює або куди йде, і т. Д. Я просто створю клас "config", який має лише публічні властивості що зберігають усі потрібні йому точки даних, і я хотів би мати можливість керувати зовнішньою мережею DLL, щоб запобігти перекомпіляції її для внесення змін. Тоді я використовую XML-серіалізацію .Net для збереження та завантаження об’єктного представлення класу у файл.

Існує багато способів вирішити читання та отримати доступ до нього, від 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, натисніть властивості
--- встановити "Build Action" = Вміст
--- встановити "Copy To Output Directory" = Завжди
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 / class / project
5) Додайте користувальницькі оператори до свого коду, де ви маєте намір отримати доступ до налаштування конфігурації

using System.Configuration;
using System.Reflection;

6) Для доступу до значення

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

7) радійте, це працює

IMHO, це слід використовувати лише при розробці нового dll / бібліотеки.

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

Конфігураційний файл, в кінцевому підсумку, є чудовим орієнтиром, коли ви додаєте програми dllSettings до фактичної програми.


3

Схоже, що ці конфігураційні файли дійсно незрозуміло пояснювати, як їх поведінка змінюється від середовища розробки до розгортання. Мабуть, DLL може мати власний конфігураційний файл, але як тільки ви скопіюєте та вставте dll (разом із їх конфігураційним файлом) в інше місце, вся справа перестала працювати. Єдине рішення - це вручну об'єднати файли app.config в один файл, який буде використовуватися лише exec. Наприклад, myapp.exe матиме файл myapp.exe.config, який містить усі параметри для всіх dll, що використовуються myapp.exe. Я використовую 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;

У статті зазначено кілька застережень. HTH


1

Як каже Марк, це неможливо (хоча Visual Studio дозволяє додавати файл конфігурації програми в проект бібліотеки класів).

Ви можете перевірити клас AssemblySettings, який, здається, робить можливими файли конфігурації.


0

У цій публікації обговорювались подібні проблеми та вирішували мою проблему Як динамічно завантажувати окремий файл налаштувань програми та об’єднуватись із поточними налаштуваннями? може бути корисним


Хоча це теоретично може відповісти на питання, бажано було б сюди включити істотні частини відповіді та надати посилання для довідки.
Аді

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);
    }
}
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.