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

546

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

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

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

Амитабх
источник
Я наткнулся на следующую статью, которая выглядит немного проще, но я сам не пробовал. fknut.blogspot.com/2009/11/… Кроме того, в MS Connect есть запрос на добавление функции, который может стоить проголосовать, так что он будет включен во все версии следующего SP или версии. connect.microsoft.com/VisualStudio/feedback/details/564414
Ким Р

Ответы:

413

Теперь это работает с надстройкой 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>
Скотт Хансельман
источник
О, это мило! Иметь приложение с многочисленными конфигурационными файлами (log4net, nHibernate, web.config) и помнить о необходимости изменить их все было немного больно. Я тоже не собирался переносить код в CruiseControl.NET, но, похоже, это тоже очень просто.
DilbertDave
10
К вашему сведению, SlowCheetah был фантастическим расширением, которое теперь не будет поддерживаться после VS 2014. Согласно автору, Сайеду Ибрагиму Хашими, sedodream.com/2014/08/11/… .
bdeem
5
@andrewb, я читал это здесь ; однако это было год назад. После пересмотра нити, и читать комментарии, выглядит как кто - то предоставили версию , которая работает с VS2015 здесь .
Анил Натха
2
Прекрасно работает с Visual Studio 2017 и Visual STudio 2019
Гильерме де Хесус Сантос
1
Это здесь и сейчас
Уго Фрейтас
574

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

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

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

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

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

Visual Studio позволяет редактировать файлы .csproj прямо в редакторе - сначала нужно просто выгрузить проект. Затем щелкните по нему правой кнопкой мыши и выберите « Изменить <ProjectName> .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. Активируйте магию преобразований (необходимо только для версий Visual Studio до VS2017 )

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

<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>
Дан Абрамов
источник
4
Огромное спасибо за это! Одно замечание: если вы добавите новые файлы .config в проект после редактирования csproj, они будут сгруппированы в App.config. Я добавил один перед редактированием csproj и, по сути, получил две ссылки на него, одну сгруппированную и одну сольную.
Джефф Свенсен
8
Одна из проблем этого подхода заключается в том, что когда вы просматриваете вкладку «Публикация» в свойствах проекта, а затем нажимаете кнопку «Файлы приложения» ... вы заметите, что app.config, app.Debug.config, app.Release.config должны быть развернуты как часть процесса публикации. Конечно, вы получите правильный файл MyApp.exe.config, но я не хочу, чтобы этот дополнительный багаж был развернут. Должен быть способ сохранить файлы приложения. *. Config в проекте как <Нет> вместо <Содержимое>.
Ли Гриссом
6
Единственная проблема, которую это оставляет для некоторых, - это ответ, первоначально взятый у Олега Сыча, который не затрагивает ключевую часть. Если в вашем отдельном приложении. (Env) .configs вы НЕ перечисляете '<configuration xmlns: xdt = " schemas.microsoft.com/XML-Document-Transform ">' и что-то вроде <appSettings xdt: Transform = "Replace"> или атрибуты, которые делают подобные вещи в строках настройки, это не будет работать. Этот последний бит информации является ключевым, и как только я его добавил, все началось.
djangojazz
24
Вы могли бы заменить v10.0с , v$(VisualStudioVersion)чтобы убедиться , что ваш проект действительно работает со всеми более поздними версиями VS.
Тибо Д.
14
У меня произошла ошибка MSBuild MSB3021: невозможно скопировать файл. Не удалось найти файл obj \ Release \ ConsoleApp.exe во время сборки. Поэтому я немного изменил решение, чтобы повторно использовать целевой раздел <Target Name = "AfterBuild"> вместо создания нового
аналога
137

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

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

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

сельдь
источник
6
Нерассказанные чудеса MSBuild. Теперь интересно, что еще возможно. Btw. это работает также для развертывания clickonce непосредственно из VS (в отличие от ответов с более высоким рейтингом).
Борис Борисович
4
Изменения могут стать обременительными и подверженными ошибкам, если конфиги содержат много записей, которые одинаковы для всех сборок. Сейчас решаем проблему, когда в одной среде .config пропущено изменение, и, конечно, это было производство.
Jeepwran
1
Наличие двух копий конфигурационного файла не является проблемой, если разработчики не поддерживают его вручную.
anIBMer
1
Это красиво, работает как шарм! Я вставил только <AppConfig>App.Release.config</AppConfig>строку в существующее <PropertyGroupусловие для Releaseконфигурации, и IDE показала волнистую линию под <AppConfig>... строкой, говоря, что ее нет в схеме или что-то в этом роде, но я все равно сохранил файл, перезагрузил файл проекта и выполнил сборку в Releaseконфиге и все заработало!
Шива
1
При этом вы потеряете функциональность настроек дизайнера.
Ондржей
33

По моему опыту, вещи, которые мне нужно сделать для конкретной среды, это такие вещи, как строки подключения, настройки приложений и часто игнорируемые настройки. Система конфигурации позволяет указывать эти вещи в отдельных файлах. Таким образом, вы можете использовать это в вашем 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).

