Archivo de configuración C # DLL

191

Estoy tratando de agregar un archivo app.config a mi DLL, pero todos los intentos han fallado.

De acuerdo con MusicGenesis en ' Poner información de configuración en un archivo DLL ' esto no debería ser un problema. Así que obviamente estoy haciendo algo mal ...

El siguiente código debería devolver mi ConnectionString desde mi DLL:

return ConfigurationManager.AppSettings["ConnectionString"];

Sin embargo, cuando copio el archivo app.config a mi aplicación de consola, funciona bien.

¿Algunas ideas?

Megabyte
fuente
De acuerdo con la publicación mencionada: si el nombre del dll era MyDll.dll, entonces el archivo de configuración debería ser MyDLL.dll.config. Entonces, si lee la configuración desde el archivo dll, debería referirse a su propia configuración ¿verdad?
MegaByte
11
No importa qué código solicite: está buscando el archivo especificado para AppDomain: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile configuración
Marc Gravell
Una nota: la pregunta "poner información de configuración en un archivo DLL" se trata de separar el código de configuración de su aplicación en una biblioteca para mantenerlo separado del código principal de la aplicación. Esto es muy diferente de un archivo de configuración separado y especial para una DLL por sí solo.
Chris Ammerman
vea esta publicación [ingrese la descripción del enlace aquí] [1], fue la solución para mí [1]: stackoverflow.com/questions/2389290/…
dhailis
vea esta publicación [¿Cómo cargar un archivo de configuración de la aplicación por separado dinámicamente y combinar con la configuración actual?] [1] podría ser de ayuda [1]: stackoverflow.com/questions/2389290/…
dhailis

Respuestas:

277

No es trivial crear un archivo de configuración .NET para un .DLL, y por una buena razón. El mecanismo de configuración .NET tiene muchas características incorporadas para facilitar la actualización / actualización fácil de la aplicación y para proteger las aplicaciones instaladas de pisotear los archivos de configuración de los demás.

Hay una gran diferencia entre cómo se usa una DLL y cómo se usa una aplicación. Es poco probable que tenga varias copias de una aplicación instalada en la misma máquina para el mismo usuario. Pero es muy posible que tenga 100 aplicaciones o bibliotecas diferentes, todas haciendo uso de alguna DLL de .NET.

Si bien rara vez es necesario rastrear la configuración por separado para diferentes copias de una aplicación dentro de un perfil de usuario, es muy poco probable que desee que todos los diferentes usos de una DLL compartan la configuración entre sí. Por esta razón, cuando recupera un objeto de Configuración utilizando el método "normal", el objeto que recupera está vinculado a la configuración del Dominio de la Aplicación en el que está ejecutando, en lugar del ensamblaje particular.

El dominio de la aplicación está vinculado al ensamblado raíz que cargó el ensamblado en el que se encuentra realmente su código. En la mayoría de los casos, este será el ensamblaje de su .EXE principal, que es lo que cargó el .DLL. Es posible activar otros dominios de aplicación dentro de una aplicación, pero debe proporcionar explícitamente información sobre cuál es el ensamblado raíz de ese dominio de aplicación.

Debido a todo esto, el procedimiento para crear un archivo de configuración específico de la biblioteca no es tan conveniente. Es el mismo proceso que usaría para crear un archivo de configuración portátil arbitrario que no esté vinculado a ningún ensamblado en particular, pero para el que desea utilizar el esquema XML de .NET, la sección de configuración y los mecanismos de los elementos de configuración, etc. Esto implica crear un ExeConfigurationFileMapobjeto , cargando los datos para identificar dónde se almacenará el archivo de configuración y luego llamando ConfigurationManager. OpenMappedExeConfigurationpara abrirlo en una nueva Configurationinstancia. Esto lo cortará de la protección de versión que ofrece el mecanismo automático de generación de rutas.

Estadísticamente hablando, es probable que esté utilizando esta biblioteca en un entorno interno, y es poco probable que tenga múltiples aplicaciones que la utilicen en una sola máquina / usuario. Pero si no, hay algo que debe tener en cuenta. Si utiliza un único archivo de configuración global para su DLL, independientemente de la aplicación que lo esté haciendo referencia, debe preocuparse por los conflictos de acceso. Si dos aplicaciones que hacen referencia a su biblioteca se ejecutan al mismo tiempo, cada una con su propio Configurationobjeto abierto, cuando una guarde los cambios, causará una excepción la próxima vez que intente recuperar o guardar datos en la otra aplicación.

