Configuração do .NET (app.config / web.config / settings.settings)

162

Eu tenho um aplicativo .NET que possui arquivos de configuração diferentes para compilações de depuração e lançamento. Por exemplo, o arquivo debug app.config aponta para um SQL Server de desenvolvimento com a depuração ativada e o destino da versão aponta para o SQL Server ativo. Existem também outras configurações, algumas das quais são diferentes na depuração / liberação.

Atualmente, uso dois arquivos de configuração separados (debug.app.config e release.app.config). Eu tenho um evento de construção no projeto que diz que, se for uma versão, copie release.app.config para app.config, caso contrário, copie debug.app.config para app.config.

O problema é que o aplicativo parece obter as configurações do arquivo settings.settings, então eu tenho que abrir o settings.settings no Visual Studio, o que me avisa que as configurações foram alteradas, então eu aceito as alterações, salve o arquivo settings.settings e para reconstruir para usar as configurações corretas.

Existe um método melhor / recomendado / preferido para obter um efeito semelhante? Ou igualmente, eu lidei com isso completamente errado e existe uma abordagem melhor?

Gavin
fonte
Eu quero desativar a depuração no Windows, tentei desmarcar toda a caixa de seleção nas configurações de depuração, mas ainda assim eu poderia depurar o exe da liberação do bin .. Alguém pode me ajudar nisso ..
Vinoth Narayan

Respostas:

62

Qualquer configuração que possa diferir entre os ambientes deve ser armazenada no nível da máquina , não no nível do aplicativo . (Mais informações sobre os níveis de configuração.)

Estes são os tipos de elementos de configuração que eu normalmente armazeno no nível da máquina:

Quando cada ambiente (desenvolvedor, integração, teste, estágio, live) possui suas próprias configurações exclusivas no diretório c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , é possível promover o código do aplicativo entre ambientes sem nenhum modificações pós-compilação.

E, obviamente, o conteúdo do diretório CONFIG no nível da máquina é controlado por versão em um repositório diferente ou em uma estrutura de pastas diferente do seu aplicativo. Você pode tornar seus arquivos .config mais amigáveis ​​ao controle de origem através do uso inteligente do configSource .

Faço isso há 7 anos, em mais de 200 aplicativos ASP.NET em mais de 25 empresas diferentes. (Não estou tentando me gabar, só quero que você saiba que nunca vi uma situação em que essa abordagem não funcione.)

Portman
fonte
3
E uma situação em que você não controla o servidor da Web e, portanto, não pode alterar a configuração no nível da máquina? Os exemplos incluem um servidor web de terceiros ou um servidor web compartilhado entre vários departamentos de uma empresa.
RationalGeek
1
Não funcionaria. Mas na era das máquinas virtuais, servidores Amazon EC2 e US $ 400 da Dell, alguém realmente faz algo sério em máquinas compartilhadas? Não estou tentando ser insensível - eu realmente acho que se você estiver trabalhando em um servidor da web compartilhado, deverá reavaliar.
Portman
7
A maioria das empresas que eu trabalhei no com sites internos hospedar vários aplicativos em um servidor - há uma reavaliação teria que ser feito em nível corporativo
MPritchard
Vários aplicativos em um servidor estão bem, desde que todos estejam no mesmo "ambiente". Ou seja, você não deseja a instância LIVE do App1 no mesmo servidor que a instância DEV do App2. Por exemplo, suas configurações de SMTP seriam compartilhadas em todos os seus aplicativos. Na produção, você aponta para um servidor de email real; em desenvolvimento, você aponta para um arquivo em disco.
Portman
7
Eu sei que isso funcionará, mas isso ainda vai contra o que eu recomendaria ao tentar automatizar a implantação. Eu acho que as configurações são específicas da aplicação, elas precisam ser controladas por versão junto com a aplicação e evoluir junto com ela. Confiar na configuração da máquina apenas muda, e, na minha opinião, fica mais difícil. Eu gosto de manter juntas coisas que mudam juntas e implantá-las juntas. Se eu adicionar uma nova configuração para Dev, provavelmente precisarei de uma configuração equivalente para prod.
Miguel Madero
51

