Como selecionar diferentes app.config para várias configurações de compilação

115

Eu tenho um projeto do tipo dll que contém testes de integração MSTest. Na minha máquina, os testes passam e eu quero que o mesmo aconteça em um servidor de CI (eu uso o TeamCity). Mas os testes falham, porque preciso ajustar algumas configurações em app.config. É por isso que eu estava pensando em ter um segundo arquivo app.config separado que manterá as configurações do servidor CI.

Então eu gostaria de ter

/ Sln
 / Proj
  app.config (acho que isso é exigido pelo VS)
  app.Release.config (Este é um arquivo de configuração independente e autônomo)

Portanto, se eu selecionar Release configuration na build config no CI, gostaria de usar o arquivo app.Release.config em vez de app.config

Problema
Isso não parece ser direto para projetos simples do tipo .dll. Para projetos da web, posso fazer transformações de configuração da web. Eu encontrei um hack para fazer essas transformações para um projeto do tipo dll, mas não sou um grande fã de hacks.

Pergunta
Qual é a abordagem padrão para ajustar arquivos app.config dependendo da configuração de compilação para projetos .NET (como Debug, Release, ...)?

oleksii
fonte

Respostas:

154

Use o plugin SlowCheetah . Para mais opções e detalhes sobre como usar o SlowCheetah, continue lendo.

Como você já notou, não existe uma maneira padrão e fácil de usar diferentes arquivos de configuração para um projeto do tipo Biblioteca (.dll) . A razão é que o pensamento atual é: "Você não precisa"! Os desenvolvedores de framework consideram que você precisa de configuração para o arquivo executável: seja um console, desktop, web, aplicativo móvel ou qualquer outra coisa. Se você começar a fornecer configuração para uma dll , pode acabar com algo que posso chamar de inferno de configuração . Você pode não entender mais (facilmente) por que essas e aquelas variáveis ​​têm valores tão estranhos que vêm aparentemente de lugar nenhum.

"Espere", - você pode dizer, "mas eu preciso disso para minha integração / teste de unidade, e é uma biblioteca!". E isso é verdade e isso é o que você pode fazer (escolha apenas um, não misture):

1. SlowCheetah - transforma o arquivo de configuração atual

