App.Config Transformation para proyectos que no son proyectos web en Visual Studio?


546

Para la aplicación basada en Web de Visual Studio 2010, tenemos características de Transformación de configuración mediante las cuales podemos mantener múltiples archivos de configuración para diferentes entornos. Pero la misma característica no está disponible para los archivos App.Config para Windows Services / WinForms o Console Application.

Hay una solución disponible como se sugiere aquí: aplicar magia XDT a App.Config .

Sin embargo, no es sencillo y requiere una serie de pasos. ¿Hay una manera más fácil de lograr lo mismo para los archivos app.config?


Me he encontrado con el siguiente artículo que parece un poco más simple pero no lo he intentado yo mismo. fknut.blogspot.com/2009/11/… Además, hay una solicitud de función en MS Connect que vale la pena votar, por lo que se incluye en todos los ámbitos en el próximo SP o versión. connect.microsoft.com/VisualStudio/feedback/details/564414
Kim R

Respuestas:


413

Esto funciona ahora con el complemento de Visual Studio tratado en este artículo: SlowCheetah - Web.config Transformation Syntax ahora generalizada para cualquier archivo de configuración XML .

Puede hacer clic con el botón derecho en su web.config y hacer clic en "Agregar transformaciones de configuración". Cuando haga esto, obtendrá un web.debug.config y un web.release.config. Puede hacer un web.whatever.config si lo desea, siempre que el nombre se alinee con un perfil de configuración. Estos archivos son solo los cambios que desea realizar, no una copia completa de su web.config.

Puede pensar que desea utilizar XSLT para transformar un web.config, pero si bien se sienten intuitivamente correctos, en realidad es muy detallado.

Aquí hay dos transformaciones, una usando XSLT y la misma usando la sintaxis / espacio de nombres de Transformación de documentos XML. Al igual que con todas las cosas, en XSLT hay varias formas de hacer esto, pero se entiende la idea general. XSLT es un lenguaje de transformación de árbol generalizado, mientras que este despliegue está optimizado para un subconjunto específico de escenarios comunes. Pero, lo bueno es que cada transformación XDT es un complemento .NET, por lo que puede hacer el suyo.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
  <xsl:copy>           
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="/configuration/appSettings">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <xsl:element name="add">
      <xsl:attribute name="key">NewSetting</xsl:attribute>
      <xsl:attribute name="value">New Setting Value</xsl:attribute>
    </xsl:element>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

O lo mismo a través de la transformación de implementación:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
   <appSettings>
      <add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/>
   </appSettings>
</configuration>

Oh eso es dulce! Tener una aplicación con numerosos archivos de configuración (log4net, nHibernate, web.config) y recordar cambiarlos todos fue un poco difícil. Tampoco tenía ganas de mover el código a CruiseControl.NET, pero parece que eso también es muy sencillo.
DilbertDave

10
Para su información, SlowCheetah fue una extensión fantástica que ahora no será compatible después de VS 2014. Según el autor, Sayed Ibrahim Hashimi, sedodream.com/2014/08/11/… .
bdeem

55
@ Andrew, lo leí aquí ; Sin embargo, eso fue hace un año. Después de volver a visitar el hilo y leer los comentarios, parece que alguien ha proporcionado una versión que funciona con VS2015 aquí .
Anil Natha

2
Trabajando perfectamente con Visual Studio 2017 y Visual STudio 2019
Guilherme de Jesus Santos

1
Esta aqui ahora
Hugo Freitas

574

Probé varias soluciones y aquí está la más simple que encontré personalmente.
Dan señaló en los comentarios que la publicación original pertenece a Oleg Sych . ¡ Gracias, Oleg!

Aquí están las instrucciones:

1. Agregue un archivo XML para cada configuración al proyecto.

Por lo general, tendrá Debugy Releaseconfiguraciones para nombrar sus archivos App.Debug.configy App.Release.config. En mi proyecto, creé una configuración para cada tipo de entorno, por lo que es posible que desee experimentar con eso.

2. Descargue el proyecto y abra el archivo .csproj para editarlo

Visual Studio le permite editar archivos .csproj directamente en el editor; solo necesita descargar el proyecto primero. Luego haga clic derecho sobre él y seleccione Editar <ProjectName> .csproj .

3. Enlace los archivos de la aplicación. * .Config a la aplicación principal.config

