Odpowiednik „app.config” dla biblioteki (DLL)

149

Czy istnieje odpowiednik app.configdla bibliotek (DLL)? Jeśli nie, jaki jest najłatwiejszy sposób przechowywania ustawień konfiguracji specyficznych dla biblioteki? Proszę wziąć pod uwagę, że biblioteka może być używana w różnych aplikacjach.

Louis Rhys
źródło

Odpowiedzi:

161

Państwo może mieć osobny plik konfiguracyjny, ale musisz ją przeczytać „ręcznie”, to ConfigurationManager.AppSettings["key"]będzie tylko do odczytu config Zgromadzenia uruchomiony.

Zakładając, że używasz Visual Studio jako swojego IDE, możesz kliknąć prawym przyciskiem myszy żądany projekt → Dodaj → Nowy element → Plik konfiguracji aplikacji

Spowoduje to dodanie App.configdo folderu projektu, umieszczenie ustawień tam w <appSettings>sekcji. Jeśli nie używasz programu Visual Studio i dodajesz plik ręcznie, nadaj mu nazwę: DllName.dll.config , w przeciwnym razie poniższy kod nie będzie działał poprawnie.

Teraz do odczytu z tego pliku mamy taką funkcję:

string GetAppSetting(Configuration config, string key)
{
    KeyValueConfigurationElement element = config.AppSettings.Settings[key];
    if (element != null)
    {
        string value = element.Value;
        if (!string.IsNullOrEmpty(value))
            return value;
    }
    return string.Empty;
}

I aby z niego skorzystać:

Configuration config = null;
string exeConfigPath = this.GetType().Assembly.Location;
try
{
    config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
}
catch (Exception ex)
{
    //handle errror here.. means DLL has no sattelite configuration file.
}

if (config != null)
{
    string myValue = GetAppSetting(config, "myKey");
    ...
}

Będziesz także musiał dodać odwołanie do przestrzeni nazw System.Configuration, aby klasa ConfigurationManager była dostępna.

Podczas budowania projektu, oprócz biblioteki DLL, będziesz również mieć DllName.dll.configplik, jest to plik, który musisz opublikować z samą biblioteką DLL.

Powyższy kod to podstawowy przykładowy kod. Osoby zainteresowane pełnym przykładem mogą skorzystać z innej odpowiedzi .

Czarodziej cienia jest dla ciebie ucho
źródło
1
@Rodney spróbuj zmienić string exeConfigPath = this.GetType().Assembly.Location;na coś takiego:string exeConfigPath = @"C:\MyFolder\DllFolder\ExeName.exe";
Shadow Wizard is Ear For You
1
Masz pomysł, jak to zrobić, jeśli dll jest kopiowany do nieznanego folderu przez narzędzie do testowania jednostkowego resharper?
Samouk
11
Wskazówka dla każdego, kto to wdraża: aby zautomatyzować generowanie pliku DllName.dll.config przez odwoływanie się do aplikacji, po prostu zmieniłem nazwę app.config na DllName.dll.config i zmieniłem właściwość „Kopiuj do katalogu wyjściowego” na „Zawsze kopiuj” . Poza tym potrzebowałem parametrów połączenia, które można pobrać za pomocą config.ConnectionStrings.ConnectionStrings [connStringName] .ConnectionString.
Jeff G
2
nazwa pliku app.cfg jest bardzo ważna do odczytywania wartości appcfg, nazwa pliku powinna brzmieć „DLL_NAME.DLL.CONFIG”
SaddamBinSyed
2
Korekta mojego ostatniego komentarza. W moim rozwiązaniu VS2017, usuwając moje nowe, niedziałające pliki App.config z moich projektów testowych i DLL i po prostu ponownie dodając je do mojego projektu testowego, nagle zaczyna działać! Ustawienia My App.config są teraz automatycznie uwzględniane w pliku DLL.configs. Co za ulga!
Zeek2
30

Niestety, możesz mieć tylko jeden plik app.config na plik wykonywalny, więc jeśli masz bibliotekę DLL połączoną z aplikacją, nie mogą one mieć własnych plików app.config.

Rozwiązanie: nie musisz umieszczać pliku App.config w projekcie biblioteki klas.
Umieszczasz plik App.config w aplikacji, która odwołuje się do biblioteki dll biblioteki klas.

Na przykład, powiedzmy, że mamy bibliotekę klas o nazwie MyClasses.dll, która używa pliku app.config w następujący sposób:

string connect = 
ConfigurationSettings.AppSettings["MyClasses.ConnectionString"];

Załóżmy teraz, że mamy aplikację Windows o nazwie MyApp.exe, która odwołuje się do MyClasses.dll. Zawierałby plik App.config z wpisem takim jak:

<appSettings>
    <add key="MyClasses.ConnectionString"
         value="Connection string body goes here" />
</appSettings>

LUB

Plik xml jest najlepszym odpowiednikiem pliku app.config. W razie potrzeby użyj serializacji / deserializacji XML. Możesz to nazwać, jak chcesz. Jeśli Twoja konfiguracja jest „statyczna” i nie wymaga zmian, możesz również dodać ją do projektu jako zasób osadzony.

Mam nadzieję, że to daje jakiś pomysł

PawanS
źródło
6
ConfigurationSettingsjest teraz przestarzały i zastąpiony przez ConfigurationManager, więc odpowiednik byłby terazConfigurationManager.AppSettings
Gone Coding
2
głos w dół. pytanie dotyczy biblioteki dll, a nie aplikacji. najlepsze rozwiązanie: stackoverflow.com/a/5191101/2935383
raiserle
3
Podejrzewam, że ta sugestia nie zadziała w przypadku bibliotek dll z późnym wiązaniem, które nie miałyby wiedzy o wywoływaniu ich przez plik wykonywalny.
beanmf
9

Pliki konfiguracyjne mają zakres aplikacji, a nie zakres zestawu. Musisz więc umieścić sekcje konfiguracyjne swojej biblioteki w pliku konfiguracyjnym każdej aplikacji, która korzysta z Twojej biblioteki.

To powiedziawszy, nie jest dobrą praktyką pobieranie konfiguracji z pliku konfiguracyjnego aplikacji, szczególnie z appSettingssekcji, w bibliotece klas. Jeśli twoja biblioteka potrzebuje parametrów, prawdopodobnie powinny one być przekazywane jako argumenty metod w konstruktorach, metodach fabrycznych itp. Przez tego, kto wywołuje twoją bibliotekę. Zapobiega to przypadkowemu używaniu przez wywołujące aplikacje pozycji konfiguracji, które były oczekiwane przez bibliotekę klas.

To powiedziawszy, pliki konfiguracyjne XML są niezwykle przydatne, więc najlepszym kompromisem, jaki znalazłem, jest użycie niestandardowych sekcji konfiguracji. Możesz umieścić konfigurację swojej biblioteki w pliku XML, który jest automatycznie odczytywany i analizowany przez framework i unikasz potencjalnych wypadków.

Możesz dowiedzieć się więcej o sekcjach konfiguracji niestandardowej w MSDN, a także Phil Haack ma na ich temat fajny artykuł .

madd0
źródło
7
„pobieranie konfiguracji z pliku konfiguracyjnego w bibliotece klas nie jest dobrą praktyką” - zdecydowanie się z tym nie zgadzam. Na przykład biblioteka klas DAL powinna normalnie pobierać dane konfiguracyjne, takie jak parametry połączenia z pliku konfiguracyjnego aplikacji, zamiast przekazywać te informacje z warstwy BLL. Wszystkie klasy Framework, które używają konfiguracji (np. Członkostwo ASP.NET), działają w ten sposób.
Joe
Nieznacznie zmodyfikowałem odpowiedź. Nadal trzymam się tego, co powiedziałem, ale masz rację, nigdy nie zamierzałem sugerować, że pliki konfiguracyjne w ogóle nie powinny być używane. Chodziło mi o to, że zamiast opartych na konwencji appSettingssekcje niestandardowe stanowią doskonałą alternatywę; w końcu jest to właściwie to, czego używa członkostwo ASP.NET.
madd0
5
public class ConfigMan
{
    #region Members

    string _assemblyLocation;
    Configuration _configuration;

    #endregion Members

    #region Constructors

    /// <summary>
    /// Loads config file settings for libraries that use assembly.dll.config files
    /// </summary>
    /// <param name="assemblyLocation">The full path or UNC location of the loaded file that contains the manifest.</param>
    public ConfigMan(string assemblyLocation)
    {
        _assemblyLocation = assemblyLocation;
    }

    #endregion Constructors

    #region Properties

    Configuration Configuration
    {
        get
        {
            if (_configuration == null)
            {
                try
                {
                    _configuration = ConfigurationManager.OpenExeConfiguration(_assemblyLocation);
                }
                catch (Exception exception)
                {
                }
            }
            return _configuration;
        }
    }

    #endregion Properties

    #region Methods

    public string GetAppSetting(string key)
    {
        string result = string.Empty;
        if (Configuration != null)
        {
            KeyValueConfigurationElement keyValueConfigurationElement = Configuration.AppSettings.Settings[key];
            if (keyValueConfigurationElement != null)
            {
                string value = keyValueConfigurationElement.Value;
                if (!string.IsNullOrEmpty(value)) result = value;
            }
        }
        return result;
    }

    #endregion Methods
}

Żeby coś zrobić, przerobiłem pierwszą odpowiedź na klasę. Użycie jest takie jak:

ConfigMan configMan = new ConfigMan(this.GetType().Assembly.Location);
var setting = configMan.GetAppSetting("AppSettingsKey");
Firegarden
źródło
4

Jeśli dodasz ustawienia do projektu biblioteki klas w programie Visual Studio (właściwości projektu, ustawienia), doda on plik app.config do projektu z odpowiednimi sekcjami userSettings / applatioNSettings i domyślnymi wartościami tych ustawień z ustawień. plik.

Jednak ten plik konfiguracyjny nie będzie używany w czasie wykonywania - zamiast tego biblioteka klas używa pliku konfiguracyjnego swojej aplikacji hostującej.

Uważam, że głównym powodem generowania tego pliku jest to, że można skopiować / wkleić ustawienia do pliku konfiguracyjnego aplikacji hosta.

Joe
źródło
4

Obecnie tworzę wtyczki dla marki oprogramowania detalicznego, które są w rzeczywistości bibliotekami klasy .net. Zgodnie z wymaganiami każda wtyczka musi być skonfigurowana przy użyciu pliku konfiguracyjnego. Po kilku badaniach i testach skompilowałem następującą klasę. Wykonuje swoją pracę bez zarzutu. Zauważ, że nie zaimplementowałem lokalnej obsługi wyjątków w moim przypadku, ponieważ łapię wyjątki na wyższym poziomie.

Może być konieczne wprowadzenie pewnych poprawek, aby uzyskać właściwy przecinek dziesiętny, w przypadku liczb dziesiętnych i podwójnych, ale działa to dobrze w przypadku moich informacji o kulturze ...

static class Settings
{
    static UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
    static Configuration myDllConfig = ConfigurationManager.OpenExeConfiguration(uri.Path);
    static AppSettingsSection AppSettings = (AppSettingsSection)myDllConfig.GetSection("appSettings");
    static NumberFormatInfo nfi = new NumberFormatInfo() 
    { 
        NumberGroupSeparator = "", 
        CurrencyDecimalSeparator = "." 
    };

    public static T Setting<T>(string name)
    {
        return (T)Convert.ChangeType(AppSettings.Settings[name].Value, typeof(T), nfi);
    }
}

Przykład pliku App.Config

<add key="Enabled" value="true" />
<add key="ExportPath" value="c:\" />
<add key="Seconds" value="25" />
<add key="Ratio" value="0.14" />

Stosowanie:

  somebooleanvar = Settings.Setting<bool>("Enabled");
  somestringlvar = Settings.Setting<string>("ExportPath");
  someintvar =     Settings.Setting<int>("Seconds");
  somedoublevar =  Settings.Setting<double>("Ratio");

Kredyty dla Shadow Wizard i MattC

Yiannis Leoussis
źródło
1
To powinna być akceptowana odpowiedź. Bardzo kompaktowy i „działa od razu po wyjęciu z pudełka”. Dobra rzecz
nmarler
2

Odpowiadając na pierwotne pytanie, zazwyczaj dodaję plik konfiguracyjny do mojego projektu testowego jako łącze; można następnie użyć atrybutu DeploymentItem, aby dodać go do folderu Out przebiegu testowego.

[TestClass]
[DeploymentItem("MyProject.Cache.dll.config")]
public class CacheTest
{
    .
    .
    .
    .
}

W odpowiedzi na komentarze, że zestawy nie mogą być specyficzne dla projektu, mogą i zapewnia to dużą elastyczność, szczególnie. podczas pracy z frameworkami IOC.

Allan Elder
źródło
2

Napotkałem ten sam problem i rozwiązałem go, tworząc klasę statyczną Parameterspo dodaniu pliku konfiguracyjnego aplikacji do projektu:

