WebSurfer's Home

Filter by APML

App.Debug.config と App.Release.config を使った構成ファイル書き換え

by WebSurfer 10. November 2024 18:31

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

に書き換えます。

これによりソリューションエクスプローラーで構成ファイルを見た時、下の画像のように階層表示されるようになります。

App.Debug.config と App.Release.config

なお、これをやらなくても、自分が試した限りですが、書き換えには影響ありませんでした。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 フォルダの構成ファイル

この記事の例で、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 という名前で配置するという設定になっています。


もう一つオマケを書きます。

x64 Native Tools Command Prompt を使ってプロジェクトファイルの $(MSBuildExtensionsPath) などの値を調べる方法です。

x64 Native Tools Command Prompt は Visual Studio Installer で「C++ によるデスクトップ開発」のワークロードを追加し、インストールの詳細で「最新の v143 ビルドツール用 C++ ATL」と「最新の v143 ビルドツール用 C++ MFC」にチェックを入れてインストールすれば、Windows の「スタート」メニューに現れるようになります。

x64 Native Tool

そのツールを使ってプロジェクトファイルの $(MSBuildExtensionsPath) などの値を調べるには、プロジェクトファイルに以下の様なコードを追加します。

<Target Name="PrintProperties">
  <Message Text="MSBuildExtensionsPath="$(MSBuildExtensionsPath)""/>
  <Message Text="VisualStudioVersion="$(VisualStudioVersion)""/>
  <Message Text="Configuration="$(Configuration)""/>
  <Message Text="IntermediateOutputPath="$(IntermediateOutputPath)""/>
  <Message Text="TargetFileName="$(TargetFileName)""/>
  <Message Text="AppConfigWithTargetPath="$(AppConfigWithTargetPath)""/>
</Target>

そして x64 Native Tools Command Prompt を起動し、プロジェクトファイルのあるディレクトリに移動してから、msbuild <プロジェクトファイル名> /t:PrintProperties とタイプして実行すれば、以下のように結果が表示されます。

Command Prompt

Tags: , , , ,

.NET Framework

WPF で Chart を表示

by WebSurfer 8. February 2022 12:33

ASP.NET Web アプリで Chart を使う方法を先の記事「Chart (ASP.NET Web Forms 用)」や「MVC で Chart を利用する方法」に書きましたが、それらに表示した棒グラフを同様に Chart を使って WPF アプリで表示する方法を書きます。

WPF アプリに Chart 表示

この記事でプロジェクトの作成に使ったのは Visual Studio Community 2022 のテンプレート「WPF アプリ (.NET Framework)」で、フレームワークは .NET Framework 4.8 です。

プロジェクトを作成したら、ソリューションエクスプローラーの「参照」を左クリック ⇒[参照の追加(R)...]をクリックして表示される「参照マネージャー」」ダイアログで以下の項目を参照に追加します。

  • WindowsFormIntegration
  • System.Windows.Forms
  • System.Windows.Forms.DataVisualization

上の 2 つが WPF アプリで Windows Forms 用のコントロールを使うために必要なもの、最後のものが Chart コントロールを使うためのものです。

次に、自動生成された MainWindow.xaml の XAML のコードに以下の画像の赤枠のコードを追加します。赤枠部分に Windows Froms 用のコントロール(この記事の例では Chart)を配置することができます。

XAML

Microsoft のドキュメント「チュートリアル: WPF での XAML を使用した Windows フォーム コントロールのホスト」のように XAML 上で Windows Forms 用コントロールを配置することができますが、この記事では MainWindow.xaml.cs で動的に Chart コントロールを作って配置することにします。

MainWindow.xaml.cs のコードは以下の通りです。これを実行した結果がこの記事の一番上の画像です。

using System.Windows;
using System.Data;
using System.Windows.Forms.DataVisualization.Charting;
using System.Data.SqlClient;

namespace WpfChart
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly Chart chart;

        public MainWindow()
        {
            InitializeComponent();

            this.chart = new Chart
            {
                Name = "chart1",
                Width = 600,
                DataSource = CreateDataTable().DefaultView
            };

            this.windowsFormsHost.Child = this.chart;

            Legend legend = new Legend()
            {
                DockedToChartArea = "ChartArea1",
                IsDockedInsideChartArea = false,
                Name = "Legend1"
            };

            chart.Legends.Add(legend);

            Series series = new Series()
            {
                Name = "三吉",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "三吉"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "春日",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "春日"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "東雲",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "東雲"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "府中",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "府中"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "広島",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "広島"
            };
            chart.Series.Add(series);

            ChartArea chartArea = new ChartArea()
            {
                Name = "ChartArea1",
                AxisY = new Axis() { Title = "売上高" },
                AxisX = new Axis() { Title = "売上月" }
            };
            chart.ChartAreas.Add(chartArea);
        }

        private DataTable CreateDataTable()
        {
            string connString = @"・・・接続文字列・・・";
            string selectQuery = @"SELECT Month, 
                SUM(CASE WHEN Name = N'三吉' THEN Sales ELSE 0 END) AS 三吉,
                SUM(CASE WHEN Name = N'春日' THEN Sales ELSE 0 END) AS 春日,
                SUM(CASE WHEN Name = N'東雲' THEN Sales ELSE 0 END) AS 東雲,
                SUM(CASE WHEN Name = N'府中' THEN Sales ELSE 0 END) AS 府中,
                SUM(CASE WHEN Name = N'広島' THEN Sales ELSE 0 END) AS 広島
                FROM Shop GROUP BY Month";

            using (var connection = new SqlConnection(connString))
            {
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    var adapter = new SqlDataAdapter(command);
                    var table = new DataTable();
                    adapter.Fill(table);
                    return table;
                }
            }
        }
    }
}

Tags: ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  August 2026  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar