Digamos que estoy creando una aplicación llamada ConsoleApp2 .
Debido a algunas bibliotecas de terceros que estoy usando, mi archivo app.config predeterminado genera código como
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Esto se debe a que mi solución hace referencia a diferentes versiones de una biblioteca, por lo que debe decirles a todos: " Oye, si buscas alguna versión antigua de esta biblioteca, solo usa la nueva versión ". Y eso está bien.
El problema es que quiero definir un archivo de configuración separado "test.exe.config" donde tengo algunas configuraciones y deshacerme del generado automáticamente.
Para informar a mi aplicación sobre el nuevo archivo de configuración, estoy usando un código como
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");
Y eso funciona (casi) perfectamente. Y escribí allí " casi " ya que aunque la <appSettings>
sección se está leyendo correctamente, la <runtime>
sección no se está mirando en mi archivo de configuración personalizado, pero la aplicación lo busca en el archivo de configuración predeterminado, lo cual es un problema ya que quiero poder borrar eso más tarde.
Entonces, ¿cómo puedo decirle a mi aplicación que lea también la <runtime>
información de mi archivo de configuración personalizado?
Cómo reproducir el problema
Una muestra simple para reproducir mi problema es la siguiente:
Cree una biblioteca llamada ClassLibrary2 ( .Net Framework v4.6 ) con una sola clase de la siguiente manera
using Newtonsoft.Json.Linq;
using System;
namespace ClassLibrary2
{
public class Class1
{
public Class1()
{
var json = new JObject();
json.Add("Succeed?", true);
Reash = json.ToString();
}
public String Reash { get; set; }
}
}
Tenga en cuenta la referencia al paquete Newtonsoft . El que está instalado en la biblioteca es v10.0.2 .
Ahora cree una aplicación de consola llamada ConsoleApp2 ( .Net Framework v4.6 ) con una clase llamada Programa cuyo contenido es simplemente el siguiente:
using System;
using System.Configuration;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");
var AppSettings = ConfigurationManager.AppSettings;
Console.WriteLine($"{AppSettings.Count} settings found");
Console.WriteLine($"Calling ClassLibrary2: {Environment.NewLine}{new ClassLibrary2.Class1().Reash}");
Console.ReadLine();
}
}
}
Esta aplicación debería haber instalado también Newtonsoft , pero en una versión diferente v12.0.3 .
Compile la aplicación en modo de depuración. Luego, en la carpeta ConsoleApp2 / ConsoleApp2 / bin / Debug, cree un archivo llamado test.exe.config con el siguiente contenido
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="A" value="1"/>
<add key="B" value="1"/>
<add key="C" value="1"/>
</appSettings>
</configuration>
y tenga en cuenta que en esa misma carpeta de depuración también está el archivo de configuración predeterminado ConsoleApp2.exe.config con un contenido como
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Si en este momento ejecuta la aplicación, se compilará sin problemas y debería ver una consola como
Tenga en cuenta que la configuración (3) se leyó correctamente desde mi archivo de configuración personalizado. Hasta aquí todo bien...
Ahora cambie el nombre del archivo de configuración predeterminado a algo como _ConsoleApp2.exe.config y vuelva a ejecutar la aplicación. Ahora debería obtener una FileLoadException .
Entonces, de nuevo, ¿cómo puedo decirle a mi aplicación que lea la <runtime>
información de mi archivo de configuración personalizado?
Razón fundamental
La razón por la que estoy buscando una respuesta a esta pregunta es la siguiente:
Cuando lanzamos nuestra aplicación, colocamos todos los archivos .exe y .dll en una carpeta y nuestro archivo de configuración personalizado (con configuraciones, etc.) en otra, donde nuestros clientes tienen archivos similares.
En la carpeta con los archivos .exe y .dll, tratamos de mantener la menor cantidad posible, por lo que me pidieron que encontrara una forma de deshacerme de esa ConsoleApp2.exe.config si es posible. Ahora, dado que los enlaces mencionados anteriormente se escribieron en ese archivo de configuración, intenté mover esa información a nuestro archivo de configuración personalizado ... pero hasta ahora no he logrado: las redirecciones de enlace siempre se intentan leer desde ese ConsoleApp2.exe .config , así que tan pronto como lo elimino , obtengo excepciones ...
fuente
<runtime>
sección desde otra configuración, pero el problema ocurre debido a la forma en que administra la configuración común. Si administra la configuración común de una manera diferente, este problema ya no es relevante. Verifique mi respuesta y considere usar transformaciones de configuración en lugar de ajustar el tiempo de ejecución.Respuestas:
Probablemente estés buscando transformaciones de configuración :
La idea detrás es crear múltiples configuraciones en Visual Studio como Debug, Release, Production, Test ... en el administrador de configuración y un archivo de configuración predeterminado más las llamadas transformaciones.
Tenga en cuenta que puede crear tantas configuraciones como desee en el administrador de configuración. Para agregar nuevos, haga clic en Configuraciones de soluciones (el menú desplegable que muestra "Debug" o "Release") y seleccione "Configuration Manager ...". Ábralo y verá una lista de todas las configuraciones existentes actualmente. Despliegue el cuadro combinado "Configuración de solución activa" y seleccione "
<New...>
" para agregar más.Esas transformaciones especifican lo que hace que la configuración específica sea diferente de la predeterminada, por lo que no necesita repetir lo que ya ha especificado en la configuración predeterminada, en su lugar solo menciona las diferencias, por ejemplo:
que encuentra la configuración relevante por su clave
ClientSessionTimeout
y establece su valor100
reemplazando el valor original en el archivo de configuración (esto es lo quexdt:Transform="SetAttributes" xdt:Locator="Match(key)"
significan los atributos de transformación adicionales ). También puede especificar eliminar configuraciones existentes (especificando en suxdt:Transform="Remove"
lugar), por ejemploeliminaría un ID de usuario que debería estar allí solo para la depuración, no para la versión (para obtener más información sobre las opciones disponibles, consulte aquí , descrito para Web.config, pero también aplicable para App.config).
Además del
App.Config
archivo, tiene un archivo por configuración, es decir ,App.Debug.Config
para depuración,App.Release.Config
para lanzamiento, etc. Visual Studio lo ayuda a crearlos.Ya he creado respuestas en StackOverflow aquí y allá , que lo describe en detalle, por favor, eche un vistazo.
Si tiene problemas para mostrarlos en Visual Studio, eche un vistazo aquí .
Con respecto a su justificación :
Las transformaciones están creando un archivo de configuración completo aplicando el archivo de transformación al archivo de configuración predeterminado. El archivo resultante se compila y se coloca en la carpeta "bin", junto con los otros archivos compilados. Por lo tanto, si tiene una configuración "Release" seleccionada, todos los archivos, incluido el archivo de configuración transformado, se compilan en "bin \ Release".
Y el archivo de configuración se nombra como el archivo exe más ".config" al final (en otras palabras, no hay ".Release.config" en la carpeta binaria, sino un "MySuperCoolApp.exe.config" creado - para la aplicación "MySuperCoolApp.exe").
Del mismo modo, lo mismo es cierto para la otra configuración: cada configuración crea una subcarpeta dentro de "bin". Si está utilizando scripts, se puede hacer referencia a esa subcarpeta como
$(TargetDir)
en un evento posterior a la compilación.fuente
Transformación de la configuración
Dado que el problema ocurre cuando intenta usar otro archivo de configuración (no nativo), está tratando de encontrar una solución para sustituirlo 'adecuadamente'. En mi respuesta, quiero dar un paso atrás y centrarme en la razón por la que desea sustituirlo. Según lo que describió en la pregunta, tiene que definir configuraciones de aplicación personalizadas. Si entendí correctamente, planea vincularlo al proyecto de destino, establezca la propiedad 'Copiar a la salida' en 'Siempre' y lo obtendrá cerca de la aplicación.
En lugar de copiar el nuevo archivo de configuración, hay una manera de transformar uno existente (nativo), en su caso,
ConsoleApp2.exe.config
utilizando transformaciones Xdt . Para lograr eso, crea un archivo de transformación y declara allí solo las secciones que desea transformar, por ejemplo:Los beneficios de dicho enfoque son:
La única desventaja de este enfoque es la curva de aprendizaje: debe aprender la sintaxis y saber cómo pegar las transformaciones a sus configuraciones en MSBuild.
.NET Core admite la transformación, aquí hay un ejemplo de cómo crear transformaciones para web.config, pero puede aplicar transformaciones a cualquier configuración.
Si desarrolla aplicaciones .NET (no .NET Core), le recomendaría que consulte Slowcheetah .
Hay muchos recursos y blogs útiles sobre la transformación, es bastante utilizado. Por favor contáctame si tienes dificultades.
Desde mi punto de vista, las transformaciones de configuración son una solución adecuada para lograr su objetivo, por lo que le recomiendo considerarlo en lugar de modificar el tiempo de ejecución.
Externalizar secciones de configuración
Si aún desea mantener appSettings en una ubicación común, puede externalizar las secciones de configuración con el atributo ConfigSource . Mira esto y este hilo para más detalles:
La sección AppSettings contiene el atributo File que le permite fusionar parámetros de otro archivo.
Esta opción le permite reemplazar ciertas secciones de la configuración, pero no todo el contenido en sí. Entonces, si solo necesita appSettings, es totalmente aplicable: simplemente coloque el archivo de configuración con appSettings en una ubicación común compartida con el usuario y el archivo de configuración de parche (agregar
file
oconfigSource
atributo) para obtener esta sección desde esa ubicación. Si necesita más secciones, deberá extraerlas como archivos separados.fuente
Para trabajar correctamente con un
.config
archivo diferente , puede mantener el predeterminado para administrar redireccionamientos de ofertas y otro para los parámetros de su aplicación. Para hacerlo, cambiar app.config predeterminado en tiempo de ejecución se ve muy bien.También puede cerrar la generación automática de redireccionamiento de enlace y usar solo un archivo app.config hecho a mano. Aquí hay un ejemplo: necesita una forma de hacer referencia a 2 versiones diferentes de la misma DLL de terceros
Editar Teniendo en cuenta la justificación: si lo entiendo, no desea el archivo app.exe.config en absoluto. Ya logras poner y leer contenido personalizado en otro lugar.
Solo queda la redirección vinculante.
Puede deshacerse de él administrando la redirección de enlace en tiempo de ejecución como se hace aquí: https://stackoverflow.com/a/32698357/361177 También puede recrear un solucionador de enlace configurable haciendo que su código mire el archivo de configuración.
Mis dos centavos aquí: es factible pero no creo que valga la pena.
Edición 2 Esta solución parece prometedora https://stackoverflow.com/a/28500477/361177
fuente
AppDomain.CurrentDomain.AssemblyResolve
evento y hacer que este método obtenga las reglas de enlace del archivo de configuración.