Plik konfiguracyjny C # DLL

191

Próbuję dodać plik app.config do mojej biblioteki DLL, ale wszystkie próby zakończyły się niepowodzeniem.

Według MusicGenesis w „ Umieszczanie informacji o konfiguracji w bibliotece DLL ” nie powinno to stanowić problemu. Więc oczywiście robię coś złego ...

Poniższy kod powinien zwrócić mój ConnectionString z mojej biblioteki DLL:

return ConfigurationManager.AppSettings["ConnectionString"];

Jednak po skopiowaniu pliku app.config do aplikacji konsoli działa dobrze.

Jakieś pomysły?

MegaByte
źródło
Według wspomnianego postu: jeśli dll miał na imię MyDll.dll, to plik konfiguracyjny powinien mieć nazwę MyDLL.dll.config. Więc jeśli czytasz ustawienia konfiguracji z poziomu biblioteki dll, powinien on odnosić się do własnej konfiguracji, prawda?
MegaByte
11
Nie ma znaczenia, o jaki kod prosi - szuka pliku określonego dla AppDomain: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile ustawienie
Marc Gravell
Uwaga: pytanie „umieszczenie informacji o konfiguracji w bibliotece DLL” dotyczy rozdzielenia kodu konfiguracji aplikacji w bibliotece, aby był oddzielony od głównego kodu aplikacji. To bardzo różni się od oddzielnego pliku konfiguracyjnego i specjalnego dla biblioteki DLL.
Chris Ammerman,
zobacz ten post [wpisz opis linku tutaj] [1], było dla mnie rozwiązaniem [1]: stackoverflow.com/questions/2389290/...
daili
zobacz ten post [Jak dynamicznie ładować osobny plik ustawień aplikacji i scalić z bieżącymi ustawieniami?] [1] może być pomocny [1]: stackoverflow.com/questions/2389290/...
daili

Odpowiedzi:

277

Utworzenie pliku konfiguracyjnego .NET dla pliku .DLL nie jest łatwe. Mechanizm konfiguracyjny .NET ma wiele wbudowanych funkcji, które ułatwiają aktualizację / aktualizację aplikacji i chronią zainstalowane aplikacje przed wzajemnym zadeptaniem plików konfiguracyjnych.

Istnieje duża różnica między sposobem użycia biblioteki DLL a sposobem użycia aplikacji. Jest mało prawdopodobne, aby na tym samym komputerze zainstalowanych było wiele kopii aplikacji dla tego samego użytkownika. Ale możesz mieć 100 różnych aplikacji lub bibliotek, które korzystają z niektórych bibliotek DLL .NET.

Chociaż rzadko zachodzi potrzeba osobnego śledzenia ustawień dla różnych kopii aplikacji w ramach jednego profilu użytkownika, tak jest bardzo ważne mało prawdopodobne, aby wszystkie różne zastosowania biblioteki DLL współdzieliły ze sobą konfigurację. Z tego powodu, gdy pobierasz obiekt konfiguracji przy użyciu metody „normalnej”, zwracany obiekt jest powiązany z konfiguracją domeny aplikacji, w której wykonujesz, a nie z konkretnym zestawem.

Domena aplikacji jest powiązana z zestawem głównym, który załadował zestaw, w którym znajduje się Twój kod. W większości przypadków będzie to zestaw głównego pliku .EXE, który załadował plik .DLL. Możliwe jest rozpinanie innych domen aplikacji w aplikacji, ale musisz jawnie podać informacje o tym, czym jest zestaw główny tej domeny aplikacji.

Z tego powodu procedura tworzenia pliku konfiguracyjnego specyficznego dla biblioteki nie jest tak wygodna. Jest to ten sam proces, którego użyłbyś do utworzenia dowolnego przenośnego pliku konfiguracyjnego niepowiązanego z żadnym konkretnym zestawem, ale dla którego chcesz skorzystać ze schematu XML platformy .NET, sekcji konfiguracji i mechanizmów elementów konfiguracji itp. Oznacza to utworzenie ExeConfigurationFileMapobiektu , ładowanie danych w celu zidentyfikowania miejsca przechowywania pliku konfiguracyjnego, a następnie wywoływanie ConfigurationManager. OpenMappedExeConfigurationaby otworzyć go w nowej Configurationinstancji. To będzie wyciąć cię z ochrony wersji oferowanej przez mechanizm automatycznego generowania ścieżki.