La forma más segura y sencilla de evitar esto es exigir que el ensamblado que está cargando su DLL también proporcione cierta información sobre sí mismo, o detectarlo examinando el dominio de aplicación del ensamblaje de referencia. Use esto para crear algún tipo de estructura de carpetas para mantener archivos de configuración de usuario separados para cada aplicación que haga referencia a su DLL.

Si está seguro de que desea tener una configuración global para su DLL, sin importar a dónde se haga referencia, deberá determinar su ubicación en lugar de que .NET descubra una adecuada de forma automática. También deberá ser agresivo sobre la administración del acceso al archivo. Necesitará almacenar en caché tanto como sea posible, manteniendo la Configurationinstancia SOLO todo el tiempo necesario para cargar o guardar, abrir inmediatamente antes y desechar inmediatamente después. Y finalmente, necesitará un mecanismo de bloqueo para proteger el archivo mientras está siendo editado por una de las aplicaciones que usan la biblioteca.

Chris Ammerman
fuente
Creo que el mecanismo de sincronización debería ser un "evento con nombre", etc., porque es un proceso cruzado
Jacob
8
: / Meh. La nuestra es una aplicación empresarial monstruosa con el .exe principal escrito por chicos en una zona horaria diferente y los módulos representados por varias DLL y vinculados dinámicamente a través de un marco de plugin personalizado. Toda esta pomposidad "necesitará asegurarse de que varias aplicaciones puedan usar su DLL simultáneamente" es exactamente incorrecta.
JohnL4
Además, en gran parte de mi carrera, he visto estos encantadores mecanismos genéricos de objetos compartidos completamente ignorados, con equipos que crean DLL (o JAR) que solo se pueden usar en un contexto (y deben estar presentes, o la aplicación falla ) También podrían estar estáticamente vinculados, pero eso es una pasada.
JohnL4
1
"Estadísticamente hablando, es probable que estés usando esta biblioteca en un entorno interno, y es poco probable que tengas múltiples aplicaciones haciendo uso de ella en cualquier máquina / usuario". La diferencia entre teoría y práctica me pone de mal humor a veces.
JohnL4
1
@Panzercrisis, la función Settings.settings de Visual Studio crea automáticamente rutas específicas de la versión para todas las configuraciones del usuario. Ver: stackoverflow.com/questions/35778528/…
Deantwo
101

si desea leer la configuración del archivo de configuración de la DLL pero no de las aplicaciones raíz web.config o app.config, use el código siguiente para leer la configuración en el archivo dll.

var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
string dllConfigData = appConfig.AppSettings.Settings["dllConfigData"].Value;
Morbia
fuente
En C ++ administrado para VS 2008 System :: Configuration :: Configuration ^ appConfig = ConfigurationManager :: OpenExeConfiguration (Assembly :: GetExecutingAssembly () -> Location); Cadena ^ name = appConfig-> AppSettings-> Settings ["name"] -> Value;
Hans
Gracias, esto realmente resolvió mi problema. He estado lidiando con este problema durante aproximadamente dos días, y no lo hice funcionar hasta ahora. Como depuración de una prueba, ConfigurationManager estaba leyendo desde la máquina.config -Creo-, ya que las cadenas de conexión extraídas eran sobre SQLExpress -cadena de conexión que no había enumerado-.
yopez83
19

Tuve el mismo problema y busqué en la web durante varias horas, pero no pude encontrar ninguna solución, así que hice la mía. Me preguntaba por qué el sistema de configuración .net es tan inflexible.

Antecedentes: quiero que mi DAL.dll tenga su propio archivo de configuración para la base de datos y la configuración de DAL. También necesito la app.config para Enterprise Library y sus propias configuraciones. Entonces necesito tanto la app.config como la dll.config.

¡Lo que no quería hacer es transferir todas las propiedades / configuraciones de la aplicación a mi capa DAL!

doblar el "AppDomain.CurrentDomain.SetupInformation.ConfigurationFile" no es posible porque lo necesito para el comportamiento normal de app.config.