public static class Parameters
{
    // For a Web Application
    public static string PathConfig { get; private set; } =
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config");

    // For a Class Library
    public static string PathConfig { get; private set; } =
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "LibraryName.dll.config");

    public static string GetParameter(string paramName)
    {
        string paramValue = string.Empty;

        using (Stream stream = File.OpenRead(PathConfig))
        {
            XDocument xdoc = XDocument.Load(stream);

            XElement element = xdoc.Element("configuration").Element("appSettings").Elements().First(a => a.Attribute("key").Value == paramName);
            paramValue = element.Attribute("value").Value;
        }

        return paramValue;
    }
}

Następnie uzyskaj parametr taki jak ten:

Parameters.GetParameter("keyName");
krlzlx
źródło
1
Znakomity! Pomogło mi to w uruchomieniu automatycznych testów sterownika aplikacji systemu Windows na komputerach docelowych. Biblioteki dll w moim przypadku pochodziły z projektu testowego. Jedyne, co chciałbym dodać, to to, że w Win App Driver (i prawdopodobnie w innych formach automatycznego testowania) BaseDirectory jest w rzeczywistości folderem wyjściowym, który zmienia się za każdym razem. Musiałem podciąg taki jak ten ... AppDomain.CurrentDomain.BaseDirectory.Substring (0, AppDomain.CurrentDomain.BaseDirectory.IndexOf ("TestResults")). w ten sposób mogłem wyciąć niechciany folder wyjściowy, ponieważ mój plik konfiguracyjny znajdował się w tym samym folderze co moje testowe biblioteki DLL.
Ewan
1

zestawy nie mają własnego pliku app.config. Używają pliku app.config aplikacji, która ich używa. Więc jeśli twój zestaw oczekuje pewnych rzeczy w pliku konfiguracyjnym, po prostu upewnij się, że plik konfiguracyjny twojej aplikacji zawiera te wpisy.

Jeśli zestaw jest używany przez wiele aplikacji, każda z tych aplikacji będzie musiała mieć te wpisy w swoim pliku app.config.

To, co poleciłbym, to na przykład zdefiniowanie właściwości klas w twoim zestawie dla tych wartości

private string ExternalServicesUrl
{
  get
  {
    string externalServiceUrl = ConfigurationManager.AppSettings["ExternalServicesUrl"];
    if (String.IsNullOrEmpty(externalServiceUrl))
      throw new MissingConfigFileAppSettings("The Config file is missing the appSettings entry for: ExternalServicesUrl");
    return externalServiceUrl;
  }
}

Tutaj właściwość ExternalServicesUrl pobiera swoją wartość z pliku konfiguracyjnego aplikacji. Jeśli w którejkolwiek aplikacji używającej tego zestawu brakuje tego ustawienia w pliku konfiguracyjnym, pojawi się wyjątek o jasne jest, że czegoś brakuje.

MissingConfigFileAppSettings to niestandardowy wyjątek. Możesz zgłosić inny wyjątek.

Oczywiście lepszym rozwiązaniem byłoby zapewnienie metody tych klas jako parametrów, a nie poleganie na ustawieniach pliku konfiguracyjnego. W ten sposób aplikacje korzystające z tych klas mogą decydować, skąd i jak dostarczają te wartości.

Shiv Kumar
źródło
Uwaga do powyższego: podczas uruchamiania testów xUnit na bibliotece DLL zestawu .NET, xUnit odczyta plik .config biblioteki w czasie wykonywania. I zignoruje każdy plik App.config dodany do projektu testowego lub DLL.
Zeek2
1

Użyj Dodaj istniejący element, wybierz konfigurację aplikacji z projektu dll. Przed kliknięciem przycisku dodaj użyj małej strzałki w dół po prawej stronie przycisku dodawania, aby „dodać jako link”

Robię to cały czas w moim dev.

ghostJago
źródło
1

Preambuła : używam NET 2.0;

Rozwiązanie przesłane przez Yiannisa Leoussisa jest do zaakceptowania, ale miałem z nim pewien problem.

Po pierwsze, static AppSettingsSection AppSettings = (AppSettingsSection)myDllConfig.GetSection("appSettings");zwraca null. Musiałem to zmienić nastatic AppSettingSection = myDllConfig.AppSettings;

Wtedy return (T)Convert.ChangeType(AppSettings.Settings[name].Value, typeof(T), nfi);nie ma haczyka na wyjątki. Więc zmieniłem to