Statystycznie rzecz biorąc, prawdopodobnie używasz tej biblioteki w ustawieniach wewnętrznych i jest mało prawdopodobne, że będziesz mieć wiele aplikacji korzystających z niej na jednym komputerze / użytkowniku. Ale jeśli nie, jest coś, o czym należy pamiętać. Jeśli używasz jednego globalnego pliku konfiguracyjnego dla swojej biblioteki DLL, niezależnie od aplikacji, do której się ona odwołuje, musisz martwić się o konflikty dostępu. Jeśli dwie aplikacje odwołujące się do Twojej biblioteki działają jednocześnie, każda z własnąConfiguration otwartym obiektem, to kiedy jedna zapisze zmiany, spowoduje to wyjątek przy następnej próbie pobrania lub zapisania danych w drugiej aplikacji.

Najbezpieczniejszym i najprostszym sposobem na obejście tego jest wymaganie, aby zestaw, który ładuje bibliotekę DLL, również podał pewne informacje o sobie lub wykrył je, badając domenę aplikacji zestawu odniesienia. Użyj tego, aby utworzyć strukturę folderów do przechowywania osobnych plików konfiguracji użytkownika dla każdej aplikacji odwołującej się do twojej biblioteki DLL.

Jeśli masz pewność, że chcesz mieć globalne ustawienia dla swojej biblioteki DLL, bez względu na to, gdzie jest do niej odniesienie, musisz określić jej lokalizację zamiast .NET automatycznie wymyślić odpowiednią. Musisz także być agresywny w zarządzaniu dostępem do pliku. Musisz buforować jak najwięcej, utrzymując Configurationinstancję TYLKO tak długo, jak długo trwa ładowanie lub zapisywanie, otwierając bezpośrednio przed i usuwając zaraz po. I w końcu będziesz potrzebować mechanizmu blokującego, aby chronić plik, gdy jest on edytowany przez jedną z aplikacji korzystających z biblioteki.

Chris Ammerman
źródło
myślę, że mechanizm synchronizacji powinien być „zdarzeniem nazwanym” itp., ponieważ jest to proces
wieloprocesowy
8
: / Meh. Nasza to potworna aplikacja korporacyjna z głównym .exe napisanym przez facetów w innej strefie czasowej oraz modułami reprezentowanymi przez różne biblioteki DLL i dynamicznie powiązanymi za pomocą niestandardowej struktury wtyczek. Wszystko to „musisz zadbać o to, by wiele aplikacji mogło jednocześnie korzystać z biblioteki DLL”, jest tak, że pompatyczność jest błędna.
JohnL4,
Ponadto w dużej części mojej kariery widziałem, że te cudowne ogólne mechanizmy współdzielonych obiektów są całkowicie ignorowane, a zespoły tworzą biblioteki DLL (lub pliki JAR), które mogą być używane tylko w jednym kontekście (i muszą być obecne, w przeciwnym razie aplikacja zawiedzie) ). Równie dobrze mogą być związani statycznie, ale to pasja.
JohnL4,
1
„Statystycznie rzecz biorąc, prawdopodobnie używasz tej biblioteki w ustawieniach wewnętrznych i jest mało prawdopodobne, że będziesz mieć wiele aplikacji korzystających z niej na jednym komputerze / użytkowniku”. Różnica między teorią a praktyką sprawia, że ​​czasami jestem zrzędliwy.
JohnL4,
1
@Panzercrisis, funkcja ustawień. Ustawień programu Visual Studio automatycznie tworzy ścieżki specyficzne dla wersji dla wszystkich ustawień użytkownika. Zobacz: stackoverflow.com/questions/35778528/...
Deantwo
101

jeśli chcesz odczytać ustawienia z pliku konfiguracyjnego DLL, ale nie z aplikacji root webconcon lub app.config, użyj poniższego kodu, aby odczytać konfigurację w bibliotece dll.

var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
string dllConfigData = appConfig.AppSettings.Settings["dllConfigData"].Value;
Morbia
źródło
W zarządzanym C ++ dla VS 2008 System :: Konfiguracja :: Konfiguracja ^ appConfig = ConfigurationManager :: OpenExeConfiguration (Assembly :: GetExecutingAssembly () -> Location); Ciąg ^ name = appConfig-> AppSettings-> Ustawienia [„nazwa”] -> Wartość;
Hans
Dzięki, to naprawdę rozwiązało mój problem. Zajmowałem się tym problemem od około dwóch dni i dopiero teraz zacząłem działać. Podczas debugowania testu Menedżer konfiguracji czytał z machine.config -Myślę-, ponieważ wyciągnięte parametry połączenia dotyczyły SQLExpress-ciągu połączenia, którego nie wymieniłem-.
yopez83
19

Miałem ten sam problem i przeszukiwałem Internet przez kilka godzin, ale nie mogłem znaleźć żadnego rozwiązania, więc zrobiłem własny. Zastanawiałem się, dlaczego system konfiguracji .net jest tak nieelastyczny.

Tło: Chcę, aby mój plik DAL.dll miał własny plik konfiguracyjny dla ustawień bazy danych i DAL. Potrzebuję również app.config dla biblioteki Enterprise i jej własnych konfiguracji. Potrzebuję więc zarówno app.config, jak i dll.config.

To, czego nie chciałem robić, to przekazywanie wszystkich właściwości / ustawień z aplikacji do mojej warstwy DAL!

wygięcie „AppDomain.CurrentDomain.SetupInformation.ConfigurationFile” nie jest możliwe, ponieważ potrzebuję go do normalnego zachowania app.config.

Moje wymagania / punkty widzenia były następujące:

  • NIE ma ręcznego kopiowania czegokolwiek z ClassLibrary1.dll.config do WindowsFormsApplication1.exe.config, ponieważ jest to niemożliwe do odtworzenia dla innych programistów.
  • zachowuję użycie silnego pisania „Properties.Settings.Default.NameOfValue” (zachowanie ustawień), ponieważ uważam, że jest to ważna funkcja i nie chciałem jej utracić
  • Odkryłem brak ApplicationSettingsBase do wstrzyknięcia własnego / niestandardowego pliku konfiguracyjnego lub zarządzania (wszystkie niezbędne pola są prywatne w tych klasach)
  • użycie przekierowania pliku „configSource” nie jest możliwe, ponieważ musielibyśmy skopiować / przepisać ClassLibrary1.dll.config i dostarczyć kilka plików XML dla kilku sekcji (też mi się nie podobało)
  • Nie lubiłem pisać własnego SettingsProvider do tego prostego zadania, jak sugeruje MSDN, ponieważ myślałem, że to po prostu za dużo
  • Potrzebuję tylko sekcji applicationSettings i connectionStrings z pliku konfiguracyjnego

Wymyśliłem modyfikację pliku Settings.cs i wdrożyłem metodę, która otwiera ClassLibrary1.dll.config i odczytuje informacje o sekcji w prywatnym polu. Następnie zastąpiłem „this [string propertyName]”, więc wygenerowane Settings.Desginer.cs wywołują moją nową właściwość zamiast klasy podstawowej. Tam ustawienie jest odczytywane z listy.

Wreszcie jest następujący kod:

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

Musisz tylko skopiować ClassLibrary1.dll.config z katalogu wyjściowego ClassLibrary1 do katalogu wyjściowego aplikacji. Być może ktoś uzna to za przydatne.

Sven
źródło
14