Mis requisitos / puntos de vista fueron:

  • NO hay copia manual de nada desde ClassLibrary1.dll.config a WindowsFormsApplication1.exe.config porque esto es irreproducible para otros desarrolladores.
  • retener el uso de la escritura fuerte "Properties.Settings.Default.NameOfValue" (Comportamiento de configuración) porque creo que esta es una característica importante y no quería perderla
  • Descubrí la falta de ApplicationSettingsBase para inyectar su propio archivo de configuración o gestión personalizada (todos los campos necesarios son privados en estas clases)
  • el uso de la redirección de archivos "configSource" no es posible porque tendríamos que copiar / reescribir ClassLibrary1.dll.config y proporcionar varios archivos XML para varias secciones (tampoco me gustó)
  • No me gustaba escribir mi propio SettingsProvider para esta tarea simple como sugiere MSDN porque pensé que simplemente sería demasiado
  • Solo necesito secciones applicationSettings y connectionStrings del archivo de configuración

Se me ocurrió modificar el archivo Settings.cs e implementé un método que abre ClassLibrary1.dll.config y lee la información de la sección en un campo privado. Después de eso, he anulado "this [string propertyName]" para que el Settings.Desginer.cs generado llame a mi nueva Propiedad en lugar de la clase base. Allí, la configuración se lee de la Lista.

Finalmente está el siguiente código:

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

Solo tendrá que copiar su ClassLibrary1.dll.config desde el directorio de salida de ClassLibrary1 al directorio de salida de su aplicación. Quizás alguien lo encuentre útil.

Sven
fuente
14

Cuando uso ConfigurationManager, estoy bastante seguro de que está cargando el proceso /AppDomain archivo de configuración (app.config / web.config). Si desea cargar un archivo de configuración específico, tendrá que solicitar específicamente ese archivo por su nombre ...

Tu podrías intentar:

var config = ConfigurationManager.OpenExeConfiguration("foo.dll");
config.ConnectionStrings. [etc]
Marc Gravell
fuente
De acuerdo con la publicación mencionada: si el nombre del dll era MyDll.dll, entonces el archivo de configuración debería ser MyDLL.dll.config. Entonces, si lee la configuración desde el archivo dll, debería referirse a su propia configuración ¿verdad?
MegaByte
1
No ... no lo creo. "from with the dll" no tiene posibilidades; de forma predeterminada, está mirando el archivo de configuración definido para el dominio de aplicación: my.exe.config
Marc Gravell
1
En particular, la configuración AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.
Marc Gravell
nota: probé la configuración OpenExeConfiguration, y tampoco estoy seguro de que funcione. ¿Quizás solo combine la configuración con app.config?
Marc Gravell
Se puede hacer ... pero no con el mismo tipo de soporte y seguridad que el archivo app.config para un EXE. Mira mi respuesta.
Chris Ammerman
13

ConfigurationManager.AppSettings devuelve la configuración definida para la aplicación, no para la DLL específica, puede acceder a ellas, pero se devolverá la configuración de la aplicación.

Si está utilizando su dll desde otra aplicación, entonces ConnectionString estará en la configuración de la aplicación.

Jorge Córdoba
fuente
6

Sé que esto es tarde para la fiesta, sin embargo, pensé que compartiría la solución que uso para las DLL.

Soy más de la escuela de pensamiento KISS, así que cuando tengo una DLL .NET que quiere almacenar puntos de datos externos que controlan cómo funciona o hacia dónde va, etc. Simplemente creo una clase "config" que solo tiene propiedades públicas que almacenan todos los puntos de datos que necesita y que me gustaría poder controlar externamente a la DLL para evitar que se vuelvan a compilar para realizar los cambios. Luego uso la serialización XML de .Net para guardar y cargar la representación de objetos de la clase en un archivo.

Hay muchas maneras de manejar leerlo y acceder a él, desde Singleton, una clase de utilidad estática, hasta métodos de extensión, etc. Esto depende de cómo esté estructurada su DLL y qué método se ajuste mejor a su DLL.

Rodney S. Foley
fuente
También uso este enfoque y estoy contento con la forma en que ha funcionado hasta ahora.
Dave
4

