Configuración de .NET (app.config / web.config / settings.settings)

Tengo una aplicación .NET que tiene diferentes archivos de configuración para comstackciones de Debug y Release. Por ejemplo, el archivo debug app.config apunta a un SQL Server de desarrollo que tiene habilitada la depuración y el objective de lanzamiento apunta al servidor SQL en vivo. También hay otras configuraciones, algunas de las cuales son diferentes en depuración / versión.

Actualmente utilizo dos archivos de configuración separados (debug.app.config y release.app.config). Tengo un evento de comstackción en el proyecto que dice que si se trata de una versión de lanzamiento, copie release.app.config en app.config, o copie debug.app.config en app.config.

El problema es que la aplicación parece obtener su configuración del archivo settings.settings, así que tengo que abrir settings.settings en Visual Studio, lo que me indica que la configuración ha cambiado, así que acepto los cambios, guardo settings.settings y tengo para reconstruir para que use la configuración correcta.

¿Hay un método mejor / recomendado / preferido para lograr un efecto similar? O igualmente, ¿he abordado esto completamente mal y hay un mejor enfoque?

Cualquier configuración que pueda diferir entre entornos debería almacenarse a nivel de máquina , no a nivel de aplicación . (Más información sobre niveles de configuración)

Estos son los tipos de elementos de configuración que normalmente almaceno a nivel de máquina:

  • Configuraciones de la aplicación
  • Cadenas de conexión
  • retail = true
  • Configuración Smtp
  • Vigilancia de la salud
  • Ambiente de alojamiento
  • Llave de la máquina

Cuando cada entorno (desarrollador, integración, prueba, escenario, vivo) tiene su propia configuración única en el directorio c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , puede promocionar su código de aplicación entre entornos sin ningún modificaciones posteriores a la construcción.

Y, obviamente, el contenido del directorio CONFIG a nivel de máquina obtiene control de versión en un repository diferente o una estructura de carpeta diferente de la aplicación. Puede hacer que sus archivos .config sean más amigables con el control de fuente a través del uso inteligente de configSource .

He estado haciendo esto durante 7 años, en más de 200 aplicaciones ASP.NET en más de 25 compañías diferentes. (No estoy tratando de alardear, solo quiero hacerle saber que nunca he visto una situación en la que este enfoque no funcione).

Esto podría ayudar a algunas personas que trabajan con Settings.settings y App.config: Ten cuidado con el atributo GenerateDefaultValueInCode en el panel Propiedades mientras editas cualquiera de los valores en la grilla Settings.settings en Visual Studio (Visual Studio 2008 en mi caso).

Si establece GenerateDefaultValueInCode en True (True es el valor predeterminado aquí), el valor predeterminado se comstack en el EXE (o DLL), puede encontrarlo incrustado en el archivo cuando lo abra en un editor de texto sin formato.

Estaba trabajando en una aplicación de consola y si tenía los valores predeterminados en el EXE, ¡la aplicación siempre ignoraba el lugar del archivo de configuración en el mismo directorio! Toda una pesadilla y no hay información sobre esto en todo Internet.

Hay una pregunta relacionada aquí:

Mejorando tu proceso de comstackción

Los archivos de configuración vienen con una forma de anular la configuración:

 

En lugar de registrar dos archivos (o más), solo verifica el archivo de configuración predeterminado, y luego en cada máquina de destino, coloca un Local.config, con solo la sección appSettings que tiene las anulaciones para esa máquina en particular.

Si está utilizando secciones de configuración, el equivalente es:

 configSource="Local.config" 

Por supuesto, es una buena idea hacer copias de seguridad de todos los archivos Local.config de otras máquinas y registrarlos en algún lugar, pero no como parte de las soluciones reales. Cada desarrollador pone un “ignorar” en el archivo Local.config para que no se compruebe, lo que sobrescribiría el archivo de los demás.

(En realidad no tienes que llamarlo “Local.config”, eso es exactamente lo que uso)

