Представим ситуацию:
В Visual Studio есть решение, которое состоит из 3 проектов. В каждом из проектов в файле app.config
лежит в открытом виде строка подключения базе данных, развернутой в production среде.
Возникла необходимость работать с этим проектом в команде используя VSTS.
Хочу реализовать такой сценарий:
Разработчик синхронизирует себе репозиторий с этим решением, и в файлах app.config
строки подключения нет, он должен добавить туда строки подключения к своей локальной тестовой базе данных.
После отправки кода обратно в репозиторий, в процессе CI и CD в файлы app.config
подставляются нужные строки подключения к рабочей базе данных.
Как можно реализовать это?
В каждом из проектов в файле app.config лежит в открытом виде строка подключения базе данных, развернутой в production среде.
Звучит опасно. Получается, предоставляя разработчику доступ на чтение репозитория, вы сразу доверяете ему доступ к боевой базе данных. Думаю, что доступ к боевой инфраструктуре нужно максимально ограничивать. Значит, из репозитория это нужно убрать.
Предлагаю перенести ключевые параметры подключения (например, логин и пароль, или токен, или что там ещё может быть) из конфига в переменные окружения.
Многие CI-серверы умеют хранить такие переменные и инициализировать ими окружение перед запуском вашего приложения. Точно умеют GitLab CI, Jenkins и Travis (ссылки ведут на документацию по фичам).
Доступ к просмотру и редактированию секретных переменных при этом будет у пользователей с максимальным уровнем доступа.
Программист будет инициализировать окружение сам, можно написать какой-то скрипт ему в помощь.
Как уже написал @NickVolynkin держать строки подключения к БД в App.config
- плохая практика.
Можно создать локальный файл конфигурации, например, ConnectStrings.config
, в котором хранить строки подключения, и которого не будет в репозитории (для каждого разработчика он свой). Также написать класс-обработчик этой конфигурации, и при создании подключения к БД вызывать методы этого класса.
Бонусом ко всему сказанному. Есть возможность трансформировать app.config
(равно как и любой другой файл) при сборке. Такая функциональность есть по умолчанию в проектах WCF сервисов для web.config
, но её же можно добавить в другие типы проектов.
Схема следующая: есть некий дефолтный пустой app.config
, рядом с ним лежат дополнительные файлы с названиями app.Debug.config
, app.Release.config
, app.Test.config
и так далее, которые содержат инструкции для трансформации исходного app.config
. Если предположить, что в CI и CD используются только определённые конфигурации сборки (например, только Release), то можно в app.Release.config
держать нужный набор настроек для работы с продуктивом. Разработчики же свободно могут править app.config
локально и работать с Debug конфигурацией (если разработчикам необходимо работать и с Release, можно добавить новую конфигурацию CIRelease, которая будет использоваться только на билдсервере).
Для использования данного подхода не в WCF проекте, нужно совершить несколько махинаций.
Создаём файл ConfigurationTransform.targets
следующего содержания:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<PropertyGroup>
<AllowedReferenceRelatedFileExtensions>
$(AllowedReferenceRelatedFileExtensions);
.dll.config
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<PropertyGroup>
<ResolveReferencesDependsOn>
TransformConfig;
$(ResolveReferencesDependsOn)
</ResolveReferencesDependsOn>
</PropertyGroup>
<Target Name="TransformConfig" BeforeTargets="_CopyAppConfigFile" Condition="Exists('App.$(Configuration).config')">
<!--Создаём трансофрмированный app config в промежуточной директории.-->
<TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
<!--Сообщаем, что для сборки нужно использовать только что сгенерированный файл.-->
<ItemGroup>
<AppConfigWithTargetPath Remove="App.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
</Project>
Кладём файл в папку решения (уровень .sln
) и подключаем его сразу после Microsoft.CSharp.targets
.
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\ConfigurationTransform.targets" />
Можно в принципе подключать данный таск и напрямую в .csproj
без создания дополнительного файла, но с доп. файлом автоматически решается проблема с наличием нескольких проектов, для которых нужно трансформировать app.config
.
Пример. Исходный app.config
:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<connectionStrings>
<add name="Test" connectionString="" />
</connectionStrings>
</configuration>
Файл трансформации app.Release.config
:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="Test" connectionString="ProductionConnectionString" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
</configuration>
В файле трансформаций используется операция SetAttributes
, которая задаёт атрибуты для узла, который идентифицируется по атрибуту name
(xdt:Locator="Match(name)"
). Другие примеры трансформаций здесь.
Вуаля, при сборке в конфигурации ConfigName
, если рядом с app.config
есть файл app.ConfigName.config
, он будет использован для трансформации и сборка будет проведена с использованием промежуточного трансформированного файла.
Пример таска отсюда.
Еще бонусом небольшой лайфхак. Чтобы файлы трансформации в Solution Explorer не занимали много места, их можно показывать как дочерние от главного app.config
:
Для этого в .csproj
нужно поправить регистрацию файлов трансформации, добавив тэг DependentUpon
:
<ItemGroup>
<None Include="App.config" />
<None Include="App.Release.config">
<DependentUpon>App.config</DependentUpon>
</None>
</ItemGroup>
Строки из app.config
можно вынести в отдельный файл через атрибут configSource
.
В файлах web.config пишется что-то вроде этого:
<connectionStrings configSource="bin\connectionstrings.config" />
В файлах app.config пишем вот так:
<connectionStrings configSource="connectionstrings.config" />
Во все проекты подключаем общий для всех файл connectionstrings.config
как ссылку и настраиваем его копирование при сборке. В файле проекта должно получиться примерно вот это:
<Content Include="..\connectionstrings.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Дальше этот файл можно добавить в .gitignore
, позволив тем самым каждому разработчику иметь свой вариант этого файла. (Только не забудьте положить рядом файл вроде connectionstrings.sample.config
чтобы разработчикам не пришлось каждый раз заполнять его с нуля методом проб и ошибок).
При сборке этот файл надо будет генерировать на стороне сборочного сервера. Также можно при релизной сборке применить трансформацию к конфигу, заменив секцию connectionStrings
целиком.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Добрый день, вот допустим у меня есть код который посылает гет запрос
Пример кода взят из MSDNВ нем показано,как получить сумму ,группируя по одному полю
Всем доброго времениИзучая динамическую компиляцию наткнулся на проблему - при добавлении некоторых using в текст кода компилируемой программы,...
Есть DGV,в которую считывается таблица из бд(с помощью класса linq2sql)Визуально это выглядит вот так: