Equivalente a 'app.config' per una libreria (DLL)


149

Esiste un equivalente app.configper le librerie (DLL)? In caso contrario, qual è il modo più semplice per archiviare le impostazioni di configurazione specifiche di una libreria? Si prega di considerare che la libreria potrebbe essere utilizzata in diverse applicazioni.

Risposte:


161

È possibile avere file di configurazione separato, ma dovrete leggerlo "manualmente", la ConfigurationManager.AppSettings["key"]leggerà solo la configurazione della corsa di assemblaggio.

Supponendo che stai utilizzando Visual Studio come IDE, puoi fare clic con il pulsante destro del mouse sul progetto desiderato → Aggiungi → Nuovo elemento → File di configurazione dell'applicazione

Questo verrà aggiunto App.configalla cartella del progetto, inserisci le tue impostazioni nella <appSettings>sezione. Nel caso in cui non si utilizzi Visual Studio e si aggiunga il file manualmente, assicurarsi di dargli tale nome: DllName.dll.config , altrimenti il ​​codice seguente non funzionerà correttamente.

Ora per leggere da questo file hanno una tale funzione:

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

E per usarlo:

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

Dovrai anche aggiungere un riferimento allo spazio dei nomi System.Configuration per rendere disponibile la classe ConfigurationManager.

Quando costruisci il progetto, oltre alla DLL avrai anche il DllName.dll.configfile, questo è il file che devi pubblicare con la stessa DLL.

Quanto sopra è un codice di esempio di base, per coloro che sono interessati a un esempio in scala reale, fare riferimento a questa altra risposta .


1
@Rodney prova a cambiare string exeConfigPath = this.GetType().Assembly.Location;in qualcosa del tipo:string exeConfigPath = @"C:\MyFolder\DllFolder\ExeName.exe";
Shadow Wizard è Ear For You

1
Qualche idea su come eseguire questa operazione se la DLL viene copiata in una cartella sconosciuta dallo strumento di test dell'unità resharper?
Autodidatta,

11
Un suggerimento per chiunque lo stia implementando: per automatizzare la generazione di DllName.dll.config facendo riferimento alle applicazioni, ho semplicemente rinominato app.config in DllName.dll.config e modificato la proprietà "Copia nella directory di output" in "Copia sempre" . Inoltre, avevo bisogno di stringhe di connessione, che possono essere recuperate utilizzando config.ConnectionStrings.ConnectionStrings [connStringName] .ConnectionString.
Jeff G,

2
il nome del file app.cfg è molto importante per leggere i valori di appcfg, il nome del file dovrebbe essere "DLL_NAME.DLL.CONFIG"
SaddamBinSyed il

2
Correzione al mio ultimo commento. Nella mia soluzione VS2017, rimuovendo i miei nuovi file App.config non funzionanti dai miei progetti di test e DLL e semplicemente aggiungendoli nuovamente al mio progetto di test, all'improvviso ha iniziato a funzionare! La mia impostazione App.config ora viene automaticamente inclusa in DLL.configs. Che sollievo!
Zeek2,

30

Sfortunatamente, puoi avere un solo file app.config per eseguibile, quindi se hai DLL collegate nella tua applicazione, non possono avere i loro file app.config.

La soluzione è: non è necessario inserire il file App.config nel progetto della libreria di classi.
Inserisci il file App.config nell'applicazione che fa riferimento alla DLL della libreria di classi.

Ad esempio, supponiamo di avere una libreria di classi denominata MyClasses.dll che utilizza il file app.config in questo modo:

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

Supponiamo ora che abbiamo un'applicazione Windows denominata MyApp.exe che fa riferimento a MyClasses.dll. Conterrebbe un App.config con una voce come:

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

O

Un file XML è il migliore equivalente per app.config. Utilizzare xml serializzare / deserializzare secondo necessità. Puoi chiamarlo come vuoi. Se la configurazione è "statica" e non è necessario modificarla, è possibile aggiungerla al progetto come risorsa incorporata.

Spero che dia qualche idea


6
ConfigurationSettingsè ora obsoleto e sostituito da ConfigurationManager, quindi l'equivalente sarebbe oraConfigurationManager.AppSettings
Gone Coding

2
voto contrario. la domanda è per dll e non per app. soluzione migliore: stackoverflow.com/a/5191101/2935383
raiserle

3
Sospetto che questo suggerimento non funzionerà nel caso di DLL in ritardo che non avrebbero conoscenza dell'eseguibile che le chiama.
beanmf,

9

I file di configurazione sono nell'ambito dell'applicazione e non nell'ambito dell'assembly. Quindi dovrai inserire le sezioni di configurazione della tua libreria nel file di configurazione di ogni applicazione che utilizza la tua libreria.

