Equivalente a 'app.config' para una biblioteca (DLL)

149

¿Hay un equivalente para app.configpara bibliotecas (DLL)? Si no, ¿cuál es la forma más fácil de almacenar configuraciones de configuración que son específicas de una biblioteca? Tenga en cuenta que la biblioteca puede usarse en diferentes aplicaciones.

Louis Rhys
fuente

Respuestas:

161

Usted puede tener archivo de configuración independiente, pero tendrá que leerlo "manual", elConfigurationManager.AppSettings["key"] leerá únicamente la configuración del conjunto de marcha.

Suponiendo que está utilizando Visual Studio como su IDE, puede hacer clic con el botón derecho en el proyecto deseado → Agregar → Nuevo elemento → Archivo de configuración de la aplicación

Esto se agregará App.configa la carpeta del proyecto, ponga su configuración allí en la <appSettings>sección. En caso de que no esté utilizando Visual Studio y agregue el archivo manualmente, asegúrese de asignarle el nombre DllName.dll.config ; de lo contrario, el código siguiente no funcionará correctamente.

Ahora para leer de este archivo tenemos dicha función:

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

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

También deberá agregar una referencia al espacio de nombres System.Configuration para tener disponible la clase ConfigurationManager.

Al construir el proyecto, además de la DLL tendrás DllName.dll.config archivo, ese es el archivo que debe publicar con la propia DLL.

Lo anterior es un código de muestra básico, para aquellos interesados ​​en un ejemplo a escala completa, consulte esta otra respuesta .

Shadow Wizard es Ear for You
fuente
1
@Rodney intenta cambiar string exeConfigPath = this.GetType().Assembly.Location;a algo como:string exeConfigPath = @"C:\MyFolder\DllFolder\ExeName.exe";
Shadow Wizard is Ear For You
1
¿Alguna idea de cómo hacer esto si la herramienta de prueba de la unidad de intercambio está copiando el dll en una carpeta desconocida?
Autodidacta
11
Un consejo para cualquier otra persona que implemente esto: para automatizar la generación de DllName.dll.config haciendo referencia a las aplicaciones, simplemente cambié el nombre de app.config a DllName.dll.config y cambié la propiedad "Copiar al directorio de salida" a "Copiar siempre" . Además, mi necesidad era de cadenas de conexión, que se pueden recuperar usando config.ConnectionStrings.ConnectionStrings [connStringName] .ConnectionString.
Jeff G
2
el nombre del archivo app.cfg es muy importante para leer los valores appcfg, el nombre del archivo debe ser "DLL_NAME.DLL.CONFIG"
SaddamBinSyed
2
Corrección a mi último comentario. En mi solución VS2017, al eliminar mis nuevos archivos App.config que no funcionan de mis proyectos de prueba y DLL y simplemente volver a agregarlos a mi proyecto de prueba, ¡de repente comienza a funcionar! Mi configuración de App.config ahora se incluye automáticamente en DLL.configs. ¡Qué alivio!
Zeek2
30

Desafortunadamente, solo puede tener un archivo app.config por ejecutable, por lo que si tiene archivos DLL vinculados a su aplicación, no pueden tener sus propios archivos app.config.

La solución es: no necesita colocar el archivo App.config en el proyecto de la Biblioteca de clases.
Pones el archivo App.config en la aplicación que hace referencia a la dll de tu biblioteca de clases.

Por ejemplo, supongamos que tenemos una biblioteca de clases llamada MyClasses.dll que usa el archivo app.config de la siguiente manera:

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

Ahora, supongamos que tenemos una aplicación de Windows llamada MyApp.exe que hace referencia a MyClasses.dll. Contendría un App.config con una entrada como:

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

O

Un archivo xml es el mejor equivalente para app.config. Utilice xml serialize / deserialize según sea necesario. Puedes llamarlo como quieras. Si su configuración es "estática" y no necesita cambiar, también podría agregarla al proyecto como un recurso incrustado.

Espero que te dé alguna idea

PawanS
fuente
66
ConfigurationSettingsahora está obsoleto y reemplazado por ConfigurationManager, por lo que el equivalente ahora seríaConfigurationManager.AppSettings
Gone Coding
2
voto negativo la pregunta es por dll y no por aplicación. mejor solución: stackoverflow.com/a/5191101/2935383
raiserle
3
Sospecho que esta sugerencia no funcionará en el caso de dlls vinculados tarde que no tendrían conocimiento del ejecutable que los llama.
beanmf
9

Los archivos de configuración tienen ámbito de aplicación y no ámbito de ensamblado. Por lo tanto, deberá colocar las secciones de configuración de su biblioteca en el archivo de configuración de cada aplicación que esté utilizando su biblioteca.

