App.Config Преобразование для проектов, которые не являются веб-проектами в Visual Studio?

Для веб-приложения Visual Studio 2010 у нас есть функции преобразования конфигурации, с помощью которых мы можем поддерживать несколько файлов конфигурации для разных сред. Но та же функция недоступна для файлов App.Config для служб Windows/WinForms или консольного приложения.

Здесь предлагается обходной путь, предложенный здесь: Применение магии XDT к App.Config.

Однако это не просто и требует ряда шагов. Есть ли более простой способ добиться того же для файлов app.config?

17 ответов

Решение

Теперь это работает с надстройкой Visual Studio, описанной в этой статье: SlowCheetah - Синтаксис преобразования Web.config теперь обобщен для любого файла конфигурации XML.

Вы можете щелкнуть правой кнопкой мыши на своем файле web.config и нажать "Добавить преобразования конфигурации". Когда вы сделаете это, вы получите web.debug.config и web.release.config. Вы можете создать web.whwhat.config, если хотите, если имя совпадает с профилем конфигурации. Эти файлы - просто изменения, которые вы хотите внести, а не полная копия вашего web.config.

Вы можете подумать, что захотите использовать XSLT для преобразования web.config, но, хотя они кажутся интуитивно правильными, на самом деле они очень многословны.

Вот два преобразования, одно с использованием XSLT, а другое с использованием синтаксиса / пространства имен преобразования документов XML. Как и во всех вещах, в XSLT есть несколько способов сделать это, но вы поймете общую идею. XSLT - это обобщенный язык преобразования деревьев, в то время как этот вариант развертывания оптимизирован для определенного подмножества общих сценариев. Но самое интересное в том, что каждое XDT-преобразование представляет собой плагин.NET, поэтому вы можете создавать свои собственные.

<?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>

Или то же самое с помощью преобразования развертывания:

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

Я попробовал несколько решений, и вот самое простое, которое я лично нашел.
Dan указал в комментариях, что оригинальный пост принадлежит Олегу Сычу - спасибо, Олег!

Вот инструкции:

1. Добавьте файл XML для каждой конфигурации в проект.

Как правило, у вас будет Debug а также Release конфигурации, так что назовите ваши файлы App.Debug.config а также App.Release.config, В моем проекте я создал конфигурацию для каждого типа среды, поэтому вы можете поэкспериментировать с этим.

2. Выгрузите проект и откройте файл.csproj для редактирования.

Visual Studio позволяет редактировать файлы .csproj прямо в редакторе - сначала нужно просто выгрузить проект. Затем щелкните по нему правой кнопкой мыши и выберите " Изменить .csproj".

3. Привязать файлы конфигурации App. *. К основному файлу App.config.

Найдите раздел файла проекта, который содержит все App.config а также App.*.config Рекомендации. Вы заметите, что их действия по сборке установлены на None:

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

Во-первых, установите действие сборки для всех Content,
Затем сделайте все зависящие от конфигурации файлы зависимыми от основного App.config Visual Studio группирует их так же, как это делает дизайнер и файлы с выделенным кодом.

Замените XML выше на приведенный ниже:

<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. Активируйте преобразования магии

В конце файла после

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

и до финала

</Project>

вставьте следующий 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>

Теперь вы можете перезагрузить проект, собрать его и наслаждаться App.config преобразования!

FYI

Убедитесь, что ваш App.*.config файлы имеют правильную настройку, как это:

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

Другое решение, которое я нашел, это НЕ использовать преобразования, а просто иметь отдельный файл конфигурации, например, app.Release.config. Затем добавьте эту строку в ваш файл csproj.

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

Это не только сгенерирует правильный файл myprogram.exe.config, но если вы используете проект установки и развертывания в Visual Studio для создания MSI, это заставит проект развертывания использовать правильный файл конфигурации при упаковке.

Вдохновленный Олегом и другими в этом вопросе, я предпринял решение /questions/21876244/appconfig-preobrazovanie-dlya-proektov-kotoryie-ne-yavlyayutsya-veb-proektami-v-visual-studio/21876262#21876262 на шаг, чтобы включить следующее.

  • Работает с ClickOnce
  • Работает с проектами установки и развертывания в VS 2010
  • Работает с VS2010, 2013, 2015 (не тестировал 2012, хотя должен работать).
  • Работает с Team Build. (Вы должны установить либо A) Visual Studio, либо B) Microsoft.Web.Publishing.targets и Microsoft.Web.Publishing.Tasks.dll)