Korzystając z Menedżera konfiguracji, jestem prawie pewien, że ładuje proces /AppDomain plik konfiguracji (app.config / web.config). Jeśli chcesz załadować określony plik konfiguracyjny, musisz poprosić o ten plik według nazwy ...

Możesz spróbować:

var config = ConfigurationManager.OpenExeConfiguration("foo.dll");
config.ConnectionStrings. [etc]
Marc Gravell
źródło
Według wspomnianego postu: jeśli dll miał na imię MyDll.dll, to plik konfiguracyjny powinien mieć nazwę MyDLL.dll.config. Więc jeśli czytasz ustawienia konfiguracji z poziomu biblioteki dll, powinien on odnosić się do własnej konfiguracji, prawda?
MegaByte
1
Nie ... nie wydaje mi się. „from with dll” nie ma żadnych szans; domyślnie szuka pliku konfiguracyjnego zdefiniowanego dla AppDomain: my.exe.config
Marc Gravell
1
W szczególności ustawienie AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.
Marc Gravell
Uwaga: Próbowałem OpenExeConfiguration i nie jestem pewien, czy to działa. Być może po prostu połącz konfigurację z app.config?
Marc Gravell
To może być zrobione ... ale nie z tego samego rodzaju wsparcia i bezpieczeństwa, co plik app.config do EXE. Zobacz moją odpowiedź.
Chris Ammerman,
13

ConfigurationManager.AppSettings zwraca ustawienia zdefiniowane dla aplikacji, a nie dla konkretnej biblioteki DLL, można uzyskać do nich dostęp, ale zostaną zwrócone ustawienia aplikacji.

Jeśli używasz dll z innej aplikacji, ConnectionString powinien znajdować się w ustawieniach aplikacji.

Jorge Córdoba
źródło
6

Wiem, że to spóźnia się na imprezę, ale pomyślałem, że podzielę się rozwiązaniem, którego używam dla bibliotek DLL.

Jestem bardziej szkołą myślenia KISS, więc kiedy mam bibliotekę .NET DLL, która chce przechowywać zewnętrzne punkty danych, które kontrolują jej działanie lub kierunek, itp. Po prostu tworzę klasę „config”, która ma tylko właściwości publiczne które przechowują wszystkie potrzebne punkty danych i które chciałbym mieć możliwość kontrolowania zewnętrznego pliku DLL, aby zapobiec ponownej kompilacji w celu wprowadzenia zmian. Następnie używam serializacji XML .Net do zapisania i załadowania reprezentacji obiektowej klasy do pliku.

Istnieje wiele sposobów radzenia sobie z czytaniem i dostępem do nich, od Singletona, statycznej klasy narzędziowej, po metody rozszerzeń itp. Zależy to od struktury biblioteki DLL i metody, która najlepiej pasuje do biblioteki DLL.

Rodney S. Foley
źródło
Korzystam również z tego podejścia i cieszę się z dotychczasowego sposobu jego realizacji.
Dave
4

masz rację, możesz odczytać plik konfiguracyjny dll. Walczyłem z tym przez jeden dzień, dopóki nie dowiedziałem się, że problem tkwi w moim pliku konfiguracyjnym. Zobacz mój kod poniżej. był w stanie uciec.

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

mój Plugin1.dll.configwyglądał jak poniżej;

<?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>

Dowiedziałem się, że w moim pliku konfiguracyjnym brakuje <appSettings>tagu, więc rozejrzyj się, twój problem mógł być inny, ale nie tak daleki od mojego.

mugume david
źródło
3

Ponieważ zestaw znajduje się w tymczasowej pamięci podręcznej, należy połączyć ścieżkę, aby uzyskać konfigurację biblioteki dll:

var appConfig = ConfigurationManager.OpenExeConfiguration(
    Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));
Lin Song Yang
źródło
zamiast „Path.Combine (Environment.CurrentDirectory, Assembly.GetExecutingAssembly (). ManifestModule.Name)” można użyć „Assembly.GetExecutingAssembly (). Lokalizacja”
Cadburry
3

