.NET Framework 版の ASP.NET Web アプリは構成ファイルに Web.config を使っており、Visual Studio で開発を行う場合は運用環境にデプロイする際の書き換えを定義した Web.Release.config というファイルを作っておき、Visual Studio の発行ツールを使ってのデプロイ時に自動的に書き換えることが可能になっています。
詳しくはMicrosoft のドキュメント「Visual Studio を使用した ASP.NET Web 配置: Web.config ファイル変換」を見てください。
Windows Forms アプリや WPF アプリにはそのような機能は用意されていません。しかし、ネットの記事などを読むと、ASP.NET Web アプリと同様に、書き換え定義ファイルを作って構成ファイルを書き換えることができるそうです。
という訳で、今さらながらですが、Visual Studio 2022 Community のテンプレートを使って作成したターゲットフレームワーク .NET Framework 4.8 の Windows Forms アプリおよび WPF アプリで試してみました。
(注: .NET をターゲットフレームワークにしたアプリでは App.config ではなくて appsettings.json を使うことが推奨されています。その場合の書き換え方法については先の記事「WinForms アプリで構成情報の上書き (CORE)」を見てください)
Windows Forms アプリ、WPF アプリは App.config を構成ファイルのベースに使います。App.config 自体はアプリが使う構成ファイルではないことに注意してください。書き換えを行わない場合は、App.config の内容がそのままコピーされた <プロジェクト名>.exe.config
という名前のファイルが作成され、それが構成ファイルになります。
App.Debug.config と App.Release.config は、App.config との差分を指定してデバッグ用とリリース用の構成ファイルを作成するための変換ファイルになります。環境(Debug または Release)ごとに App.config の内容を指定された差分に従って書き換えて <プロジェクト名>.exe.config
という名前の構成ファイルを作成し、bin\Debug または bin\Release フォルダに配置するようにします。
以下に手順を書きます。Windows Forms アプリと WPF アプリで同じ手順になります。
(1) App.Debug.config と App.Release.config の追加
App.config は Visual Stidio のテンプレートを使って作成した Windows Forms アプリ、WPF アプリのプロジェクトには最初から含まれています。
それに App.Debug.config と App.Release.config という名前の 2 つのファイルをプロジェクトに追加します。ソリューションエクスプローラーのプロジェクトノードを右クリックし、表示されたコンテキストメニューから[追加(D)]⇒[新しい項目(W)]⇒[アプリケーション構成ファイル]と進んで、App.Debug.config と App.Release.config を追加してください。
追加したらプロジェクトファイルを開いて、
<None Include="App.config" />
<None Include="App.Debug.config" />
<None Include="App.Release.config" />
を
<None Include="App.config" />
<None Include="App.Debug.config">
<DependentUpon>App.config</DependentUpon>
</None>
<None Include="App.Release.config">
<DependentUpon>App.config</DependentUpon>
</None>
に書き換えます。
これによりソリューションエクスプローラーで構成ファイルを見た時、下の画像のように階層表示されるようになります。
なお、これをやらなくても、自分が試した限りですが、書き換えには影響ありませんでした。Visual Studio のソリューションエクスプローラーでの見た目の問題だけなのかもしれません。
(2) App.config の例
今回の例では App.config には以下のように接続文字列と基本情報を配置してみました。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<connectionStrings>
<add name="Database"
connectionString="Data Source=(localdb)\mssqllocaldb;Initial Catalog=Database;Integrated Security=True"
providerName="System.Data.SqlClient" />
<add name="MyDB"
connectionString="Data Source=(localdb)\mssqllocaldb;Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="FilePath" value="C:\Users\surfe\Documents"/>
<add key="FileName" value="original.txt"/>
</appSettings>
</configuration>
(3) App.Debug.config の例
App.Debug.config には App.config の FileName を original.txt から development.txt に変更するための変換定義を書きます。
上にも書きましたが、App.Debug.config と App.Release.config は書き換え方法を指定する XML ファイルです。 変換操作は xdt プレフィックスにマップされる XML-Document-Transform 名前空間で定義されている XML 属性を使用して指定します。
詳しい方法は Microsoft のドキュメント「Web アプリケーション プロジェクト配置の Web.config 変換構文」が参考になると思います。(日本語版は翻訳がアレなので英語版を読むことをお勧めします)
configuration 要素に xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"
という属性を追加し、書き換え方法を指定するコードを追加します。
以下の定義で FileName を original.txt から development.txt に変更します。
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="FileName" value="development.txt"
xdt:Transform="Replace"
xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
(4) App.Release.config の例
App.Release.config には、App.config の接続文字列を本番用に変更し、FileName を release.txt に変更し、さらにリリース版でのみ必要な情報 AdditionalInfo を追加するための変換定義を書きます。
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="Database"
connectionString="Data Source=Release;Initial Catalog=DB1;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
<add name="MyDB"
connectionString="Data Source=Release;Initial Catalog=DB2;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
<appSettings>
<add key="FileName" value="release.txt"
xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="AdditionalInfo" value="release version specific info"
xdt:Transform="Insert"/>
</appSettings>
</configuration>
(5) プロジェクトファイルに書き換え指示を追加
プロジェクトファイルの下の方にある、
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
の下に以下のコードを追加します。追加する場所に制約があるようで、場所を間違えると変換されないので注意してください。
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).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>
上のコードの意味については、この記事の下の方にオマケとして書きましたので興味があれば見てください。
(6) 書き換え結果
以上で、Visual Studio でプロジェクトをビルドすると、構成マネージャーの「構成」(Debug または Release)の設定に応じて、
App.config の内容が、変換ファイル App.Debug.config または App.Release.config に従って書き換えられて <プロジェクト名>.exe.config
という名前の構成ファイルが作成され、bin\Debug または bin\Release フォルダに 配置されます。下の画像はプロジェクト名が WindowsFormsApp3 という名前の Windows Forms アプリのものです。
この記事の例で、bin\Relese フォルダに生成される WindowsFormsApp3.exe.config は以下のようになります。期待通り App.config の内容が App.Release.config に従って書き換えられています。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
<connectionStrings>
<add name="Database"
connectionString="Data Source=Release;Initial Catalog=DB1;Integrated Security=True"
providerName="System.Data.SqlClient"/>
<add name="MyDB"
connectionString="Data Source=Release;Initial Catalog=DB2;Integrated Security=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="FilePath" value="C:\Users\surfe\Documents"/>
<add key="FileName" value="release.txt"/>
<add key="AdditionalInfo" value="release version specific info"/>
</appSettings>
</configuration>
(7) アプリの実行例
もちろんアプリのコードで構成ファイルから取得する情報には上の書き換え結果が反映されます。例えば、Windows Forms アプリの場合、以下のコードを、
using System.Configuration;
using System.Windows.Forms;
namespace WindowsFormsApp3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.label1.Text = ConfigurationManager
.ConnectionStrings["Database"]
.ConnectionString;
this.label2.Text = ConfigurationManager
.ConnectionStrings["MyDB"]
.ConnectionString;
this.label3.Text = ConfigurationManager
.AppSettings["FilePath"];
this.label4.Text = ConfigurationManager
.AppSettings["FileName"];
this.label5.Text = ConfigurationManager
.AppSettings["AdditionalInfo"];
}
}
}
Visual Studio の構成マネージャーの「構成」を Release にして実行すると以下の結果になります。
以下はオマケの情報です。上の「(5) プロジェクトファイルに書き換え指示を追加」に書いたコードについて調べていろいろ分かったことがあったので、備忘録として残しておくことにしました。100% 間違いないところまで深く調べたわけではなく、想像と Copilot に聞いた話が混じっていますが。
以下にコードを再掲して説明を書きます。
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).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>
UsingTask の AssemblyFile 属性には書き換えに使うアセンブリ Microsoft.Web.Publishing.Tasks.dll へのパスが指定されており、それを用いて書き換えを行っています。Web という名前のフォルダにあるということは、もともとは ASP.NET Web アプリの web.config を書き換えるのに用いているもののようです。
Target は、変換ファイル app.$(Configuration).config
が存在する場合はコンパイル後に書き換えを実行するという設定をしています。$(Configuration)
は構成オプションによって Debug または Release になります。
TransformXml は、プロジェクトの App.config ファイルを変換ファイル app.$(Configuration).config
をベースに書き換えて、中間出力ディレクトリ(通常 obj\Debug
または obj\Release
)に $(TargetFileName).config
という名前で保存するという設定になっています。
$(Configuration)
は構成オプションによって Debug または Release になります。$(TargetFileName)
はビルドで生成される exe ファイルの名前で、<プロジェクト名>.exe
となります。
ItemGroup の中の最初の AppConfigWithTargetPath Remove="app.config" は最終出力フォルダ(bin\Debug
または bin\Release
)にオリジナルの App.config が書き込まれないことを確実にするためだそうです。
copilot に聞いた話ですが、AppConfigWithTargetPath は bin\Debug または bin\Release フォルダを意味するわけではないそうです。copilot によると "AppConfigWithTargetPath doesn't directly refer to the bin/Debug or bin/Release folders themselves, but it is related to how the app.config file is processed and used within those output directories." とのことでした。
(自分が AppConfigWithTargetPath Remove="app.config" を削除してビルドして試した限りでは、App.config が bin\Debug または bin\Release に書き込まれることはなかったのですが・・・)
ItemGroup の中の 2 つ目の AppConfigWithTargetPath Include=... は、中間出力ディレクトリに置かれた $(TargetFileName).config
ファイルを、最終出力フォルダ(bin\Debug
または bin\Release
)に <プロジェクト名>.exe.config
という名前で配置するという設定になっています。