Isso pode ajudar algumas pessoas a lidar com Settings.settings e App.config: cuidado com o atributo GenerateDefaultValueInCode no painel Propriedades ao editar qualquer um dos valores na grade Settings.settings no Visual Studio (Visual Studio 2008 no meu caso).

Se você definir GenerateDefaultValueInCode como True (True é o padrão aqui!), O valor padrão será compilado no EXE (ou DLL), você poderá encontrá-lo incorporado no arquivo ao abri-lo em um editor de texto sem formatação.

Eu estava trabalhando em um aplicativo de console e, se eu tivesse assumido o padrão no EXE, o aplicativo sempre ignorava o arquivo de configuração colocado no mesmo diretório! Um pesadelo e nenhuma informação sobre isso em toda a Internet.

romano
fonte
7
Foi exatamente isso que aconteceu comigo no fim de semana passado. Eu arranquei muito cabelo tentando descobrir por que meu aplicativo parecia estar ignorando meu arquivo app.config! Ele deve se conectar a um serviço da Web e o URL do serviço está no meu app.config. Sem o meu conhecimento, quando criei a referência da Web, ele também criou um arquivo Settings.Settings E codificou o valor padrão no código. Mesmo quando finalmente descobri (e removi) o arquivo de configurações, esse valor padrão permaneceu no código rígido e foi incorporado no exe. MUITO FRUSTRANTE!! Graças a este post, agora eu pode se livrar desse "recurso"
Mike K
+1 Esta resposta é crítica : se você deseja que sua configuração vá para o arquivo app.config, defina seu atributo GenerateDefaultValueInCode como False (o padrão é True).
Sabuncu
34

Há uma pergunta relacionada aqui:

Melhorando seu processo de compilação

Os arquivos de configuração oferecem uma maneira de substituir as configurações:

<appSettings file="Local.config">

Em vez de fazer check-in em dois arquivos (ou mais), você só faz check-in no arquivo de configuração padrão e, em cada máquina de destino, coloca um Local.config, apenas com a seção appSettings que substitui essa máquina em particular.

Se você estiver usando seções de configuração, o equivalente é:

configSource="Local.config"

Obviamente, é uma boa ideia fazer cópias de backup de todos os arquivos Local.config de outras máquinas e registrá-las em algum lugar, mas não como parte das soluções reais. Cada desenvolvedor coloca um "ignorar" no arquivo Local.config para que não seja verificado, o que sobrescreveria o arquivo de todos os outros.

(Na verdade, você não precisa chamá-lo de "Local.config", é isso que eu uso)

Eric Z Beard
fonte
14

Pelo que estou lendo, parece que você está usando o Visual Studio para o seu processo de compilação. Você já pensou em usar o MSBuild e o Nant ?

A sintaxe xml de Nant é um pouco estranha, mas depois que você entende, fazer o que você mencionou se torna bastante trivial.

<target name="build">
    <property name="config.type" value="Release" />

    <msbuild project="${filename}" target="Build" verbose="true" failonerror="true">
        <property name="Configuration" value="${config.type}" />
    </msbuild>

    <if test="${config.type == 'Debug'}">
        <copy file=${debug.app.config}" tofile="${app.config}" />
    </if>

    <if test="${config.type == 'Release'}">
        <copy file=${release.app.config}" tofile="${app.config}" />
    </if>

</target>
Steven Williams
fonte
8

Costumávamos usar projetos de implantação da Web, mas desde então migramos para o NAnt. Em vez de ramificar e copiar diferentes arquivos de configuração, atualmente incorporamos os valores de configuração diretamente no script de construção e injetamos em nossos arquivos de configuração por meio de tarefas xmlpoke:

  <xmlpoke
    file="${stagingTarget}/web.config"
    xpath="/configuration/system.web/compilation/@debug"
    value="true"
  />

Em qualquer um dos casos, seus arquivos de configuração podem ter os valores de desenvolvedor desejados e funcionarão bem dentro do seu ambiente de desenvolvimento sem interromper seus sistemas de produção. Descobrimos que é menos provável que os desenvolvedores alterem arbitrariamente as variáveis ​​do script de compilação ao testar as coisas; portanto, configurações incorretas acidentais foram mais raras do que com outras técnicas que tentamos, embora ainda seja necessário adicionar cada var no início do processo para que o valor dev não é empurrado para prod por padrão.

jasondoucette
fonte
7

Meu empregador atual resolveu esse problema colocando primeiro o nível do desenvolvedor (depuração, estágio, exibição ao vivo etc.) no arquivo machine.config. Em seguida, eles escreveram o código para pegar isso e usar o arquivo de configuração correto. Isso resolveu o problema com a sequência de conexões incorreta após a implantação do aplicativo.

Recentemente, eles escreveram um serviço web central que envia de volta a string de conexão correta a partir do valor no valor machine.config.

Essa é a melhor solução? Provavelmente não, mas funciona para eles.

Hector Sosa Jr
fonte
1
Na verdade, eu acho isso elegante, porque gosto de manter as várias versões de configuração visíveis em uma solução, mesmo que elas não estejam ativas.
annakata 12/01/09
1
Esta é uma solução muito intrigante. Gostaria de ver um exemplo disso em ação.
Mike K
5

Uma das soluções que me funcionou bem foi usar um WebDeploymentProject. Eu tinha 2/3 arquivos web.config diferentes no meu site e, ao publicar, dependendo do modo de configuração selecionado (release / staging / etc ...), copiava o Web.Release.config e o renomeia para web. config no evento AfterBuild e exclua os que eu não preciso (Web.Staging.config, por exemplo).

<Target Name="AfterBuild">
    <!--Web.config -->
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Release.config" DestinationFiles="$(OutputPath)\Web.config" />
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Staging|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Staging.config" DestinationFiles="$(OutputPath)\Web.config" />
    <!--Delete extra files -->
    <Delete Files="$(OutputPath)\Web.Release.config" />
    <Delete Files="$(OutputPath)\Web.Staging.config" />
    <Delete Files="@(ProjFiles)" />
  </Target>
Adam Vigh
fonte
3

Nosso projeto tem o mesmo problema em que tivemos que manter as configurações para dev, qa, uat e prod. Aqui está o que seguimos (aplica-se apenas se você estiver familiarizado com o MSBuild):

Use o MSBuild com a extensão de tarefas da Comunidade MSBuild. Ele inclui a tarefa 'XmlMassUpdate' que pode 'atualizar em massa' entradas em qualquer arquivo XML depois que você fornece o nó correto para começar.

Implementar:

1) Você precisa ter um arquivo de configuração que terá suas entradas dev env; este é o arquivo de configuração na sua solução.

2) Você precisa ter um arquivo 'Substitutions.xml', que contém apenas as entradas DIFERENTES (principalmente appSettings e ConnectionStrings) para cada ambiente. As entradas que não são alteradas no ambiente não precisam ser colocadas neste arquivo. Eles podem residir no arquivo web.config da solução e não serão afetados pela tarefa

3) No seu arquivo de construção, basta chamar a tarefa de atualização em massa XML e fornecer o ambiente certo como parâmetro.

Veja o exemplo abaixo:

    <!-- Actual Config File -->
    <appSettings>
        <add key="ApplicationName" value="NameInDev"/>
        <add key="ThisDoesNotChange" value="Do not put in substitution file" />
    </appSettings>

    <!-- Substitutions.xml -->
    <configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
      <substitutions>
        <QA>
           <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInQA"/>
           </appSettings>            
        </QA>
        <Prod>
          <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInProd"/>
          </appSettings>            
        </Prod>
     </substitutions>
    </configuration>


<!-- Build.xml file-->

    <Target Name="UpdateConfigSections">
            <XmlMassUpdate ContentFile="Path\of\copy\of\latest web.config" SubstitutionsFile="path\of\substitutionFile" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(Environment)" />
        </Target>