Encuentre la sección del archivo del proyecto que contiene todos App.config y App.*.configreferencias. Notarás que sus acciones de construcción están configuradas para None:

<None Include="App.config" />
<None Include="App.Debug.config" />
<None Include="App.Release.config" />

Primero, establezca la acción de construcción para todos ellos Content.
A continuación, haga todo los archivos específicos de la configuración dependan del principal App.configpara que Visual Studio los agrupe como lo hace con los archivos de diseñador y de código subyacente.

Reemplace XML arriba con el siguiente:

<Content Include="App.config" />
<Content Include="App.Debug.config" >
  <DependentUpon>App.config</DependentUpon>
</Content>
<Content Include="App.Release.config" >
  <DependentUpon>App.config</DependentUpon>
</Content>

4. Activa transformaciones mágicas (solo es necesario para las versiones de Visual Studio anteriores a VS2017 )

Al final del archivo después

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

y antes de la final

</Project>

inserte el siguiente XML:

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="CoreCompile" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
    <!-- Force build process to use the transformed configuration file from now on. -->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="app.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
  </Target>

¡Ahora puedes recargar el proyecto, construirlo y disfrutar de las App.configtransformaciones!

FYI

Asegúrese de que sus App.*.configarchivos tengan la configuración correcta como esta:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
     <!--magic transformations here-->
</configuration>

44
¡Mil gracias por esto! Una nota, si agrega los nuevos archivos .config al proyecto después de editar el csproj, aparecerán agrupados en App.config. Agregué uno antes de editar el csproj y esencialmente terminé con dos enlaces, uno agrupado y otro solo.
Jeff Swensen

8
Un problema con este enfoque es que cuando mira la pestaña "Publicar" en las propiedades del proyecto y luego hace clic en el botón "Archivos de aplicación" ... notará que app.config, app.Debug.config, app.Release.config se ve obligado a implementarse como parte del proceso de publicación. Claro, también obtienes el archivo MyApp.exe.config correcto, pero no quiero que se implemente ese equipaje adicional. Debe haber una manera de mantener los archivos de configuración de la aplicación. *. En el proyecto como <Ninguno> en lugar de <Contenido>.
Lee Grissom el

66
El único problema que esto deja fuera para algunos es que la respuesta tomada originalmente de Oleg Sych deja fuera una pieza clave. Si en su aplicación individual. (Env) .configs NO enumera '<configuration xmlns: xdt = " schemas.microsoft.com/XML-Document-Transform ">' y algo así como <appSettings xdt: Transform = "Reemplazar"> o atributos que hacen cosas similares en las líneas de configuración, no funcionará. Este último bit de información es clave y una vez que lo agregué, todo comenzó a funcionar.
djangojazz

24
Puede reemplazar v10.0con v$(VisualStudioVersion)para asegurarse de que su proyecto funciona con todas las versiones posteriores de VS.
Thibault D.

14
Tuve un error MSBuild error MSB3021: no se puede copiar el archivo. No se pudo encontrar el archivo 'obj \ Release \ ConsoleApp.exe' durante la compilación. Así que cambio un poco la solución para reutilizar la sección objetivo <Target Name = "AfterBuild"> en lugar de crear un nuevo me gusta en la solución
asidis

137

Otra solución que he encontrado es NO usar las transformaciones, sino solo tener un archivo de configuración separado, por ejemplo, app.Release.config. Luego agregue esta línea a su archivo csproj.

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <AppConfig>App.Release.config</AppConfig>
  </PropertyGroup>

Esto no solo generará el archivo myprogram.exe.config correcto, sino que si está utilizando el Proyecto de instalación y distribución en Visual Studio para generar MSI, obligará al proyecto de implementación a usar el archivo de configuración correcto al empaquetar.


66
Las maravillas no contadas de MSBuild. Ahora me pregunto qué más es posible. Por cierto. esto también funciona para implementaciones de clickonce directamente desde VS (en contraste con las respuestas con mayor voto).
Boris B.

44
Los cambios pueden volverse onerosos y propensos a errores si las configuraciones contienen muchas entradas que son IGUALES para todas las compilaciones. Enfrentando un problema en este momento en el que .config de un entorno perdió un cambio, y por supuesto fue la producción.
jeepwran

1
Tener dos copias del archivo de configuración no es un problema, siempre que los desarrolladores no sean quienes lo mantengan manualmente.
anIBMer

1
Esto es hermoso, funciona como un encanto! Pegué solo la <AppConfig>App.Release.config</AppConfig>línea dentro de la <PropertyGroupcondición existente para la Releaseconfiguración y el IDE mostró una línea ondulada debajo de la <AppConfig>... línea que decía que no estaba en el esquema o algo así, pero de todos modos guardé el archivo y volví a cargar el archivo del proyecto e hice una compilación en Releaseconfig y funcionó!
Shiva

1
Con esto, perderá la funcionalidad del diseñador de configuraciones.
Ondřej

33

En mi experiencia, las cosas que necesito hacer específicas para el entorno son cosas como cadenas de conexión, configuraciones de aplicaciones y, a menudo, configuraciones smpt. El sistema de configuración permite especificar estas cosas en archivos separados. Entonces puede usar esto en su app.config / web.config:

 <appSettings configSource="appsettings.config" />
 <connectionStrings configSource="connection.config" />
 <system.net>
    <mailSettings>
       <smtp configSource="smtp.config"/>
    </mailSettings>
 </system.net>

Lo que normalmente hago es colocar estas secciones específicas de la configuración en archivos separados, en una subcarpeta llamada ConfigFiles (ya sea en la raíz de la solución o en el nivel del proyecto, depende). Defino un archivo por configuración, por ejemplo smtp.config.Debug y smtp.config.Release.

Luego puede definir un evento de precompilación de esta manera:

copy $(ProjectDir)ConfigFiles\smtp.config.$(ConfigurationName) $(TargetDir)smtp.config

En el desarrollo de equipos, puede modificar esto aún más incluyendo% COMPUTERNAME% y / o% USERNAME% en la convención.

Por supuesto, esto implica que los archivos de destino (x.config) NO deben colocarse en el control de origen (ya que se generan). Sin embargo, aún debe agregarlos al archivo del proyecto y establecer su propiedad de tipo de salida para 'copiar siempre' o 'copiar si es más reciente'.

Simple, extensible, y funciona para todo tipo de proyectos de Visual Studio (consola, winforms, wpf, web).


Tengo exactamente la misma configuración que tú. Pero tengo problemas para transformar el archivo smtp. ¿Puedes incluir el original y la transformación? Estos son míos: El archivo base: <?xml version="1.0"?> <smtp deliveryMethod="SpecifiedPickupDirectory"> <specifiedPickupDirectory pickupDirectoryLocation="C:\mail"/> <network host="localhost"/> </smtp> La transformación:<?xml version="1.0"?> <smtp xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="Replace" from="user@email.com" deliveryMethod="Network"> <network .../> </smtp>
jgarza

No estoy seguro de entender. En esta configuración no transformo nada, es solo copiar archivos ...
jeroenh

Oh, no vi la parte de la copia. Transformaré la configuración en lugar de solo copiarla. Gracias de cualquier manera.
jgarza

Me gusta esta solución Una pequeña sugerencia: en el ejemplo de copia anterior, los argumentos de origen y destino para la copia deben estar entre comillas; de lo contrario, Pre-Build fallará para directorios con espacio en su nombre
vandre

32

Inspirado por Oleg y otros en esta pregunta, tomé la solución https://stackoverflow.com/a/5109530/2286801 un paso más para habilitar lo siguiente.

  • Funciona con ClickOnce
  • Funciona con proyectos de instalación e implementación en VS 2010
  • Funciona con VS2010, 2013, 2015 (no probó 2012, aunque debería funcionar también).
  • Trabaja con Team Build. (Debe instalar A) Visual Studio o B) Microsoft.Web.Publishing.targets y Microsoft.Web.Publishing.Tasks.dll)

Esta solución funciona realizando la transformación app.config antes de que se haga referencia a app.config por primera vez en el proceso de MSBuild. Utiliza un archivo de objetivos externos para facilitar la gestión en múltiples proyectos.

Instrucciones:

Pasos similares a la otra solución. He citado lo que sigue siendo igual y lo he incluido para completar y comparar más fácilmente.

