Digamos que estou criando um aplicativo chamado ConsoleApp2 .
Devido a algumas bibliotecas de terceiros que estou usando, meu arquivo app.config padrão está gerando 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>
Isso é porque minhas referências solução versões diferentes de uma biblioteca, por isso precisa de dizer a todos: " Ei, se você olhar para qualquer OldVersion desta biblioteca, basta usar NewVersion ". E está tudo bem.
O problema é que eu quero definir um arquivo de configuração separado "test.exe.config", onde tenho algumas configurações e me livrar da gerada automaticamente.
Para informar meu aplicativo sobre o novo arquivo de configuração, estou usando um código como
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");
E isso funciona (quase) perfeitamente. E eu escrevi lá " quase ", pois, embora a <appSettings>
seção esteja sendo lida corretamente, a <runtime>
seção não está sendo analisada no meu arquivo de configuração personalizado, mas o App o procura no arquivo de configuração padrão, o que é um problema, já que eu quero poder excluí-lo mais tarde.
Então, como posso dizer ao meu aplicativo para ler também as <runtime>
informações do meu arquivo de configuração personalizado?
Como reproduzir o problema
Um exemplo simples para reproduzir meu problema é o seguinte:
Crie uma biblioteca chamada ClassLibrary2 ( .Net Framework v4.6 ) com uma única classe da seguinte maneira
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; }
}
}
Observe a referência ao pacote Newtonsoft . O instalado na biblioteca é a v10.0.2 .
Agora crie um aplicativo de console chamado ConsoleApp2 ( .Net Framework v4.6 ) com uma classe chamada Program cujo conteúdo é simplesmente o seguinte:
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();
}
}
}
Este aplicativo deve ter instalado também o Newtonsoft , mas em uma versão diferente v12.0.3 .
Crie o aplicativo no modo de depuração. Em seguida, na pasta ConsoleApp2 / ConsoleApp2 / bin / Debug, crie um arquivo chamado test.exe.config com o seguinte conteúdo
<?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>
e observe que nessa mesma pasta Debug também há o arquivo de configuração padrão ConsoleApp2.exe.config com um conteúdo 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>
Se nesse ponto você executar o aplicativo, ele será compilado sem problemas e você deverá ver um console como
Observe que as (3) configurações foram lidas corretamente no meu arquivo de configuração personalizado. Por enquanto, tudo bem...
Agora renomeie o arquivo de configuração padrão para algo como _ConsoleApp2.exe.config e execute novamente o aplicativo. Agora você deve obter uma FileLoadException .
Então, novamente, como posso dizer ao meu aplicativo para ler as <runtime>
informações do meu arquivo de configuração personalizado?
Fundamentação
A razão pela qual estou procurando uma resposta para esta pergunta é a seguinte:
Quando lançamos nosso aplicativo, colocamos todos os arquivos .exe e .dll em uma pasta e nosso arquivo de configuração personalizado (com configurações, etc) em outra, onde nossos clientes têm arquivos semelhantes.
Na pasta com os arquivos .exe e .dll, tentamos manter o mínimo possível, por isso me pediram para encontrar uma maneira de se livrar desse ConsoleApp2.exe.config, se possível. Agora, desde que as ligações mencionadas acima foram escritas nesse arquivo de configuração, tentei mover essas informações para nosso arquivo de configuração personalizado ... mas até agora não consegui: os redirecionamentos de ligação sempre tentam ser lidos a partir desse ConsoleApp2.exe .config , assim que removê-lo, recebo exceções ...
fonte
<runtime>
seção de outra configuração, mas o problema ocorre devido à maneira como você gerencia a configuração comum. Se você gerenciar configurações comuns de uma maneira diferente, esse problema não será mais relevante. Por favor, verifique minha resposta e considere usar transformações de configuração em vez de ajustar o tempo de execução.Respostas:
Você provavelmente está procurando por transformações de configuração :
A idéia por trás disso é que você crie várias configurações no Visual Studio, como Depuração, Lançamento, Produção, Teste ... no gerenciador de configuração e um arquivo de configuração padrão, além das chamadas transformações.
Observe que você pode criar quantas configurações desejar no gerenciador de configuração. Para adicionar novos, clique em Configurações da solução (no menu suspenso "Debug" ou "Release") e selecione "Configuration Manager ...". Abra-o e você verá uma lista de todas as configurações existentes no momento. Desça a caixa de combinação "Configuração da solução ativa" e selecione "
<New...>
" para adicionar mais.Essas transformações especificam o que torna a configuração específica diferente da padrão - portanto, você não precisa repetir o que já especificou na configuração padrão. Em vez disso, basta mencionar as diferenças, por exemplo:
que localiza a configuração relevante por sua chave
ClientSessionTimeout
e define seu valor100
substituindo o valor original no arquivo de configuração (é isso que os atributos adicionais da transformaçãoxdt:Transform="SetAttributes" xdt:Locator="Match(key)"
significam). Você também pode especificar para remover as configurações existentes (especificando emxdt:Transform="Remove"
vez disso), por exemploremoveria um ID de usuário que deveria estar lá apenas para depuração, não para o lançamento (Para saber mais sobre as opções disponíveis, consulte aqui - descrito para Web.config, mas também aplicável para App.config).
Além do
App.Config
arquivo que você tem um arquivo por configuração, ou seja,App.Debug.Config
para a depuração,App.Release.Config
para lançamento etc. Visual Studio ajuda a criá-los.Já criei respostas no StackOverflow aqui e ali , que o descrevem em detalhes, por favor, dê uma olhada.
Se você está tendo problemas para exibi-los no Visual Studio, dê uma olhada aqui .
Em relação à sua justificativa :
As transformações estão criando um arquivo de configuração completo aplicando o arquivo de transformação ao arquivo de configuração padrão. O arquivo resultante é compilado e colocado na pasta "bin" - junto com os outros arquivos compilados. Portanto, se você tiver uma configuração "Release" selecionada, todos os arquivos, incluindo o arquivo de configuração transformado, serão compilados em "bin \ Release".
E o arquivo de configuração é nomeado exatamente como o arquivo exe mais ".config" anexado no final (em outras palavras, não há ".Release.config" na pasta binária, mas um "MySuperCoolApp.exe.config" foi criado - para o aplicativo "MySuperCoolApp.exe").
Da mesma forma, o mesmo acontece com a outra configuração - cada configuração cria uma subpasta dentro de "bin" - se você estiver usando scripts, essa subpasta pode ser referenciada como
$(TargetDir)
em um evento pós-compilação.fonte
Transformação de configuração
Dado o problema ocorrer ao tentar usar outro arquivo de configuração (não nativo), você está tentando encontrar uma solução para substituí-lo 'adequadamente'. Na minha resposta, quero recuar um pouco e focar na razão pela qual você deseja substituí-lo. Com base no que você descreveu na pergunta, você pode definir configurações personalizadas do aplicativo. Se entendi corretamente, você planeja vinculá-lo ao projeto de destino, defina a propriedade 'Copy to output' como 'Always' e a aproximará do aplicativo.
Em vez de copiar o novo arquivo de configuração, existe uma maneira de transformar um existente (nativo), no seu caso -
ConsoleApp2.exe.config
usando transformações Xdt . Para isso, você cria o arquivo de transformação e declara apenas as seções que deseja transformar, por exemplo:Os benefícios dessa abordagem são:
A única desvantagem dessa abordagem é a curva de aprendizado: você precisa aprender sintaxe e saber como colar transformações nas suas configurações no MSBuild.
O .NET Core tem suporte para transformação, aqui está um exemplo de como criar transformações para web.config, mas você pode aplicar transformações a qualquer configuração.
Se você desenvolver aplicativos .NET (não o .NET Core), recomendo consultar o Slowcheetah .
Existem muitos recursos e benefícios úteis de blog sobre transformação, é bastante utilizado. Entre em contato comigo se tiver dificuldades.
Do meu ponto de vista, as transformações de configuração são uma solução certa para atingir seu objetivo, portanto, recomendo fortemente considerá-lo, em vez de ajustar o tempo de execução.
Externalizar seções de configuração
Se você ainda deseja manter o appSettings no local comum, pode externalizar as seções de configuração com o atributo ConfigSource . Verifique este e este tópico para obter detalhes:
A seção AppSettings contém o atributo File que permite mesclar parâmetros de outro arquivo.
Esta opção permite substituir determinadas seções da configuração, mas não todo o conteúdo. Portanto, se você precisar apenas de appSettings, é totalmente aplicável - basta colocar o arquivo de configuração com o appSettings no local comum compartilhado com o arquivo de configuração do usuário e do patch (adicionar
file
ouconfigSource
atributo) para originar esta seção a partir desse local. Se você precisar de mais seções, precisará extraí-las como arquivos separados.fonte
Para funcionar corretamente com
.config
arquivos diferentes , você pode manter o padrão para gerenciar redirecionamentos de espera e outro para os parâmetros do aplicativo. Para fazer isso, mude o app.config padrão em tempo de execução .Você também pode desligar a geração automática de redirecionamento de ligação e usar apenas um arquivo app.config criado manualmente. Há um exemplo aqui: Precisa de uma maneira de fazer referência a 2 versões diferentes da mesma DLL de terceiros
Editar Tendo em conta a justificativa: Se eu entendi, você não deseja o arquivo app.exe.config. Você já consegue colocar e ler o conteúdo personalizado em outro lugar.
Permanece apenas o redirecionamento de ligação.
Você pode se livrar dele gerenciando o redirecionamento de ligação em tempo de execução, como é feito aqui: https://stackoverflow.com/a/32698357/361177 Você também pode recriar um resolvedor de ligação configurável fazendo seu código olhar para o arquivo de configuração.
Meus dois centavos aqui: é viável, mas não acho que valha a pena.
Editar 2 Esta solução parece promissora https://stackoverflow.com/a/28500477/361177
fonte
AppDomain.CurrentDomain.AssemblyResolve
evento e faça com que este método obtenha as regras de ligação do arquivo de configuração.