Скачать исходный код |
При работе с корпоративными проектами нужно создавать приложения разного рода. Например, проект может содержать несколько веб-сайтов, WCF-сервисы, Silverlight-приложения, Windows-сервисы или консольные утилиты. Все эти приложения нужно будет включить в Continuous Integration, чтобы выпуск версий для тестового сервера и боевого производился в автоматическом режиме. У всех этих приложений есть файлы настроек, которые обычно хранятся в App.config или других XML-подобных файлах. Для разных версий приложений мы должны хранить свои строки подключения к БД, ссылки к сторонним сервисам, SMTP-хостам, задавать разные уровни логирования и т.п.
Мы уже рассмотрели, как работать с Web.config, как писать для него трансформацию, что такое трансформация и как она встраивается в процесс сборки проекта. Продолжим эту тему рассмотрением работы с App.config.
Создание App.config для каждой конфигурации
Для примера я создал два проекта: Консольное приложение и WPF приложение. В обоих проектах используются App.config и изначально они выглядят так:
В солюшене по-умолчанию есть две конфигурации и я сразу добавил еще одну (как добавлять конфигурацию мы рассматривали в статье Continuous Integration: Создание собственной конфигурации):
Для каждой конфигурации сборки мы создадим файл трансформации. Первое, что надо сделать - создать 3 файла: App.Debug.config, App.UAT.config, App.Release.config.
Теперь сделаем, чтобы эти файлы конфиугаций красиво отображались в дереве солюшена. Для этого откроем файл проекта в текстом редакторе и добавим в секцию с App.cofing XML-код:
ConsoleApp.csproj:
<ItemGroup> <None Include="App.config" /> <None Include="App.Debug.config"> <DependentUpon>App.config</DependentUpon> </None> <None Include="App.UAT.config"> <DependentUpon>App.config</DependentUpon> </None> <None Include="App.Release.config"> <DependentUpon>App.config</DependentUpon> </None> </ItemGroup>
После обновления файла проекта Visual Studio отобразит наши файлы:
Описываем трансформацию в App.config
Мы создали файл трансформации для каждой конфигурации сборки. Для примера я добавил трансформации ключа в секции appSettings.
App.config:
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="SmtpHost" value="debug-smtphost"/> </appSettings> </configuration>
App.Debug.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add xdt:Transform="Replace" xdt:Locator="Match(key)" key="SmtpHost" value="debug-smtphost"/> </appSettings> </configuration>
App.Release.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add xdt:Transform="Replace" xdt:Locator="Match(key)" key="SmtpHost" value="release-smtphost"/> </appSettings> </configuration>
App.UAT.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add xdt:Transform="Replace" xdt:Locator="Match(key)" key="SmtpHost" value="uat-smtphost"/> </appSettings> </configuration>
Обратите внимание на указание пространства имен: xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"
В примере мы используем Replace для замены всего нода в XML, но кроме полной замены TransformXml поддержиет и другие трасформации. Более подробно о них на MSDN.
Включаем трансформацию App.config
Для того, чтобы описанные трансформации начали применяться, нужно включить их в процесс сборки проекта. В файле проекта добавляем:
ConsoleApp.csproj:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll"/> <Target Name="AfterBuild"> <TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="App.config" StackTrace="true" /> </Target>
Мы использовали элемент UsingTask для подключения задач из сборки Microsoft.Web.Publishing.Tasks.dll. Вы можете скопировать эту сборку и связанные с ней к себе в проект, чтобы указать для подключения локальный путь, а саму сборку добавить в source control.
Переключим конфигурацию сборки в Release и запустим Build проекта. Мы видим, что в файл App.config были подставлены данные из App.Release.config:
Итоги
Включение трансформации для App.config осуществляется в 2 шага: добавить трансформации, включить трансформацию во время сборки проекта. Часть операций приходится делать вручную.
Таким же образом можно включить трансформации для любых XML-файлов.
Статья из серии Continuous Integration: Работа с Config-файлами:
http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5
ОтветитьУдалитьНеплохой экстеншн для автоматического создания файлов для разных конфигураций. А ещё, что более важно, умеет показывать файл после трансформации.
И ещё вы не сталкивались с трансформацией Web.config для локальных проектов? У меня почему-то при запуске сайта через IIS Express трансформации не происходит. (Просто используется файл web.config).
Для добавления новых конфигураций я бы посоветовал использовать
ОтветитьУдалитьSlowCheetah - http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5, а для группировки файлов можно использовать NestIn - https://bitbucket.org/jfromaniello/nestin/
Спасибо за расширения, надо будет посмотреть.
ОтветитьУдалитьПроблем с Express не было, вообще IIS не при чем при трансформации. Видимо в чем-то другом дело.
Да, спасибо, надо будет посмотреть эти расширения.
ОтветитьУдалитьЯ хотел показать как это внутри все работает, чтобы было понимание.
Хм, странно. Вот я создал пустой проект, сделал конфиги:
ОтветитьУдалитьWeb.config:
Web.Debug.config:
Если запустить сайт в студии Ctrl+F5, то ConfigurationManager.AppSettings["Test"] даёт значение "Web.config", т.е. трансформаци не работают =(
Дайте пожалуйста ссылку на код проекта
ОтветитьУдалитьhttp://yadi.sk/d/BPLbCWrq4vHoV
ОтветитьУдалитьЧто делать, если приложение нужно разворачивать на десятки разных сайтов?
ОтветитьУдалитьПервое, что приходит в голову - сделать десяток трансформаций. Но условие можно сделать не по названию конфигурации $(Configuration), а в зависимости от параметра. Например, номера сайта или языка сайта.
ОтветитьУдалитьСпасибо за ссылку, посмотрел код.
ОтветитьУдалитьВ файле проекта на AfterBuild вы не прописали вызов трансформации.
<Target Name="AfterBuild">
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" StackTrace="true" />
</Target>
Пройдите, пожалуйста, по шагам http://blog.byndyu.ru/2013/05/continuous-integration-webconfig.html
О! Большое спасибо. Теперь работает =)
ОтветитьУдалитьУ тебя работает трансформация в ClickOnce приложениях? У меня сама трансформация отрабатывает, но после деплоя приложения - ошибка "Файл, .exe.config, имеет рассчитанный хеш, отличный от указанного в манифесте.". Похоже, что трансформация запускается после того, как хэш конфига был посчитан. Не знаешь, как сделать. чтобы запускалась до?
ОтветитьУдалитьНашел решение. Вот этот VS extension делает правильные изменения в .csproj файле, которые не портят ClickOnce сборку: http://visualstudiogallery.msdn.microsoft.com/579d3a78-3bdd-497c-bc21-aa6e6abbc859
ОтветитьУдалитьО, ты описал проблему и решение :) Спасибо!
ОтветитьУдалитьРешение без использования сторонних расширений:
ОтветитьУдалитьhttp://stackoverflow.com/a/5109530
Сергей, я один в один решение описал.
ОтветитьУдалитьРазличие в последнем шаге. У Вас:
ОтветитьУдалитьи Destination="App.config"
В приведенном варианте:
и
Destination="$(IntermediateOutputPath)$(TargetFileName).config"
У меня VS2010 и Вашем варианте я получаю "Could not write Destination file...". В VS2012 не пробовал.
О, спасибо
ОтветитьУдалитьВидимо в примере кода к этой статье ошибся, в других уже поправил https://github.com/AlexanderByndyu/ContiniousIntegration-WebConfigAppConfigExamples/blob/master/RefactoringConfigExample/ConsoleApp/ConsoleApp.csproj
Да пожалуйста. Спасибо и Вам за отличные статьи!
ОтветитьУдалитьКстати, такая же заковырка в статье про трансформацию Web.config (Destination="Web.config"). В VS2010 получаю ошибку "Could not write Destination file...". Кроме того, в таком подходе при сборке в VS и при наличии TFS нужно постоянно делать Check Out для Web.config. Я нашел только такое решение:
Добавил файлы Site.config, Site.Debug.config, Site.Release.config, а Web.config вынес из под Source Control.
Есть ли лучшее решение?
Тут есть несколько вариантов:
ОтветитьУдалить- сделать основной файл Web.Common.config, а Web.config генерировать
- для Web.config не делать трансформации в Debug и с ним работать на проекте. Остальные варианты сборки перенести на CI (TeamCity, TFS,...), там трансформации и коммитить не надо
Нет проблемы с правами на файл? Например, когда я пытаюсь заменить файл трансформацией вылетает ошибка чтения файла (т.к. к сожалению tfs-2010 все файлы выставляет read-only). Помогает только копирование в файл с новым именем.
ОтветитьУдалитьЕсть такая проблема в TFS, решали так:
ОтветитьУдалить