Detto questo, non è una buona pratica ottenere la configurazione dal file di configurazione dell'applicazione, specialmente la appSettingssezione, in una libreria di classi. Se la tua libreria ha bisogno di parametri, probabilmente dovrebbero essere passati come argomenti di metodo nei costruttori, metodi di fabbrica, ecc. Da chiunque stia chiamando la tua biblioteca. Ciò impedisce alle applicazioni di chiamata di riutilizzare accidentalmente le voci di configurazione previste dalla libreria di classi.

Detto questo, i file di configurazione XML sono estremamente utili, quindi il miglior compromesso che ho trovato è l'utilizzo di sezioni di configurazione personalizzate. Puoi mettere la configurazione della tua libreria in un file XML che viene letto e analizzato automaticamente dal framework ed eviti potenziali incidenti.

Puoi saperne di più sulle sezioni di configurazione personalizzate su MSDN e anche Phil Haack ha un bell'articolo su di esse.


7
"Non è una buona pratica ottenere la configurazione da un file di configurazione in una libreria di classi" - Non sono assolutamente d'accordo con questo. Ad esempio, una libreria di classi DAL dovrebbe normalmente ottenere dati di configurazione come stringhe di connessione dal file di configurazione dell'applicazione anziché far passare queste informazioni dal livello BLL. Qualsiasi classe Framework che utilizza la configurazione (ad es. Appartenenza ASP.NET) funziona in questo modo.
Joe

Ho modificato leggermente la mia risposta. Continuo a sostenere ciò che ho detto, ma hai ragione, non ho mai avuto intenzione di implicare che i file di configurazione non debbano essere usati affatto. Ciò che intendevo dire era che, invece che basate su convenzioni appSettings, le sezioni personalizzate offrono un'ottima alternativa; è praticamente ciò che utilizza l'abbonamento ASP.NET dopo tutto.
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
}

Solo per qualcosa da fare, ho riformulato la risposta migliore in una classe. L'utilizzo è simile a:

ConfigMan configMan = new ConfigMan(this.GetType().Assembly.Location);
var setting = configMan.GetAppSetting("AppSettingsKey");

4

Se aggiungi Impostazioni a un progetto Libreria di classi in Visual Studio (Proprietà progetto, Impostazioni), verrà aggiunto un file app.config al tuo progetto con le relative sezioni Impostazioni utente / ApplicazioneN Impostazioni e i valori predefiniti per queste impostazioni dalle Impostazioni. file.

Tuttavia, questo file di configurazione non verrà utilizzato in fase di runtime, invece la libreria di classi utilizza il file di configurazione della sua applicazione di hosting.

Credo che il motivo principale per la generazione di questo file sia quello di poter copiare / incollare le impostazioni nel file di configurazione dell'applicazione host.


4

Attualmente sto creando plugin per un marchio di software al dettaglio, che in realtà sono librerie di classi .net. Come requisito, ogni plugin deve essere configurato usando un file di configurazione. Dopo un po 'di ricerche e prove, ho compilato la seguente classe. Fa il lavoro perfettamente. Si noti che non ho implementato la gestione delle eccezioni locali nel mio caso perché prendo eccezioni a un livello superiore.

Alcune modifiche potrebbero essere necessarie per ottenere il punto decimale giusto, in caso di decimali e doppi, ma funziona bene per il mio CultureInfo ...

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

Esempio di file App.Config

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

Uso:

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

Crediti a Shadow Wizard e MattC


1
Questa dovrebbe essere la risposta accettata. Molto compatto e "funziona subito". Roba buona
nmarler

2

In risposta alla domanda originale, in genere aggiungo il file di configurazione nel mio progetto di test come collegamento; è quindi possibile utilizzare l'attributo DeploymentItem per aggiungerlo alla cartella Out dell'esecuzione del test.

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

In risposta ai commenti secondo cui gli assiemi non possono essere specifici del progetto, possono farlo e offre grande flessibilità esp. quando si lavora con framework IOC.


2

Ho riscontrato lo stesso problema e risolto creando una classe statica Parametersdopo aver aggiunto un file di configurazione dell'applicazione al progetto:

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

Quindi ottenere un parametro come questo:

Parameters.GetParameter("keyName");

1
Brillante! Questo mi ha aiutato a eseguire i test automatici del mio driver di applicazione Windows su macchine target. Le dll nel mio caso provenivano da un progetto di test. L'unica cosa che aggiungerei è che in Win App Driver (e forse altre forme di test automatizzati), BaseDirectory è in realtà la cartella di output che cambia ogni volta. Ho dovuto eseguire la sottostringa in questo modo ... AppDomain.CurrentDomain.BaseDirectory.Substring (0, AppDomain.CurrentDomain.BaseDirectory.IndexOf ("TestResults")). in questo modo potrei tagliare la cartella di output indesiderata poiché il mio file di configurazione si trovava nella stessa cartella delle mie DLL di test.
Ewan,