estás en lo correcto, puedes leer el archivo de configuración de un dll. Luché con esto por un día hasta que descubrí que el problema era mi archivo de configuración. Ver mi código a continuación. Fue capaz de correr.

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

mi Plugin1.dll.configmiró como abajo;

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

Descubrí que mi archivo de configuración no tenía la <appSettings>etiqueta, así que mire a su alrededor, su problema podría haber sido diferente, pero no tan lejos del mío.

Mugume David
fuente
3

Dado que el ensamblaje reside en un caché temporal, debe combinar la ruta para obtener la configuración del dll:

var appConfig = ConfigurationManager.OpenExeConfiguration(
    Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));
Lin Song Yang
fuente
en lugar de "Path.Combine (Environment.CurrentDirectory, Assembly.GetExecutingAssembly (). ManifestModule.Name)" puede usar "Assembly.GetExecutingAssembly (). Location"
Cadburry
3

Si está utilizando bibliotecas que buscan una gran cantidad de confusión detrás de escena, como WCF, podría considerar hacer esto:

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

O en PowerShell:

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

En mi opinión, esta técnica es un olor a código y en realidad solo es adecuada para su uso en secuencias de comandos ad hoc. Si desea hacer esto en el código de producción, tal vez sea hora de una revisión arquitectónica.

NO se recomienda lo siguiente:
Como curiosidad técnica, aquí hay una variación sobre el tema. Puede crear un constructor estático dentro de una de las clases alojadas en la DLL y realizar esta llamada desde allí. No recomendaría hacer esto, excepto como último recurso.

Paul Williams
fuente
3

La solución completa no se encuentra a menudo en un solo lugar ...

1) Cree un archivo de configuración de la aplicación y asígnele el nombre "yourDllName.dll.config"
2) Haga clic derecho en el archivo de configuración creado anteriormente en VS Solution Explorer, haga clic en propiedades
--- set "Build Action" = Content
--- set "Copy To Output Directory" = Siempre
3) Agregue una sección de AppSettings al archivo de configuración (yourDllName.dll.config) con su yourKeyName y yourKeyValue

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

4) Agregue System.Configuration a sus referencias dll / class / project
5) Agregue las instrucciones de uso a su código donde tiene la intención de acceder a la configuración

using System.Configuration;
using System.Reflection;

6) Para acceder al valor

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

7) regocíjate, funciona

En mi humilde opinión, esto solo debe usarse cuando se desarrolla un nuevo dll / library.

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

El archivo de configuración termina siendo una gran referencia, para cuando agrega los ajustes de la aplicación dll a su aplicación real.

David C Fuchs
fuente
3

Parece que estos archivos de configuración son realmente confusos de aclarar a medida que su comportamiento cambia del entorno de desarrollo a la implementación. Aparentemente, un archivo DLL puede tener su propio archivo de configuración, pero una vez que copie y pegue el archivo DLL (junto con su archivo de configuración) en otro lugar, todo dejará de funcionar. La única solución es fusionar manualmente los archivos app.config en un solo archivo, que solo será utilizado por el ejecutivo. Por ejemplo, myapp.exe tendrá un archivo myapp.exe.config que contiene todas las configuraciones para todos los archivos dlls utilizados por myapp.exe. Estoy usando VS 2008.

kenny
fuente
2

He encontrado lo que parece una buena solución para este problema. Estoy usando VS 2008 C #. Mi solución implica el uso de espacios de nombres distintos entre múltiples archivos de configuración. He publicado la solución en mi blog: http://tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html .

Por ejemplo:

Este espacio de nombres lee / escribe la configuración de dll:

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

Este espacio de nombres lee / escribe la configuración de exe:

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

Hay algunas advertencias mencionadas en el artículo. HTH

Tommie C.
fuente
1

Como dice Marc, esto no es posible (aunque Visual Studio le permite agregar un archivo de configuración de la aplicación en un proyecto de biblioteca de clases).

Es posible que desee consultar la clase AssemblySettings que parece hacer posible los archivos de configuración de ensamblaje.

Gerrie Schenck
fuente
0

Para un dll, no debe depender de la configuración, ya que la configuración es propiedad de la aplicación y no de dll.

Esto se explica aquí

Saravanan
fuente
0

puedes usar este código:

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
fuente