0. Agregue un nuevo archivo a su proyecto llamado AppConfigTransformation.targets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Transform the app config per project configuration.-->
  <PropertyGroup>
    <!-- This ensures compatibility across multiple versions of Visual Studio when using a solution file.
         However, when using MSBuild directly you may need to override this property to 11.0 or 12.0 
         accordingly as part of the MSBuild script, ie /p:VisualStudioVersion=11.0;
         See http://blogs.msdn.com/b/webdev/archive/2012/08/22/visual-studio-project-compatability-and-visualstudioversion.aspx -->
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
  </PropertyGroup>

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.targets" />

  <Target Name="SetTransformAppConfigDestination" BeforeTargets="PrepareForBuild" 
          Condition="exists('app.$(Configuration).config')">
    <PropertyGroup>
      <!-- Force build process to use the transformed configuration file from now on. -->
      <AppConfig>$(IntermediateOutputPath)$(TargetFileName).config</AppConfig>
    </PropertyGroup>
    <Message Text="AppConfig transformation destination: = $(AppConfig)" />
  </Target>

  <!-- Transform the app.config after the prepare for build completes. -->
  <Target Name="TransformAppConfig" AfterTargets="PrepareForBuild" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(AppConfig)" Transform="app.$(Configuration).config" />
  </Target>

</Project>

1. Agregue un archivo XML para cada configuración al proyecto.

Por lo general, tendrá configuraciones de depuración y lanzamiento, por lo tanto, nombre sus archivos App.Debug.config y App.Release.config. En mi proyecto, creé una configuración para cada tipo de entorno, por lo que es posible que desee experimentar con eso.

2. Descargue el proyecto y abra el archivo .csproj para editarlo

Visual Studio le permite editar .csproj directamente en el editor, solo necesita descargar el proyecto primero. Luego haga clic derecho sobre él y seleccione Editar .csproj.

3. Enlace los archivos de la aplicación. * .Config a la aplicación principal.config

Busque la sección del archivo del proyecto que contiene todas las referencias de App.config y App. *. Config y reemplace de la siguiente manera. Notarás que usamos None en lugar de Content.

<ItemGroup>
  <None Include="app.config"/>
  <None Include="app.Production.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.QA.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.Development.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
</ItemGroup>

4. Activa transformaciones mágicas

Al final del archivo después

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

y antes de la final

</Project>

inserte el siguiente XML:

<Import Project="AppConfigTransformation.targets" />

¡Hecho!


1
Intenté en VS Community 2015 RC e ignora el archivo app.Debug.config que tengo.
Khainestar

Utilicé con éxito la respuesta aceptada en un proyecto de WinForms ... pero por alguna razón desconcertante no pude aplicar la respuesta aceptada. a otro proyecto WinForms (todos en la misma solución). Esta respuesta de @bdeem es mi nuevo favorito, ya que interopera correctamente con mi proyecto MSI, ¡muchas gracias!
bkwdesign

Esto no pareció funcionar en VS 2015. Actualicé VisualStudioVersion de 10 a 12, pero sin dados. ¿Algunas ideas?
Sinaesthetic

@Sinaesthetic ¿Puedes darnos más detalles? VS 2015 Ultimate, Comunidad, etc. VB.NET, C #, ¿Algún error?
bdeem

VS2015 Enterprise. Sin errores de ningún tipo. Simplemente no hace nada.
Sinaesthetic

27

Puede usar un archivo de configuración separado por configuración, por ejemplo, app.Debug.config, app.Release.config y luego usar la variable de configuración en su archivo de proyecto:

<PropertyGroup>
    <AppConfig>App.$(Configuration).config</AppConfig>
</PropertyGroup>

Esto creará el archivo ProjectName.exe.config correcto según la configuración que esté creando.


Gracias, no utilicé su ejemplo exacto para resolver el problema que estaba teniendo, pero su ejemplo me hizo pensar y me llevó a otra soulution muy similar usando la tarea Copiar.
jpierson el

Intenté esto bajo VS 2015 Community RC y se compila, pero luego ignora el contenido de la aplicación. *. Config que he agregado.
Khainestar

14

Escribí una buena extensión para automatizar la transformación de app.config como la integrada en Transformación de configuración de proyecto de aplicación web

La mayor ventaja de esta extensión es que no necesita instalarla en todas las máquinas de compilación


1
Extensión muy útil, especialmente ahora que Slow Cheetah está entrando en modo de mantenimiento y puede no ser compatible en el futuro.
dthrasher