Это решение работает путем выполнения преобразования app.config до того, как на app.config впервые будет сделана ссылка в процессе MSBuild. Он использует внешний файл целей для более удобного управления несколькими проектами.

Инструкции:

Аналогичные шаги для другого решения. Я процитировал то, что остается тем же самым и включил это для полноты и более простого сравнения.

0. Добавьте новый файл в ваш проект с именем 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. Добавьте файл XML для каждой конфигурации в проект.

Обычно у вас есть конфигурации Debug и Release, поэтому назовите ваши файлы App.Debug.config и App.Release.config. В моем проекте я создал конфигурацию для каждого вида среды, так что вы можете поэкспериментировать с этим.

2. Выгрузите проект и откройте файл.csproj для редактирования.

Visual Studio позволяет редактировать.csproj прямо в редакторе - вам просто нужно сначала выгрузить проект. Затем щелкните по нему правой кнопкой мыши и выберите "Редактировать.csproj".

3. Привязать файлы конфигурации App. *. К основному файлу App.config.

Найдите раздел файла проекта, содержащий все ссылки на App.config и App. *. Config, и замените его следующим образом. Вы заметите, что мы используем None вместо 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. Активируйте преобразования магии

В конце файла после

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

и до финала

</Project>

вставьте следующий XML:

<Import Project="AppConfigTransformation.targets" />

Готово!

По моему опыту, вещи, которые мне нужно сделать для конкретной среды, это такие вещи, как строки подключения, настройки приложений и часто игнорируемые настройки. Система конфигурации позволяет указывать эти вещи в отдельных файлах. Так что вы можете использовать это в вашем app.config/web.config:

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

Обычно я делаю эти специфичные для конфигурации разделы в отдельных файлах, в подпапке с именем ConfigFiles (в зависимости от корня решения или на уровне проекта). Я определяю файл для каждой конфигурации, например, smtp.config.Debug и smtp.config.Release.

Затем вы можете определить событие перед сборкой следующим образом:

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

В групповой разработке вы можете изменить это, включив в соглашение%COMPUTERNAME% и / или%USERNAME%.

Конечно, это подразумевает, что целевые файлы (x.config) НЕ должны помещаться в систему контроля версий (так как они генерируются). Вы все равно должны добавить их в файл проекта и установить для их свойства выходного типа значение "всегда копировать" или "копировать, если новее".

Простой, расширяемый, и он работает для всех типов проектов Visual Studio (консоль, winforms, wpf, web).

Вы можете использовать отдельный файл конфигурации для каждой конфигурации, например, app.Debug.config, app.Release.config, а затем использовать переменную конфигурации в файле проекта:

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

После этого будет создан правильный файл ProjectName.exe.config в зависимости от конфигурации, в которую вы встраиваете.

Я написал хорошее расширение для автоматизации преобразования app.config, подобное встроенному преобразованию конфигурации проекта веб-приложения.

Самое большое преимущество этого расширения в том, что вам не нужно устанавливать его на всех сборочных машинах.

Установите "Средство преобразования конфигурации" в Visual Studio из Marketplace и перезапустите VS. Вы также сможете увидеть преобразование предварительного просмотра меню для app.config.

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

Я наткнулся на следующую статью, которая выглядит немного проще, но я сам не пробовал.

http://fknut.blogspot.com/2009/11/appconfig-transformation-with-new.html

Кроме того, в MS Connect есть запрос на добавление функциональности, который может стоить проголосовать, так что он будет включен во все разделы следующего SP или версии.

https://connect.microsoft.com/VisualStudio/feedback/details/564414

Просто небольшое улучшение в решении, которое, кажется, теперь публикуется везде:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  • то есть, если вы не планируете остаться с текущей версией VS навсегда

В итоге я выбрал немного другой подход. Я выполнил шаги Дэна до шага 3, но добавил еще один файл: App.Base.Config. Этот файл содержит параметры конфигурации, которые вы хотите в каждом сгенерированном App.Config. Затем я использую BeforeBuild (с добавлением Юрия в TransformXml), чтобы преобразовать текущую конфигурацию с базовой конфигурацией в App.config. Затем процесс сборки использует преобразованный файл App.config как обычно. Тем не менее, одна неприятность заключается в том, что вы как бы хотите исключить постоянно изменяющийся App.config из контроля версий впоследствии, но другие файлы конфигурации теперь зависят от него.

  <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>

Я решаю эту проблему с помощью этого инструмента http://ctt.codeplex.com/. Я использую его со скриптом CCNet/nAnt для создания пакетов.