jeroenh
источник
У меня точно такая же конфигурация, как и у вас. Но у меня проблемы с преобразованием файла SMTP. Можете ли вы включить оригинал и трансформацию? Это моё: Базовый файл: <?xml version="1.0"?> <smtp deliveryMethod="SpecifiedPickupDirectory"> <specifiedPickupDirectory pickupDirectoryLocation="C:\mail"/> <network host="localhost"/> </smtp> Преобразование:<?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
Я не уверен, что понимаю. В этой конфигурации я ничего не трансформирую, это просто копирование файлов ...
jeroenh
О, я не видел часть копии. Я преобразую конфиг, а не просто копирую его. Спасибо, в любом случае.
Джгарза
Мне нравится это решение. Одно небольшое предложение: в приведенном выше примере копии исходные и целевые аргументы для копирования должны быть заключены в кавычки; в противном случае Pre-Build не удастся для каталогов с пробелом в их имени
vandre
32

Вдохновленный Олегом и другими в этом вопросе, я предпринял решение https://stackoverflow.com/a/5109530/2286801 на шаг вперед, чтобы включить следующее.

  • Работает с 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, и замените его следующим образом. Вы заметите, что мы используем Нет вместо Контента.

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

Выполнено!

bdeem
источник
1
Пробовал в VS Community 2015 RC, и он игнорирует файл app.Debug.config, который у меня есть.
Хайнстар
Я успешно использовал принятый ответ в одном проекте WinForms ... но по какой-то непонятной причине не смог применить принятый ответ. в другой проект WinForms (все в том же решении). Этот ответ от @bdeem - мой новый фаворит - поскольку он правильно взаимодействует с моим проектом MSI - большое спасибо!
bkwdesign
Похоже, это не сработало в VS 2015. Я обновил VisualStudioVersion с 10 до 12, но без кубиков. Любые идеи?
Синастетическая
@Sinaesthetic Можете ли вы дать нам более подробную информацию? VS 2015 Ultimate, Community и т. Д. VB.NET, C #, есть ошибки?
bdeem
VS2015 Предприятие. Никаких ошибок вообще. Это просто ничего не делает.
Синастетик
27

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

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

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

Tevin
источник
Спасибо, я не использовал ваш точный пример, чтобы решить проблему, с которой столкнулся, но ваш пример заставил меня задуматься и привел меня к другому очень похожему решению с помощью задачи «Копировать».
jpierson
Пробовал это в VS 2015 Community RC и он собирает, но затем игнорирует содержимое приложения. *. Config, которое я добавил.
Хайнестар
14

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

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

Голанские Авраам
источник
1
Очень полезное расширение, особенно сейчас, когда медленный гепард входит в режим обслуживания и может не поддерживаться в будущем.
dthrasher
Да, люди должны прекратить медленное использование гепарда в качестве решения этой проблемы, когда эта функция теперь поддерживается задачей msformild transformxml. Архитектор SW из моей команды слишком усердно представил медленного гепарда нашему проекту и создал преобразования отладки, стадии и выпуска всех наших конфигов, большинство из которых не нуждалось в преобразовании. Излишне говорить, что в тот момент, когда он ушел, я вытащил медленного гепарда, и теперь мы просто используем одну задачу transformxml в web.config. Аххххх, простота. Нельзя сказать, что у медленного гепарда не было своего времени и места.
HarryTuttle
5

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

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

Агнель Амодия
источник
1
Это работает отлично и требует очень мало усилий или размышлений. Очень признателен. Спасибо. («Преобразование предварительного просмотра» не работает, но «Добавить преобразования» работает без проблем на VS 2017). Также, кажется, часто получать обновления.
августа
1
Огромное спасибо за решение, за сценой, оно делает именно то, что Дан Абрамов объяснил выше, не пачкая руки
Мухаммед Давуд Ансари
Это окончательное решение. Предварительный просмотр, кажется, прекрасно работает с VS 2019.
Кайл Чемпион
1
Я люблю его, но обнаружил, что он не поддерживает другие файлы не app.config без некоторого редактирования csproj. Все еще здорово, хотя, чтобы увидеть предварительный просмотр, хотя.
Ian1971
4

В итоге я выбрал немного другой подход. Я выполнил шаги Дэна до шага 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>
Waggles
источник
3

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

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  • то есть, если вы не планируете остаться с текущей версией VS навсегда
Юрий Макассюк
источник
Можете ли вы объяснить, что вы ответите немного или дать источники, чтобы объяснить это?
Ройдуккей
4
Не похоже, $(VisualStudioVersion)что установлено при использовании MSBuild напрямую.
Джереми Смит
Это должен быть комментарий к stackoverflow.com/a/5109530/2003763 (я только что добавил ту же информацию, что и комментарий там)
Thibault D.
2

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

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

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

Жоао Анжело
источник
1

Если вы используете 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 (Cloud).

Вениамин
источник
0

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

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">
avs099
источник