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

191

Я пытаюсь добавить файл app.config в мою DLL, но все попытки не увенчались успехом.

Согласно MusicGenesis в « Помещении информации о конфигурации в DLL », это не должно быть проблемой. Итак, очевидно, что я делаю что-то не так ...

Следующий код должен вернуть мою ConnectionString из моей DLL:

return ConfigurationManager.AppSettings["ConnectionString"];

Однако когда я копирую файл app.config в консольное приложение, он работает нормально.

Любые идеи?

MegaByte
источник
Согласно указанному посту: если имя dll было MyDll.dll, то файл конфигурации должен быть MyDLL.dll.config. Так что, если вы читаете настройки конфигурации из dll, это должно относиться к его собственному конфигу, верно?
MegaByte
11
Неважно, какой код запрашивает - он ищет файл, указанный для параметра AppDomain: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
Марк Грэвелл
Примечание. Вопрос «помещение информации о конфигурации в 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;
Morbia
источник
В управляемом C ++ для VS 2008 System :: Configuration :: Configuration ^ appConfig = ConfigurationManager :: OpenExeConfiguration (Assembly :: GetExecutingAssembly () -> Location); Строка ^ name = appConfig-> AppSettings-> Settings ["name"] -> Value;
Ганс
Спасибо, это действительно решило мою проблему. Я занимался этим вопросом около двух дней, и до сих пор не получил его к работе. В качестве отладки теста 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 и 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 в выходной каталог вашего приложения. Возможно, кто-то найдет это полезным.

Sven
источник
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
Нет ... я так не думаю. "с длл" не имеет шансов; по умолчанию он просматривает файл конфигурации, определенный для домена приложения: my.exe.config
Марк
1
В частности, параметр AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.
Марк Гравелл
примечание: я попробовал OpenExeConfiguration, и я не уверен, что это также работает. Возможно, просто объединить конфиг с app.config?
Марк Гравелл
Это может быть сделано ... но не с такой же поддержкой и безопасностью, как файл app.config для EXE. Смотри мой ответ.
Крис Аммерман
13

ConfigurationManager.AppSettings возвращает параметры, определенные для приложения, а не для конкретной DLL, вы можете получить к ним доступ, но будут возвращены параметры приложения.

Если вы используете dll из другого приложения, то ConnectionString должен быть в app.settings приложения.

Хорхе Кордова
источник
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>тег, так что оглянись, твоя проблема могла отличаться, но не так далеко от моей.

Mugume David
источник
3

Поскольку сборка находится во временном кэше, вы должны объединить путь для получения конфигурации dll:

var appConfig = ConfigurationManager.OpenExeConfiguration(
    Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));
Лин Сон Ян
источник
вместо «Path.Combine (Environment.CurrentDirectory, Assembly.GetExecutingAssembly (). ManifestModule.Name)» можно использовать «Assembly.GetExecutingAssembly (). Местоположение»
Cadburry
3

Если вы используете библиотеки, которые ищут большое количество скрытых настроек, таких как WCF, вы можете рассмотреть возможность сделать это:

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

Или в PowerShell:

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

ИМО, эта техника является запахом кода и на самом деле подходит только для использования в специальных сценариях. Если вы захотите сделать это в рабочем коде, возможно, пришло время пересмотреть архитектуру.

Следующее НЕ рекомендуется:
В качестве технического любопытства, вот вариация на тему. Вы можете создать статический конструктор внутри одного из классов, размещенных в DLL, и сделать этот вызов оттуда. Я бы не рекомендовал делать это, кроме как в крайнем случае.

Пол Уильямс
источник
3

Полное решение не часто можно найти в одном месте ...

1) Создайте файл конфигурации приложения и назовите его «yourDllName.dll.config»
2) Щелкните правой кнопкой мыши файл конфигурации, созданный выше в VS Solution Explorer, выберите «Свойства»
--- set "Build Action" = Content
--- set "Copy To Output Directory" = Always
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 в ваш код, где вы хотите получить доступ к настройке config.

using System.Configuration;
using System.Reflection;

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

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

7) радуйся, все работает

ИМХО, это следует использовать только при разработке новой библиотеки / библиотеки.

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

Конфигурационный файл оказывается отличным справочным материалом, когда вы добавляете appSettings библиотеки dll в ваше реальное приложение.

Дэвид С Фукс
источник
3

Похоже, что эти файлы конфигурации действительно сбивают с толку, когда их поведение меняется от среды разработки к развертыванию. Очевидно, DLL может иметь свой собственный файл конфигурации, но как только вы копируете и вставляете dll (вместе с их файлом конфигурации) в другое место, все перестает работать. Единственное решение - вручную объединить файлы app.config в один файл, который будет использоваться только exec. Например, myapp.exe будет иметь файл myapp.exe.config, который содержит все настройки для всех библиотек, используемых myapp.exe. Я использую VS 2008.

Kenny
источник
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;

Есть несколько предостережений, упомянутых в статье. НТН

Томми С.
источник
1

Как говорит Марк, это невозможно (хотя Visual Studio позволяет добавлять файл конфигурации приложения в проект библиотеки классов).

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

Джерри Шенк
источник
0

В этом посте аналогичная проблема обсуждалась и решала мою проблему. Как динамически загрузить отдельный файл настроек приложения и объединить его с текущими настройками? может быть helpfu

dhailis
источник
Хотя это может теоретически ответить на вопрос, было бы предпочтительным включить сюда основные части ответа и предоставить ссылку для справки.
Ади
0

Для DLL это не должно зависеть от конфигурации, поскольку конфигурация принадлежит приложению, а не DLL.

Это объясняется здесь

Saravanan
источник
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);
    }
}
}
Дэвид Лопес
источник