Você pode instalar o SlowCheetah - um plug-in do Visual Studio que faz todo o rastreamento (ou transformação) de XML de baixo nível para você. A forma como funciona, resumidamente:

  • Instale o SlowCheetah e reinicie o Visual Studio (Visual Studio> Ferramentas> Extensões e atualizações ...> Online> Galeria do Visual Studio> pesquise "Slow Cheetah")
  • Defina as configurações da sua solução ( Debug e Release estão lá por padrão), você pode adicionar mais (clique com o botão direito na solução em Solution Explorer > Configuration Manager ... > Active Solution Configuration > New ...
  • Adicione um arquivo de configuração se necessário
  • Clique com o botão direito no arquivo de configuração> Adicionar transformação
    • Isso criará arquivos de transformação - um por sua configuração
    • Os arquivos de transformação funcionam como injetores / modificadores, eles encontram o código XML necessário no arquivo de configuração original e injetam novas linhas ou modificam o valor necessário, o que quer que você diga para fazer

2. Brinque com o arquivo .proj - copia-renomeia um arquivo de configuração totalmente novo

Tirado originalmente daqui . É uma tarefa MSBuild personalizada que você pode incorporar ao arquivo .proj do Visual Studio . Copie e cole o seguinte código no arquivo de projeto

<Target Name="AfterBuild">
    <Delete Files="$(TargetDir)$(TargetFileName).config" />
    <Copy SourceFiles="$(ProjectDir)\Config\App.$(Configuration).config"
          DestinationFiles="$(TargetDir)$(TargetFileName).config" />
</Target>

Agora crie uma pasta no projeto chamado Confige adicione novos arquivos lá: App.Debug.config , App.Release.config e assim por diante. Agora, dependendo da sua configuração, o Visual Studio escolherá o arquivo de configuração de uma Configpasta e o copiará e renomeará no diretório de saída. Portanto, se você tiver o projeto PatternPA.Test.Integration e uma configuração de Depuração selecionados, na pasta de saída após a construção, você encontrará um arquivo PatternPA.Test.Integration.dll.config que foi copiado Config\App.Debug.confige renomeado posteriormente.

Estas são algumas notas que você pode deixar nos arquivos de configuração

<?xml version="1.0" encoding="utf-8"?>
<configuration>

    <!-- This file is copied and renamed by the 'AfterBuild' MSBuild task -->

    <!-- Depending on the configuration the content of projectName.dll.config 
        is fully substituted by the correspondent to build configuration file 
        from the 'Config' directory. -->

</configuration>

No Visual Studio, você pode ter algo assim

Estrutura do projeto

3. Use arquivos de script fora do Visual Studio

Cada ferramenta de construção (como NAnt , MSBuild ) fornecerá recursos para transformar o arquivo de configuração, dependendo da configuração. Isso é útil se você construir sua solução em uma máquina de construção, na qual você precisa ter mais controle sobre o que e como preparar o produto para lançamento.

Por exemplo, você pode usar a tarefa dll de publicação na web para transformar qualquer arquivo de configuração

<UsingTask AssemblyFile="..\tools\build\Microsoft.Web.Publishing.Tasks.dll"
    TaskName="TransformXml"/>

<PropertyGroup>
    <!-- Path to input config file -->  
    <TransformInputFile>path to app.config</TransformInputFile>
    <!-- Path to the transformation file -->    
    <TransformFile>path to app.$(Configuration).config</TransformFile>
    <!-- Path to outptu web config file --> 
    <TransformOutputFile>path to output project.dll.config</TransformOutputFile>
</PropertyGroup>

<Target Name="transform">
    <TransformXml Source="$(TransformInputFile)"
                  Transform="$(TransformFile)"
                  Destination="$(TransformOutputFile)" />
</Target>
oleksii
fonte
Sua segunda solução funciona bem, mas não para publicar projetos da web. Depois de publicar um projeto ASP.NET, o web.config original é publicado.
Massood Khaari
3
@MassoodKhaari você precisa garantir que essa tarefa seja chamada para o destino de publicação. Quando você publica um projeto, um destino de construção separado é chamado, o que pode não ser chamado pelo AfterBuilddestino padrão . Durante a compilação típica, o AfterBuilddestino é chamado por padrão. Deve haver uma solução rápida para o caso de publicação
oleksii
1
Usei seu segundo método (meio). Fui para propriedades do projeto e editou o BeforeBuild para copiar a App.<Target>.configtodo o App.configna dir projeto , não a dir saída.
SparK
@oleksii Você está certo. Mas ainda não consegui encontrar o destino que meu processo de publicação na web está usando (no Visual Studio 2013).
Massood Khaari
1
Estou usando o segundo método, mas preciso adicionar uma condição ao destino AfterBuild para garantir que o arquivo realmente exista antes da exclusão. Eu tenho uma configuração de compilação de depuração, que basicamente usa apenas o arquivo App.config padrão, mas não tinha App.Debug.config, o que significava que a etapa de compilação falharia. Acabei de adicionar Condition="Exists('$(ProjectDir)App.$(Configuration).config')".
Siewers
23

Você pode tentar a seguinte abordagem:

  1. Clique com o botão direito do mouse no projeto no Solution Explorer e selecione Unload Project .
  2. O projeto será descarregado. Clique com o botão direito no projeto novamente e selecione Edit <YourProjectName> .csproj .
  3. Agora você pode editar o arquivo de projeto dentro do Visual Studio.
  4. Localize o local no arquivo * .csproj onde o arquivo de configuração do aplicativo está incluído. Será parecido com:
    <ItemGroup>
        <None Include = "App.config" />
    </ItemGroup>
  1. Substitua essas linhas pelas seguintes:
    <ItemGroup Condition = "'$ (Configuration)' == 'Debug'">
        <None Include = "App.Debug.config" />
    </ItemGroup>

    <ItemGroup Condition = "'$ (Configuration)' == 'Release'">
        <None Include = "App.Release.config" />
    </ItemGroup>

Não tentei essa abordagem para app.configarquivos, mas funcionou bem com outros itens de projetos do Visual Studio. Você pode personalizar o processo de construção de quase qualquer maneira que desejar. De qualquer forma, me diga o resultado.

Vharavy
fonte
Tnx para a resposta, mas isso não funciona com app.config. O VS requer uma app.configconfiguração obrigatória e não está aplicando a configuração de versão se eu usar o VS build ou Teamcity VS sln build runner.
oleksii de
2
Aqui explica como fazer: Habilitar app.debug.config app.release.config
Gabrielizalo
1
Por que essa resposta tem tantos votos positivos? Eu tentei e não funciona. Na verdade, tanto no modo de depuração quanto no modo de liberação, não há arquivo App.config e, portanto, não há arquivo correspondente na pasta de saída. Os arquivos App.Debug.config e App.Release.config não têm nenhum significado para o Visual Studio.
MarkusParker de
Não funciona: .csproj não pode ser aberto, mensagem de erro "elementos fora dos elementos de destino devem ter: Incluir, Atualizar ou Remover"
Elo
12

Você deve considerar o ConfigGen . Foi desenvolvido para este fim. Ele produz um arquivo de configuração para cada máquina de implantação, com base em um arquivo de modelo e um arquivo de configurações. Sei que isso não responde especificamente à sua pergunta, mas pode muito bem responder ao seu problema.

Portanto, em vez de Depurar, Liberar etc., você pode ter Teste, UAT, Produção etc. Você também pode ter configurações diferentes para cada máquina de desenvolvedor, de modo que possa gerar uma configuração específica para sua máquina de desenvolvimento e alterá-la sem afetar a implantação de ninguém .

Um exemplo de uso pode ser ...

<Target Name="BeforeBuild">
    <Exec Command="C:\Tools\cfg -s $(ProjectDir)App.Config.Settings.xls -t       
        $(ProjectDir)App.config.template.xml -o $(SolutionDir)ConfigGen" />

    <Exec Command="C:\Tools\cfg -s $(ProjectDir)App.Config.Settings.xls -t
        $(ProjectDir)App.config.template.xml -l -n $(ProjectDir)App.config" />
</Target>

Se você colocar isso em seu arquivo .csproj, e você tem os seguintes arquivos ...

$(ProjectDir)App.Config.Settings.xls

MachineName        ConfigFilePath   SQLServer        

default             App.config      DEVSQL005
Test                App.config      TESTSQL005
UAT                 App.config      UATSQL005
Production          App.config      PRODSQL005
YourLocalMachine    App.config      ./SQLEXPRESS


$(ProjectDir)App.config.template.xml 

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   <configuration>
   <appSettings>
       <add key="ConnectionString" value="Data Source=[%SQLServer%]; 
           Database=DatabaseName; Trusted_Connection=True"/>
   </appSettings>
</configuration>

... então esse será o resultado ...

A partir do primeiro comando, um arquivo de configuração gerado para cada ambiente especificado no arquivo xls, colocado no diretório de saída $ (SolutionDir) ConfigGen

.../solutiondir/ConfigGen/Production/App.config

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   <configuration>
   <appSettings>
       <add key="ConnectionString" value="Data Source=PRODSQL005; 
           Database=DatabaseName; Trusted_Connection=True"/>
   </appSettings>
</configuration>

A partir do segundo comando, o App.config local usado em sua máquina de desenvolvimento será substituído pela configuração gerada especificada pela opção local (-l) e a opção nome do arquivo (-n).

Daniel Dyson
fonte
2
Tnx para a resposta, isso não parece ruim. Mas existem algumas desvantagens, ele mostra apenas 75 downloads (portanto, não é maduro) e funciona apenas com .xls ou .xlsx. Eu realmente não quero depender de outro formato de documento personalizado para operações simples. Eu estava procurando uma abordagem mais padrão ...
oleksii
2
Ponto justo, embora diga 194 downloads no CodePlex, xls é uma planilha, dificilmente um formato personalizado, e eu conheço três grandes bancos de investimento que aprovaram isso para uso, então se é bom o suficiente para eles ... Além disso, um dos recursos atualmente solicitados é usar xml para as configurações. Está quase pronto, mas eu prefiro a abordagem da planilha de qualquer maneira. É muito mais fácil ver todas as configurações de todos os ambientes em uma visualização tabular
Daniel Dyson
Agora estamos nos estágios finais de teste de uma versão do configGen que pode ser usada para gerar arquivos de texto simples, não apenas xml. Portanto, se você deseja gerar css, sql, javascript etc. específicos do ambiente, fique de olho no site configGen
Daniel Dyson
obrigado Daniel pela solução, isso é exatamente o que eu estava procurando. Vou tentar.
Bhupinder Singh
10

Usando a mesma abordagem do Romeo, adaptei-o ao Visual Studio 2010:

 <None Condition=" '$(Configuration)' == 'Debug' " Include="appDebug\App.config" />

 <None Condition=" '$(Configuration)' == 'Release' " Include="appRelease\App.config" />

Aqui você precisa manter os dois arquivos App.config em diretórios diferentes (appDebug e appRelease). Eu testei e funciona bem!

tem peru
fonte
3

Estou usando a ferramenta XmlPreprocess para manipulação de arquivos de configuração. Ele está usando um arquivo de mapeamento para vários ambientes (ou vários destinos de construção no seu caso). Você pode editar o arquivo de mapeamento pelo Excel. É muito fácil de usar.

Ludwo
fonte
3

SlowCheetah e FastKoala da Galeria do VisualStudio parecem ser ferramentas muito boas que ajudam com esse problema.

No entanto, se você quiser evitar addins ou usar os princípios que eles implementam mais extensivamente em seus processos de construção / integração, adicionar isso aos arquivos proj msbuild * é uma solução abreviada.

Nota: isso é mais ou menos uma reformulação da resposta nº 2 de @oleksii.

Isso funciona para projetos .exe e .dll:

  <Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Source="App_Config\app.Base.config" Transform="App_Config\app.$(Configuration).config" Destination="app.config" />
  </Target>

Isso funciona para projetos da web:

  <Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Source="App_Config\Web.Base.config" Transform="App_Config\Web.$(Configuration).config" Destination="Web.config" />
  </Target>

Observe que esta etapa acontece antes mesmo do início da construção adequada. A transformação do arquivo de configuração acontece na pasta do projeto. Para que o web.config transformado esteja disponível quando você estiver depurando (uma desvantagem do SlowCheetah).

Lembre-se de que, se você criar a pasta App_Config (ou como quiser chamá-la), os vários arquivos de configuração intermediários devem ter Build Action = None e Copy to Output Directory = Do not copy.

Isso combina as duas opções em um bloco. O apropriado é executado com base nas condições. A tarefa TransformXml é definida primeiro:

<Project>
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="TransformOnBuild" BeforeTargets="PrepareForBuild">
    <TransformXml Condition="Exists('App_Config\app.Base.config')" Source="App_Config\app.Base.config" Transform="App_Config\app.$(Configuration).config" Destination="app.config" />
    <TransformXml Condition="Exists('App_Config\Web.Base.config')" Source="App_Config\Web.Base.config" Transform="App_Config\Web.$(Configuration).config" Destination="Web.config" />
</Target>

Eniola
fonte
Acabei de tentar isso no Visual Studio 2017 e não está funcionando. Atire. Eu realmente esperava que funcionasse, porque parece ser o mais fácil de implementar.
Greg Burghardt
A tarefa TransformXml não está definida nos exemplos. Estou adicionando uma entrada. Você pode defini-lo em um arquivo mycustom.targets que é incluído em todos os seus projetos de ativação em sua solução.
Eniola
@GregBurghardt, quer experimentar agora?
Eniola
Eu talvez tente. Eu instalei o plug-in Config Transform para Visual Studio e funcionou muito bem. Na verdade, eu me pergunto se o plug-in basicamente faz o que sua resposta faz.
Greg Burghardt
Ok, deixe-me saber como foi.
Eniola
1

Veja se o mecanismo de transformação XDT (web.config) pode ajudá-lo. Atualmente, ele é suportado apenas nativamente para projetos da web, mas tecnicamente não há nada que impeça você de usá-lo em outros tipos de aplicativos. Existem muitos guias sobre como usar o XDT editando manualmente os arquivos do projeto, mas encontrei um plug-in que funciona muito bem: https://visualstudiogallery.msdn.microsoft.com/579d3a78-3bdd-497c-bc21-aa6e6abbc859

O plugin está apenas ajudando a definir a configuração, não é necessário para construir e a solução pode ser construída em outras máquinas ou em um servidor de construção sem o plugin ou qualquer outra ferramenta sendo necessária.

TGasdf
fonte
Esta deve ser a resposta agora. Acabei de experimentar no VS 2017 e funciona perfeitamente. Você não precisa publicar o projeto. Basta construir. Funciona muito bem para nosso projeto de teste para uso em nossa construção de integração contínua para que possamos executar testes Selenium no modo headless, mas localmente eles são executados com o navegador sendo aberto. +1.000.000 se eu pudesse.
Greg Burghardt
1

Resolvi este tópico com a solução que encontrei aqui: http://www.blackwasp.co.uk/SwitchConfig.aspx

Resumindo, o que eles afirmam é: "adicionando um evento pós-construção. [...] Precisamos adicionar o seguinte:

if "Debug"=="$(ConfigurationName)" goto :nocopy
del "$(TargetPath).config"
copy "$(ProjectDir)\Release.config" "$(TargetPath).config"
:nocopy
Janbro
fonte
De longe o método mais fácil de fazer o que deveria ter sido uma função muito simples e essencial que foi deturpada por pensadores exagerados! Obrigado Janbro.
BoiseBaked em
1

Eu ouvi coisas boas sobre o SlowCheetah, mas não consegui fazê-lo funcionar. Eu fiz o seguinte: adicione uma tag a cada um para uma configuração específica.

Ex:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'UAT|AnyCPU'">
    <OutputPath>bin\UAT\</OutputPath>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AppConfig>App.UAT.config</AppConfig>
  </PropertyGroup>
Mike
fonte
Esta parece ser outra maneira super simples de alterar os arquivos app.config de acordo com a configuração de compilação. Mike, você testou com as configurações padrão de depuração e liberação?
BoiseBaked em
0

Depois de algumas pesquisas sobre o gerenciamento de configurações para desenvolvimento e builds, etc., decidi lançar o meu próprio, disponibilizei-o no bitbucket em: https://bitbucket.org/brightertools/contemplate/wiki/Home

Esses vários arquivos de configuração para vários ambientes, é uma ferramenta de substituição de entrada de configuração básica que funcionará com qualquer formato de arquivo baseado em texto.

Espero que isto ajude.

Mark Redman
fonte