Según lo que estoy leyendo, parece que estás usando Visual Studio para tu proceso de comstackción. ¿Has pensado en usar MSBuild y Nant en su lugar?

La syntax xml de Nant es un poco rara, pero una vez que la entiendes, hacer lo que mencionas se vuelve bastante trivial.

             

Para mí, parece que puede beneficiarse de los proyectos de implementación web de Visual Studio 2005 .

Con eso, puedes decirle que actualice / modifique secciones de tu archivo web.config dependiendo de la configuración de comstackción.

Echa un vistazo a esta entrada de blog de Scott Gu para una descripción / muestra rápida.

Solíamos usar proyectos de implementación web, pero desde entonces migramos a NAnt. En lugar de ramificar y copiar diferentes archivos de configuración, actualmente incorporamos los valores de configuración directamente en el script de construcción e inyectándolos en nuestros archivos de configuración a través de las tareas de xmlpoke:

   

En cualquier caso, sus archivos de configuración pueden tener los valores de desarrollador que desee y funcionarán bien desde su entorno de desarrollo sin romper sus sistemas de producción. Hemos descubierto que es menos probable que los desarrolladores cambien arbitrariamente las variables de script de comstackción cuando prueban cosas, por lo que las configuraciones erróneas accidentales han sido más raras que con otras técnicas que hemos probado, aunque es necesario agregar cada var en el proceso para que el valor dev no se empuja a prod por defecto.

Mi empleador actual resolvió este problema colocando primero el nivel de desarrollo (depuración, escenario, directo, etc.) en el archivo machine.config. Luego escribieron el código para recogerlo y usar el archivo de configuración correcto. Eso resolvió el problema con la cadena de conexión incorrecta después de que se implementa la aplicación.

Recientemente escribieron un servicio web central que devuelve la cadena de conexión correcta del valor en el valor machine.config.

¿Es esta la mejor solución? Probablemente no, pero funciona para ellos.

Una de las soluciones que me funcionó fue usar un WebDeploymentProject. Tenía 2/3 diferentes archivos web.config en mi sitio, y en publicar, dependiendo del modo de configuración seleccionado (versión / staging / etc …) Copiaba sobre Web.Release.config y lo cambiaba a web. config en el evento AfterBuild, y elimine los que no necesito (Web.Staging.config por ejemplo).

          

Aquí encontrará otra solución: ¿la mejor forma de cambiar la configuración entre entornos de Desarrollo / UAT / Prod en ASP.NET? que usa XSLT para transformar web.config.

También hay algunos buenos ejemplos sobre el uso de NAnt.

Nuestro proyecto tiene el mismo problema en el que tuvimos que mantener las configuraciones para dev, qa, uat y prod. Esto es lo que seguimos (solo se aplica si está familiarizado con MSBuild):

Use MSBuild con la extensión de tareas de comunidad de MSBuild. Incluye la tarea ‘XmlMassUpdate’ que puede ‘actualizar en masa’ las entradas en cualquier archivo XML una vez que le das el nodo correcto para empezar.

Para implementar:

1) Necesita tener un archivo de configuración que tendrá sus entradas dev env; este es el archivo de configuración en su solución.

2) Necesita tener un archivo ‘Substitutions.xml’, que contiene solo las entradas que son DIFERENTES (appSettings y ConnectionStrings principalmente) para cada entorno. Las entradas que no cambian en el entorno no necesitan colocarse en este archivo. Pueden vivir en el archivo web.config de la solución y no serán tocados por la tarea

3) En su archivo de comstackción, simplemente llame a la tarea de actualización masiva de XML y proporcione el entorno correcto como parámetro.

Vea el siguiente ejemplo:

                          

reemplace ‘$ Environment’ con ‘QA’ o ‘Prod’ en función de qué env. estás construyendo para. Tenga en cuenta que debe trabajar en una copia de un archivo de configuración y no en el archivo de configuración real para evitar posibles errores no recuperables.

Simplemente ejecute el archivo de comstackción y luego mueva el archivo de configuración actualizado a su entorno de despliegue y ¡listo!