try
{
    return (T)Convert.ChangeType(AppSettings.Settings[name].Value, typeof(T), nfi);
}
catch (Exception ex)
{
    return default(T);
}

Działa to bardzo dobrze, ale jeśli masz inną bibliotekę dll, za każdym razem musisz przepisać kod dla każdego zestawu. Tak więc to jest moja wersja dla klasy do tworzenia instancji za każdym razem, gdy potrzebujesz.

public class Settings
{
    private AppSettingsSection _appSettings;
    private NumberFormatInfo _nfi;

    public Settings(Assembly currentAssembly)
    {
        UriBuilder uri = new UriBuilder(currentAssembly.CodeBase);
        string configPath = Uri.UnescapeDataString(uri.Path);
        Configuration myDllConfig = ConfigurationManager.OpenExeConfiguration(configPath);
        _appSettings = myDllConfig.AppSettings;
        _nfi = new NumberFormatInfo() 
        { 
            NumberGroupSeparator = "", 
            CurrencyDecimalSeparator = "." 
        };
    }


    public T Setting<T>(string name)
    {
        try
        {
            return (T)Convert.ChangeType(_appSettings.Settings[name].Value, typeof(T), _nfi);
        }
        catch (Exception ex)
        {
            return default(T);
        }
    }
}

W przypadku konfiguracji:

<add key="Enabled" value="true" />
<add key="ExportPath" value="c:\" />
<add key="Seconds" value="25" />
<add key="Ratio" value="0.14" />

Użyj go jako:

Settings _setting = new Settings(Assembly.GetExecutingAssembly());

somebooleanvar = _settings.Setting<bool>("Enabled");
somestringlvar = _settings.Setting<string>("ExportPath");
someintvar =     _settings.Setting<int>("Seconds");
somedoublevar =  _settings.Setting<double>("Ratio");
Matteo Gaggiano
źródło
Prosimy o zapoznanie się z głosem za usunięciem. Mój błąd polegał na wysłaniu odpowiedzi podczas jej pisania.
Matteo Gaggiano,
0

O ile wiem, musisz skopiować i wkleić żądane sekcje z biblioteki .config do pliku .config aplikacji. Otrzymujesz tylko 1 plik app.config na instancję wykonywalną.

mikrofon
źródło
jeśli używasz niestandardowych sekcji konfiguracji, możesz użyć atrybutu configSource: <MySection configSource = "mysection.config" /> i kopiować plik konfiguracyjny tylko z biblioteką dll
Jan Remunda
Dodałem nowe pytania, np. O funkcję zwracającą zawsze pusty ciąg znaków i ustawienia serwera poczty> stackoverflow.com/questions/25123544/ ... i> stackoverflow.com/questions/25138788/ ... więc mam nadzieję, że ktoś na nie odpowie jak ja jestem prawie na skraju zakodowania wartości w bibliotece DLL!
MonkeyMagix,
0

Dlaczego nie użyć:

  • [ProjectNamespace].Properties.Settings.Default.[KeyProperty] dla C #
  • My.Settings.[KeyProperty] dla VB.NET

Wystarczy wizualnie zaktualizować te właściwości w czasie projektowania poprzez:

[Solution Project]->Properties->Settings

Pedro Mora
źródło
Spowoduje to automatyczne utworzenie pliku konfiguracyjnego dla dll. Ale nie możesz odczytać zmodyfikowanych wartości z pliku konfiguracyjnego w czasie wykonywania. Wreszcie pokaże wartości aplikacji wywołującej. Zobacz także @Joe answer
Code Pope
Nie, jeśli jest skonfigurowany dla konfiguracji użytkownika. Chodzi o to, aby edytować to, czego potrzebuje użytkownik, konfigurować je w czasie wykonywania, a następnie zapisywać. Następnie, gdy użytkownik pracuje z biblioteką, ładuje ona jej konfigurację, zapisaną w odpowiedniej ścieżce użytkownika, ale działa tylko dla niego.
Pedro Mora
0

użycie z konfiguracji musi być bardzo łatwe w następujący sposób:

var config = new MiniConfig("setting.conf");

config.AddOrUpdate("port", "1580");

if (config.TryGet("port", out int port)) // if config exist
{
    Console.Write(port);
}

aby uzyskać więcej informacji, zobacz MiniConfig

Rahmat Anjirabi
źródło