1

gli assembly non hanno il proprio file app.config. Usano il file app.config dell'applicazione che li sta usando. Quindi, se l'assembly si aspetta determinate cose nel file di configurazione, assicurati solo che il file di configurazione dell'applicazione contenga quelle voci.

Se l'assembly viene utilizzato da più applicazioni, ognuna di queste applicazioni dovrà avere tali voci nel proprio file app.config.

Quello che ti consiglierei di fare è definire proprietà sulle classi nel tuo assieme per quei valori, ad esempio

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

Qui, la proprietà ExternalServicesUrl ottiene il suo valore dal file di configurazione dell'applicazione. Se in un'applicazione che utilizza questo assembly manca quell'impostazione nel file di configurazione, si otterrà un'eccezione o è evidente che qualcosa è scomparso.

MissingConfigFileAppSettings è un'eccezione personalizzata. Potresti voler lanciare un'eccezione diversa.

Ovviamente una progettazione migliore sarebbe quella di fornire al metodo di quelle classi quei valori come parametri piuttosto che basarsi sull'impostazione del file di configurazione. In questo modo le applicazioni che usano queste classi possono decidere da dove e come fornire questi valori.


Attenzione a quanto sopra: quando si eseguono i test xUnit sulla DLL dell'assembly .NET, xUnit leggerà il file .config della libreria, in fase di esecuzione. E ignorerà qualsiasi App.config aggiunto al progetto test o DLL.
Zeek2,

1

Usa aggiungi elemento esistente, seleziona l'app config dal progetto dll. Prima di fare clic su Aggiungi, utilizzare la piccola freccia rivolta verso il basso sul lato destro del pulsante Aggiungi per "Aggiungi come collegamento"

Lo faccio sempre nel mio sviluppatore.


1

Preambolo : sto usando NET 2.0;

La soluzione pubblicata da Yiannis Leoussis è accettabile ma ho avuto qualche problema.

Innanzitutto, static AppSettingsSection AppSettings = (AppSettingsSection)myDllConfig.GetSection("appSettings");restituisce null. Ho dovuto cambiarlo instatic AppSettingSection = myDllConfig.AppSettings;

Quindi il return (T)Convert.ChangeType(AppSettings.Settings[name].Value, typeof(T), nfi);non ha un problema per le eccezioni. Quindi l'ho cambiato

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

Funziona molto bene ma se hai una dll diversa devi riscrivere ogni volta il codice per ogni assembly. Quindi, questa è la mia versione per un'istanza di una classe ogni volta che ne hai bisogno.

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

Per una configurazione:

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

Usalo come:

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

Si prega di rivedere il voto per la cancellazione. Il mio errore è stato inviare la risposta mentre la scrivevo.
Matteo Gaggiano,

0

Per quanto ne so, devi copiare + incollare le sezioni desiderate dalla libreria .config nel file .config delle applicazioni. Ottieni solo 1 app.config per istanza eseguibile.


se stai usando sezioni di configurazione personalizzate, puoi usare l'attributo configSource: <MySection configSource = "mysection.config" /> e il file di configurazione copia solo con dll
Jan Remunda

Ho aggiunto nuove domande come ad esempio sulla funzione che restituisce sempre una stringa vuota e le impostazioni del server di posta> stackoverflow.com/questions/25123544/… e> stackoverflow.com/questions/25138788/…, quindi spero che qualcuno risponda a loro mentre Sono quasi al limite della semplice codifica dei valori nella DLL!
MonkeyMagix,

0

Perché non usare:

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

Devi solo aggiornare visivamente quelle proprietà in fase di progettazione tramite:

[Solution Project]->Properties->Settings


Ciò creerà automaticamente un file di configurazione per la dll. Ma non puoi leggere i valori modificati dal file di configurazione in fase di esecuzione. Infine mostrerà i valori della tua applicazione chiamante. Vedi anche la risposta di @Joe
Codice Papa

No se è configurato per la configurazione dell'utente. L'idea è di modificare ciò di cui l'utente ha bisogno, configurarlo in fase di esecuzione e quindi salvarlo. Quindi, quando l'utente lavora con la libreria, carica la sua configurazione, salvata nel suo rispettivo percorso utente, ma funziona solo per lui.
Pedro Mora,

0

l'uso da configurazioni deve essere molto molto semplice come questo:

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

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

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

per maggiori dettagli consultare MiniConfig

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.