Sí, la gente debería dejar de ralentizar a Cheetah como la solución para esto cuando esta funcionalidad ahora es compatible con la tarea transformxml msbuild. Un arquitecto sw de mi equipo introdujo el guepardo lento de una manera excesivamente entusiasta en nuestro proyecto y creó las transformaciones de depuración, puesta en escena y lanzamiento de todas nuestras configuraciones, la mayoría de las cuales no necesitaban transformación. No hace falta decir que, en el momento en que se fue, saqué el guepardo lento y ahora solo usamos una sola tarea transformxml en web.config. Ahhhhh, simplicidad. No quiere decir que el guepardo lento no tuviera su tiempo y lugar.
HarryTuttle

5

Instale la "Herramienta de transformación de configuración" en Visual Studio desde Marketplace y reinicie VS. También podrá ver la transformación de vista previa del menú para app.config.

https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform


1
Esto funciona perfectamente y requiere muy poco esfuerzo o pensamiento. Muy apreciado. Gracias. (la 'transformación de vista previa' no funciona, pero la 'agregar transformaciones' funciona perfectamente sin problemas en VS 2017). También parece recibir actualizaciones a menudo.
adudley

1
muchas gracias por la solución, detrás de escena, hace exactamente lo que Dan Abramov ha explicado anteriormente, sin ensuciarse la mano
Mohammed Dawood Ansari

Esta es la solución definitiva. La vista previa parece funcionar bien con VS 2019.
Kyle Champion

1
Me encanta, pero descubrí que no era compatible con otros archivos que no sean app.config sin alguna edición de csproj. Sin embargo, sigue siendo genial ver la vista previa.
Ian1971

4

Así que terminé adoptando un enfoque ligeramente diferente. Seguí los pasos de Dan hasta el paso 3, pero agregué otro archivo: App.Base.Config. Este archivo contiene los ajustes de configuración que desea en cada App.Config generado. Luego uso BeforeBuild (con la adición de Yuri a TransformXml) para transformar la configuración actual con la configuración Base en App.config. El proceso de compilación luego usa el App.config transformado de manera normal. Sin embargo, una molestia es que desea excluir el siempre cambiante App.config del control de origen después, pero los otros archivos de configuración ahora dependen de él.

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="BeforeBuild" Condition="exists('app.$(Configuration).config')">
    <TransformXml Source="App.Base.config" Transform="App.$(Configuration).config" Destination="App.config" />
  </Target>

3

Solo una pequeña mejora a la solución que parece estar publicada en todas partes ahora:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  • es decir, a menos que esté planeando quedarse con su versión VS actual para siempre

¿Puede explicarme un poco su respuesta o dar fuentes para explicarla?
roydukkey

44
No parece que $(VisualStudioVersion)esté configurado cuando se usa MSBuild directamente.
Jeremy Smith

Esto debería ser un comentario para stackoverflow.com/a/5109530/2003763 (acabo de agregar la misma información que un comentario allí)
Thibault D.

2

He creado otra alternativa a la publicada por Vishal Joshi donde el requisito de cambiar la acción de compilación a Contenido se elimina y también implementé el soporte básico para la implementación de ClickOnce. Digo básico, porque no lo probé a fondo, pero debería funcionar en el escenario típico de implementación de ClickOnce.

La solución consiste en un único proyecto de MSBuild que una vez importado a un proyecto de aplicación de Windows existente (* .csproj) extiende el proceso de compilación para contemplar la transformación app.config.

Puede leer una explicación más detallada en Visual Studio App.config XML Transformation y el archivo del proyecto MSBuild se puede descargar desde GitHub .


1

Si utiliza un TFS en línea (versión en la nube) y desea transformar la App.Config en un proyecto, puede hacer lo siguiente sin instalar ninguna herramienta adicional. Desde VS => Descargue el proyecto => Editar archivo de proyecto => Vaya al final del archivo y agregue lo siguiente:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
<TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="$(OutDir)\$(AssemblyName).dll.config" />

AssemblyFile and Destination funciona para uso local y servidor TFS en línea (Cloud).


0

La solución propuesta no funcionará cuando se haga referencia a una biblioteca de clases con un archivo de configuración de otro proyecto (en mi caso, fue la biblioteca de proyectos de Azure Worker). No copiará el archivo transformado correcto deobj carpeta a otra bin\##configuration-name##. Para que funcione con cambios mínimos, debe cambiar el AfterCompileobjetivo a BeforeCompile:

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.