Я создал другую альтернативу той, которую опубликовал Вишал Джоши, где снято требование изменить действие построения на Контент, а также реализована базовая поддержка развертывания ClickOnce. Я говорю простой, потому что я не тестировал его полностью, но он должен работать в типичном сценарии развертывания ClickOnce.

Решение состоит из одного проекта MSBuild, который после импорта в существующий проект приложения Windows (*.csproj) расширяет процесс сборки, чтобы предусматривать преобразование app.config.

Вы можете прочитать более подробное объяснение в Visual Studio App.config XML Transformation, а файл проекта MSBuild можно загрузить с GitHub.

Если вы используете TFS онлайн (облачная версия) и хотите преобразовать App.Config в проект, вы можете сделать следующее без установки каких-либо дополнительных инструментов. Из VS => Выгрузить проект => Редактировать файл проекта => Перейти в конец файла и добавить следующее:

<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 и Destination работают для локального использования и TFS онлайн (Облачный) сервер.

Предлагаемое решение не будет работать, если на ссылку на библиотеку классов с файлом конфигурации ссылается другой проект (в моем случае это была библиотека рабочего проекта Azure). Он не будет копировать правильный преобразованный файл из obj папка в bin\##configuration-name## папка. Чтобы он работал с минимальными изменениями, вам нужно изменить AfterCompile цель для BeforeCompile:

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">

Примечание: Из - за репутации я не могу комментировать bdeem"s пост. Вместо этого я публикую свои выводы в качестве ответа.

После bdeem"сек поста, я сделал следующее (в порядке убывания):

1. Я изменил [project].csprojфайл. Добавил <Content Include="" /> теги к ItemGroup для разных config файлы и сделал их зависимыми от оригинала config файл.

Примечание: использование <None Include="" /> не будет работать с трансформацией.

<!-- App.config Settings -->
<!-- Create App.($Configuration).config files here. -->
<Content Include="App.config" />
<Content Include="App.Debug.config">
  <DependentUpon>App.config</DependentUpon>
</Content>
<Content Include="App.Release.config">
  <DependentUpon>App.config</DependentUpon>
</Content>

2. Внизу [project].csproj файл (до закрытия </Project> тег), я импортировал ${MSBuildToolsPath\Microsoft.CSharp.targets файл, добавил UsingTask чтобы преобразовать XML и добавил Target скопировать преобразованный App.config файл в место вывода.

Обратите внимание Target также перезапишет App.Configв локальном каталоге, чтобы сразу увидеть изменения, работающие локально. В Target также использует Name="Afterbuild"свойство, чтобы файлы конфигурации можно было преобразовать после создания исполняемых файлов. По непонятным мне причинам при использовании конечных точек WCF, если я использую Name="CoreCompile", Я получу предупреждения об атрибутах службы. Name="Afterbuild" решил это.

  <!-- Task to transform the App.config using the App.($Configuration).config file. -->
  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

  <!-- Only compile the App.config if the App.($Configuration).config file exists. -->
  <!-- Make sure to use the AfterBuild name instead of CoreCompile to avoid first time build errors and WCF endpoint errors. -->
  <Target Name="AfterBuild" Condition="exists('App.$(Configuration).config')">
    <!-- Generate transformed App.config in the intermediate output directory -->    
    <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
    
    <!-- Modify the original App.config file with the transformed version. -->
    <TransformXml Source="App.config" Destination="App.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>
</Project>

3. Вернулся в Visual Studio и перезагрузил измененные файлы.

4. Добавил вручную App.*.configфайлы в проект. Это позволило им сгруппироваться под исходным App.config файл.

Примечание. Убедитесь, что App.*.config файлы имеют правильную структуру XML.

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

<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="myConn" connectionString=""; Initial Catalog=; User ID=; Password=;" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
</configuration>

5. Перестроил проект.

Еще один вариант ответа @bdeem с использованием Visual Studio 2019 и 2022. Моя проблема заключалась в том, что при использовании этого решения App.configперезаписывался, и, поскольку он находится в системе управления версиями, это не вариант.

Решение для меня состояло в том, чтобы преобразовать файл конфигурации непосредственно в выходной каталог.

        <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config to the output directory -->
    <TransformXml Source="App.config" Destination="$(OutDir)\$(TargetFileName).config" Transform="App.$(Configuration).config" />
  </Target>

Дополнительным преимуществом этого решения является то, что оно немного короче исходного решения.

Другие вопросы по тегам