Para una mejor visión general, lea esto:

http://blogs.microsoft.co.il/blogs/dorony/archive/2008/01/18/easy-configuration-deployment-with-msbuild-and-the-xmlmassupdate-task.aspx

Al igual que usted, también configuré ‘multi’ app.config, por ejemplo, app.configDEV, app.configTEST, app.config.LOCAL. Veo algo de la excelente alternativa sugerida, pero si te gusta la forma en que funciona para ti, agregaría lo siguiente:

Tengo una

para cada aplicación, agrego esto a la UI en la barra de título: desde ConfigurationManager.AppSettings.Get (“Env”);

Solo cambio el nombre de la configuración a la que apuntaré (tengo un proyecto con 8 aplicaciones con muchas bases de datos / configuración wcf frente a 4 momentos). Para implementar con clickonce en cada uno, cambio 4 seetings en el proyecto y listo. (esto me encantaría automatizar)

Mi único problema es recordar “limpiar todo” después de un cambio, ya que la configuración anterior está “bloqueada” después de un cambio de nombre manual. (Lo cual creo que le solucionará el problema de configuración).

Encuentro que esto funciona muy bien (algún día tendré tiempo para mirar MSBuild / NAnt)

Dice asp.net arriba, entonces ¿por qué no guardar su configuración en la base de datos y usar un caché personalizado para recuperarlos?

La razón por la que lo hicimos aquí es porque es más fácil (para nosotros) actualizar la base de datos continuamente que obtener permiso para actualizar continuamente los archivos de producción.

EJEMPLO de un caché personalizado:

 public enum ConfigurationSection { AppSettings } public static class Utility { #region "Common.Configuration.Configurations" private static Cache cache = System.Web.HttpRuntime.Cache; public static String GetAppSetting(String key) { return GetConfigurationValue(ConfigurationSection.AppSettings, key); } public static String GetConfigurationValue(ConfigurationSection section, String key) { Configurations config = null; if (!cache.TryGetItemFromCache(out config)) { config = new Configurations(); config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings); cache.AddToCache(config, DateTime.Now.AddMinutes(15)); } var result = (from record in config where record.Key == key select record).FirstOrDefault(); return (result == null) ? null : result.Value; } #endregion } namespace Common.Configuration { public class Configurations : List { #region CONSTRUCTORS public Configurations() : base() { initialize(); } public Configurations(int capacity) : base(capacity) { initialize(); } public Configurations(IEnumerable collection) : base(collection) { initialize(); } #endregion #region PROPERTIES & FIELDS private Crud _crud; // Db-Access layer #endregion #region EVENTS #endregion #region METHODS private void initialize() { _crud = new Crud(Utility.ConnectionName); } ///  /// Lists one-to-many records. ///  public Configurations List(ConfigurationSection section) { using (DbCommand dbCommand = _crud.Db.GetStoredProcCommand("spa_LIST_MyConfiguration")) { _crud.Db.AddInParameter(dbCommand, "@Section", DbType.String, section.ToString()); _crud.List(dbCommand, PopulateFrom); } return this; } public void PopulateFrom(DataTable table) { this.Clear(); foreach (DataRow row in table.Rows) { Configuration instance = new Configuration(); instance.PopulateFrom(row); this.Add(instance); } } #endregion } public class Configuration { #region CONSTRUCTORS public Configuration() { initialize(); } #endregion #region PROPERTIES & FIELDS private Crud _crud; public string Section { get; set; } public string Key { get; set; } public string Value { get; set; } #endregion #region EVENTS #endregion #region METHODS private void initialize() { _crud = new Crud(Utility.ConnectionName); Clear(); } public void Clear() { this.Section = ""; this.Key = ""; this.Value = ""; } public void PopulateFrom(DataRow row) { Clear(); this.Section = row["Section"].ToString(); this.Key = row["Key"].ToString(); this.Value = row["Value"].ToString(); } #endregion } }