Dicho esto, no es una buena práctica obtener la configuración del archivo de configuración de la aplicación, especialmente la appSettingssección, en una biblioteca de clases. Si su biblioteca necesita parámetros, cualquiera que llame a su biblioteca probablemente debería pasarlos como argumentos de método en constructores, métodos de fábrica, etc. Esto evita que las aplicaciones que llaman reutilicen accidentalmente las entradas de configuración que la biblioteca de clases esperaba.

Dicho esto, los archivos de configuración XML son extremadamente útiles, por lo que el mejor compromiso que he encontrado es usar secciones de configuración personalizadas. Puede poner la configuración de su biblioteca en un archivo XML que el marco lee y analiza automáticamente y evita posibles accidentes.

Puede obtener más información sobre las secciones de configuración personalizada en MSDN y también Phil Haack tiene un buen artículo sobre ellas.

madd0
fuente
77
"No es una buena práctica obtener la configuración de un archivo de configuración en una biblioteca de clases". Estoy totalmente en desacuerdo con esto. Por ejemplo, una biblioteca de clases DAL normalmente debería obtener datos de configuración como cadenas de conexión del archivo de configuración de la aplicación en lugar de que esta información pase del nivel BLL. Cualquier clase de Framework que use configuración (por ejemplo, Membresía ASP.NET) funciona de esta manera.
Joe
Modifiqué mi respuesta ligeramente. Todavía mantengo lo que dije, pero tienes razón, nunca tuve la intención de implicar que los archivos de configuración no deberían usarse en absoluto. Lo que quise decir es que, en lugar de convenciones appSettings, las secciones personalizadas ofrecen una gran alternativa; es más o menos lo que usa la Membresía ASP.NET después de todo.
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 por algo que hacer, refactoricé la respuesta principal en una clase. El uso es algo como:

ConfigMan configMan = new ConfigMan(this.GetType().Assembly.Location);
var setting = configMan.GetAppSetting("AppSettingsKey");
Jardín de fuego
fuente
4

Si agrega Configuraciones a un proyecto de Biblioteca de clases en Visual Studio (Propiedades del proyecto, Configuraciones), agregará un archivo app.config a su proyecto con las secciones relevantes de UserSettings / applytioNSettings, y los valores predeterminados para estas configuraciones desde Settings.settings expediente.

Sin embargo, este archivo de configuración no se usará en tiempo de ejecución; en cambio, la biblioteca de clases usa el archivo de configuración de su aplicación de alojamiento.

Creo que la razón principal para generar este archivo es para que pueda copiar / pegar la configuración en el archivo de configuración de la aplicación host.

Joe
fuente
4

Actualmente estoy creando complementos para una marca de software minorista, que en realidad son bibliotecas de clases .net. Como requisito, cada complemento debe configurarse utilizando un archivo de configuración. Después de un poco de investigación y pruebas, compilé la siguiente clase. Hace el trabajo sin problemas. Tenga en cuenta que no he implementado el manejo local de excepciones en mi caso porque capturo excepciones en un nivel superior.

Tal vez sea necesario realizar algunos ajustes para obtener el punto decimal correcto, en caso de decimales y dobles, pero funciona bien para mi 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);
    }
}

App.Config muestra de archivo

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

Créditos a Shadow Wizard & MattC

Yiannis Leoussis
fuente
1
Esta debería ser la respuesta aceptada. Muy compacto y "funciona desde el primer momento". Cosas buenas
nmarler
2

En respuesta a la pregunta original, normalmente agrego el archivo de configuración en mi proyecto de prueba como un enlace; luego puede usar el atributo DeploymentItem para agregarlo a la carpeta Out de la ejecución de prueba.

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

En respuesta a los comentarios de que las Asambleas no pueden ser específicas del proyecto, sí pueden hacerlo y proporciona una gran flexibilidad especialmente. cuando se trabaja con marcos IOC.

Allan Elder
fuente
2

Enfrenté el mismo problema y lo resolví creando una clase estática Parametersdespués de agregar un Archivo de configuración de la aplicación al proyecto:

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

Luego obtenga un parámetro como este:

Parameters.GetParameter("keyName");
krlzlx
fuente
1
¡Brillante! Esto me ayudó a hacer que mis pruebas automatizadas del controlador de aplicaciones de Windows se ejecuten en máquinas de destino. Los dlls en mi caso eran de un proyecto de prueba. Lo único que agregaría es que en Win App Driver (y posiblemente otras formas de pruebas automatizadas), BaseDirectory es en realidad la carpeta de salida que cambia cada vez. Tuve una subcadena como esta ... AppDomain.CurrentDomain.BaseDirectory.Substring (0, AppDomain.CurrentDomain.BaseDirectory.IndexOf ("TestResults")). de esta manera podría cortar la carpeta de salida no deseada ya que mi archivo de configuración estaba en la misma carpeta que mis dlls de prueba.
Ewan
1