Jeśli korzystasz z bibliotek, które wyszukują wiele konfiguracji za kulisami, takich jak WCF, możesz rozważyć zrobienie tego:

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

Lub w PowerShell:

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

IMO ta technika ma zapach kodu i naprawdę nadaje się tylko do użytku w skryptach ad hoc. Jeśli chcesz to zrobić w kodzie produkcyjnym, być może nadszedł czas na przegląd architektoniczny.

NIE jest zalecane:
Jako ciekawostkę techniczną, oto wariacja na temat. Możesz utworzyć konstruktora statycznego w jednej z klas znajdujących się w bibliotece DLL i stamtąd wywołać to wywołanie. Nie poleciłbym tego robić, chyba że w ostateczności.

Paul Williams
źródło
3

Pełne rozwiązanie nie jest często spotykane w jednym miejscu ...

1) Utwórz plik konfiguracyjny aplikacji i nazwij go „twojaDllName.dll.config”
2) Kliknij prawym przyciskiem myszy plik konfiguracyjny utworzony powyżej w VS Solution Explorer, kliknij właściwości
--- ustaw „Kompilacja działania” = Treść
--- ustaw „Kopiuj do katalogu wyjściowego” = Zawsze
3) Dodaj sekcję appSettings do pliku konfiguracyjnego (twojaDllName.dll.config) z twojąNazwaKluczową iNazwaKluczowa

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

4) Dodaj System.Configuration do dll / class / project odniesienia
5) Dodaj instrukcje using do swojego kodu, w którym zamierzasz uzyskać dostęp do ustawienia konfiguracji

using System.Configuration;
using System.Reflection;

6) Aby uzyskać dostęp do wartości

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

7) radujcie się, działa

IMHO, należy tego używać tylko podczas opracowywania nowej biblioteki dll / biblioteki.

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

Plik konfiguracyjny kończy się świetnym odniesieniem, ponieważ gdy dodajesz dll's appSettings do faktycznej aplikacji.

David C Fuchs
źródło
3

Wygląda na to, że te pliki konfiguracyjne są bardzo mylące, ponieważ ich zachowanie zmienia się ze środowiska deweloperskiego na wdrożenie. Najwyraźniej biblioteka DLL może mieć własny plik konfiguracyjny, ale po skopiowaniu i wklejeniu biblioteki dll (wraz z plikiem konfiguracyjnym) w innym miejscu cała sprawa przestała działać. Jedynym rozwiązaniem jest ręczne scalenie plików app.config w jeden plik, który będzie używany tylko przez exec. Na przykład myapp.exe będzie miał plik myapp.exe.config, który zawiera wszystkie ustawienia wszystkich bibliotek dll używanych przez myapp.exe. Używam VS 2008.

Kenny
źródło
2

Znalazłem dobre rozwiązanie tego problemu. Używam VS 2008 C #. Moje rozwiązanie polega na użyciu odrębnych przestrzeni nazw między wieloma plikami konfiguracyjnymi. Rozwiązanie opublikowałem na moim blogu: http://tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html .

Na przykład:

Ta przestrzeń nazw odczytuje / zapisuje ustawienia dll:

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

Ta przestrzeń nazw odczytuje / zapisuje ustawienia exe:

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

W artykule wymieniono kilka zastrzeżeń. HTH

Tommie C.
źródło
1

Jak mówi Marc, nie jest to możliwe (chociaż Visual Studio pozwala na dodanie pliku konfiguracji aplikacji w projekcie biblioteki klas).

Możesz sprawdzić klasę AssemblySettings, która wydaje się umożliwiać pliki konfiguracyjne zestawu.

Gerrie Schenck
źródło
0

W przypadku biblioteki dll nie powinno to zależeć od konfiguracji, ponieważ konfiguracja jest własnością aplikacji, a nie biblioteki dll.

Wyjaśniono to tutaj

Saravanan
źródło
0

możesz użyć tego kodu:

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);
    }
}
}
David Lopes
źródło