substitua '$ Environment' por 'QA' ou 'Prod' com base no ambiente. você está construindo para. Observe que você deve trabalhar em uma cópia de um arquivo de configuração e não no próprio arquivo de configuração para evitar possíveis erros não recuperáveis.

Basta executar o arquivo de construção e, em seguida, mover o arquivo de configuração atualizado para o seu ambiente de implementação e pronto!

Para uma melhor visão geral, leia isto:

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

Punit Vora
fonte
2

Como você, eu também configurei 'multi' app.config - por exemplo, app.configDEV, app.configTEST, app.config.LOCAL. Vejo algumas das excelentes alternativas sugeridas, mas se você gosta do modo como funciona para você, adiciono o seguinte:

eu tenho um
<appSettings>
<add key = "Env" value = "[Local] "/> para cada aplicativo que adiciono à interface do usuário na barra de título: from ConfigurationManager.AppSettings.Get ("Env");

Acabei de mudar o nome da configuração para a que estou alvejando (eu tenho um projeto com 8 aplicativos com muita configuração de banco de dados / wcf contra 4 pares). Para implantar com o clickonce em cada um, altero 4 seções no projeto e pronto. (isso eu adoraria automatizar)

Meu único objetivo é lembrar de 'limpar tudo' após uma alteração, pois a configuração antiga fica 'presa' após uma renomeação manual. (Que eu acho que irá corrigir o problema de setting.setting).

Acho que isso funciona muito bem (um dia, vou ter tempo de olhar para o MSBuild / NAnt)

Tony Trembath-Drake
fonte
0

Web.config:

O Web.config é necessário quando você deseja hospedar seu aplicativo no IIS. Web.config é um arquivo de configuração obrigatório para o IIS configurar como ele se comportará como um proxy reverso na frente do Kestrel. Você precisa manter um web.config se quiser hospedá-lo no IIS.

AppSetting.json:

Para todo o resto que não se refira ao IIS, use AppSetting.json. AppSetting.json é usado para hospedagem do Asp.Net Core. O ASP.NET Core usa a variável de ambiente "ASPNETCORE_ENVIRONMENT" para determinar o ambiente atual. Por padrão, se você executar o aplicativo sem definir esse valor, ele assumirá o padrão automaticamente para o ambiente de Produção e usará o arquivo "AppSetting.production.json". Quando você depura via Visual Studio, ele define o ambiente como Development, para usar "AppSetting.json". Consulte este site para entender como definir a variável de ambiente de hospedagem no Windows.

App.config:

App.config é outro arquivo de configuração usado pelo .NET, usado principalmente para aplicativos Windows Forms, Windows Services, Console Apps e WPF. Quando você inicia a hospedagem do Asp.Net Core por meio do aplicativo de console app.config também é usado.


TL; DR

A escolha do arquivo de configuração é determinada pelo ambiente de hospedagem escolhido para o serviço. Se você estiver usando o IIS para hospedar seu serviço, use um arquivo Web.config. Se você estiver usando outro ambiente de hospedagem, use um arquivo App.config. Consulte Configurando serviços usando arquivos de configuração e também confira Configuração no ASP.NET Core.

Alper Ebicoglu
fonte
0

Ele diz asp.net acima, então por que não salvar suas configurações no banco de dados e usar um cache personalizado para recuperá-las?

A razão pela qual fizemos isso porque é mais fácil (para nós) atualizar o banco de dados continuamente do que obter permissão para atualizar continuamente os arquivos de produção.

Exemplo de um cache 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<Configurations>(out config))
        {
            config = new Configurations();
            config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings);
            cache.AddToCache<Configurations>(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<Configuration>
    {
        #region CONSTRUCTORS

        public Configurations() : base()
        {
            initialize();
        }
        public Configurations(int capacity) : base(capacity)
        {
            initialize();
        }
        public Configurations(IEnumerable<Configuration> 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);
        }

        /// <summary>
        /// Lists one-to-many records.
        /// </summary>
        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
    }
}
Prisioneiro ZERO
fonte