Los ensamblados no tienen su propio archivo app.config. Usan el archivo app.config de la aplicación que los está usando. Entonces, si su ensamblaje espera ciertas cosas en el archivo de configuración, solo asegúrese de que el archivo de configuración de su aplicación tenga esas entradas allí.

Si su ensamblaje está siendo utilizado por múltiples aplicaciones, entonces cada una de esas aplicaciones deberá tener esas entradas en su archivo app.config.

Lo que le recomendaría que haga es definir propiedades en las clases en su ensamblaje para esos valores, por ejemplo

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

Aquí, la propiedad ExternalServicesUrl obtiene su valor del archivo de configuración de la aplicación. Si alguna aplicación que usa este ensamblado no tiene esa configuración en el archivo de configuración, obtendrá una excepción o está claro que algo se perdió.

MissingConfigFileAppSettings es una excepción personalizada. Es posible que desee lanzar una excepción diferente.

Por supuesto, un mejor diseño sería que el método de esas clases proporcione esos valores como parámetros en lugar de depender de la configuración del archivo de configuración. De esa manera, las aplicaciones que usan estas clases pueden decidir desde dónde y cómo proporcionan estos valores.

Shiv Kumar
fuente
Advertencia a lo anterior: cuando ejecute pruebas de xUnit en su DLL de ensamblado .NET, xUnit leerá el archivo .config de la biblioteca en tiempo de ejecución. E ignorará cualquier App.config agregado a la prueba o proyecto DLL.
Zeek2
1

Use agregar elemento existente, seleccione la configuración de la aplicación del proyecto dll. Antes de hacer clic en Agregar, use la pequeña flecha hacia abajo en el lado derecho del botón Agregar para "agregar como enlace"

Hago esto todo el tiempo en mi desarrollo.

fantasmaJago
fuente
1

Preámbulo : estoy usando NET 2.0;

La solución publicada por Yiannis Leoussis es aceptable pero tuve algún problema con ella.

Primero, los static AppSettingsSection AppSettings = (AppSettingsSection)myDllConfig.GetSection("appSettings");retornos son nulos. Tuve que cambiarlo astatic AppSettingSection = myDllConfig.AppSettings;

Entonces el return (T)Convert.ChangeType(AppSettings.Settings[name].Value, typeof(T), nfi);no tiene una trampa para excepciones. Así que lo he cambiado

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

Esto funciona muy bien, pero si tiene un dll diferente, debe volver a escribir cada vez que el código para cada ensamblado. Entonces, esta es mi versión para una Clase para instanciar cada vez que lo necesite.

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

Para una configuración:

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

Úselo como:

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
fuente
Por favor, revise el voto para su eliminación. Mi error fue enviar la respuesta mientras la escribía.
Matteo Gaggiano
0

Hasta donde sé, debe copiar y pegar las secciones que desee de la biblioteca .config en el archivo .config de la aplicación. Solo obtiene 1 app.config por instancia ejecutable.

Miguel
fuente
si está utilizando secciones de configuración personalizadas, puede usar el atributo configSource: <MySection configSource = "mysection.config" /> y el archivo de configuración solo copia con dll
Jan Remunda
He agregado nuevas preguntas a medida que se hacen, por ejemplo, sobre la función que siempre devuelve una cadena vacía y la configuración del servidor de correo> stackoverflow.com/questions/25123544/… y> stackoverflow.com/questions/25138788/…, así que espero que alguien les responda mientras yo ¡Estoy casi al borde de codificar los valores en la DLL!
MonkeyMagix
0

Por qué no usar:

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

Solo tiene que actualizar visualmente esas propiedades en tiempo de diseño a través de:

[Solution Project]->Properties->Settings

Pedro Mora
fuente
Esto creará automáticamente un archivo de configuración para el dll. Pero no puede leer valores modificados del archivo de configuración en tiempo de ejecución. Finalmente, mostrará los valores de su aplicación de llamada. Ver también @Joe answer
Code Pope
No si está configurado para la configuración del usuario. La idea es editar lo que el usuario necesita, configurarlo en tiempo de ejecución y luego guardarlo. Luego, cuando el usuario trabaja con la biblioteca, carga su configuración, guardada en su ruta de usuario respectiva, pero solo funciona para él.
Pedro Mora
0

El uso de configuraciones debe ser muy, muy fácil como este:

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

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

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

para más detalles ver MiniConfig

Rahmat Anjirabi
fuente