ツナ缶雑記

ぐうたらSEのブログです。主にマイクロソフト系技術を中心に扱います。

構成ファイルの書き換えを行うNuGetパッケージの作り方(XML変換編)

やりたいこと

NuGetパッケージをプロジェクトに追加したとき、勝手に構成ファイルが書き換わるパッケージというのが存在するかと思います。 Entity Framework 6.2.0とかもそうで、構成ファイルに設定が入ってきます。 このように、構成ファイルを書き換えるNuGetパッケージの作り方をまとめたいと思います。

実はこのような構成ファイルの書き換えを行う方法は2つあるのですが、今回はもっとも簡単なXML変換という手法について解説します。

なお本稿ではNuGetパッケージの作り方を詳細には解説しません。 作り方が知りたい場合は以下の記事をまずご覧ください。

tsuna-can.hateblo.jp

環境

前提条件

構成ファイルの自動書き換えが行われるのはpackages.configを使っているプロジェクトから今回作成するNuGetパッケージを参照する場合のみです。 PackageReferenceを使っている場合、構成ファイルの書き換えは行われませんので注意してください。

やり方

NuGetパッケージのプロジェクトに構成ファイルの変換設定を追加する

作成するNuGetパッケージ内に「app.config.transform」、「web.config.transform」という名前のファイルを配置しておき、そこにNuGetパッケージの参照を追加した際、構成ファイルに追加する設定を行います。 まずは「app.config.transform」ファイル、「web.config.transform」ファイル(以降、変換設定ファイル)をプロジェクトにXMLファイルとして追加します。

f:id:masatsuna:20190810234123p:plain
変換設定ファイルの追加

この例ではConfigTransformフォルダを作って、その中に変換設定ファイルを配置しています。 この段階では、名前さえ合っていれば、どこにファイルを配置してもかまいません。

続いて作成した変換設定ファイルの中身を編集します。 今回は、このNuGetパッケージを参照した際、構成ファイルのAppSettingsセクションに要素を1つ追加するようにしてみます。 ということで、2つの変換設定ファイルを以下のように設定します。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="test-key" value="test-val"/>
  </appSettings>
</configuration>

このファイルには、このNuGetパッケージを動作させるために必要な設定を書いておくのがよいです。 例えばデータアクセスを行う必要があるなら、その接続文字列の設定例とかがそれにあたります。

追加した変換設定ファイルのビルドアクションを確認する

XMLファイルをプロジェクトに追加すると、通常ビルドアクションは「なし」になりますが、念のためちゃんと確認しておきましょう。 追加した変換設定ファイルを右クリックして[プロパティ]を選択します。

f:id:masatsuna:20190810234534p:plain
ビルドアクションの確認

これが「コンテンツ」になっていると、NuGetパッケージをビルドしたときまったく別の場所に配置されてしまう問題が発生します。 必ず「なし」になっていることを確認します。

*.nuspecファイルに作成した変換設定ファイルの設定を追加する

次に、追加した変換設定ファイルをNuGetパッケージ内に含めるよう、以下のように*.nuspecファイルを編集します。

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <licenseUrl>https://tsuna-can.hateblo.jp/</licenseUrl>
    <projectUrl>https://tsuna-can.hateblo.jp/</projectUrl>
    <iconUrl>https://tsuna-can.hateblo.jp/</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <releaseNotes>初版リリース。</releaseNotes>
    <copyright>$copyright$</copyright>
    <tags>サンプル</tags>
  </metadata>
  <files>
    <file src="ConfigTransform\*.transform" target="content" />
  </files>
</package>

以前の記事で作成した*.nuspecファイルに、files要素を追加しています。 files要素は、NuGetパッケージ内に含めるファイルを追加するための要素です。 ここでは、先ほど作成した変換設定のファイルのパスをワイルドカード文字を使って設定しています。 target属性には、src属性に指定したファイルをNuGetパッケージ内のどこに配置するかを指定します。

変換設定ファイルの配置場所は、NuGetの仕様でcontentディレクトリ内直下、と定められています。 そのため、target属性には「content」を指定し、そのディレクトリ内に変換設定ファイルが配置されるようにしているわけです。

ここまでで設定はすべて完了です。 一旦プロジェクトをReleaseに設定してビルドしておきます。

NuGetパッケージを作成する

続いて、NuGetパッケージの作成を行います。 前回とまったく同じコマンドで、NuGetパッケージを作成できます。

C:\XXXXX\AppSettings.Core>nuget pack -Properties Configuration=Release
'AppSettings.Core.csproj' からパッケージをビルドしています。
MSBuild auto-detection: using msbuild version '16.2.37902.0' from 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin'.
'C:\XXXXX\AppSettings.Core\bin\Release' のファイルをパックしています。
メタデータに 'AppSettings.Core.nuspec' を使用しています。
packages.config が見つかりました。依存関係として登録されているパッケージを使用します
Successfully created package 'C:\XXXXX\AppSettings.Core\AppSettings.Core.1.0.7163.30227.nupkg'.
警告: NU5125: The 'licenseUrl' element will be deprecated. Consider using the 'license' element instead.

特に警告に対する対処も行っていないので、同じ警告が出ています。 が、ここは無視して先に進みます。

NuGetパッケージの中身を確認する

では早速ビルドして出来上がった*.nupkgファイルの中身を確認してみます。 *.nupkgファイルはzip圧縮されているので、拡張子をzipに変更して展開すれば、中身を確認できます。 以下が今回作成した*.nupkgファイルを展開した中身です。

f:id:masatsuna:20190812171138p:plain
nupkgファイルの中身

前回は存在しなかったcontentディレクトリが作成されているのがわかります。 contentディレクトリ内を見てみると、以下のように今回追加した変換設定ファイルが含まれているのがわかります。

f:id:masatsuna:20190812171801p:plain
変換設定ファイル

NuGetパッケージとして追加して動作を確認する

最後に、作成したNuGetパッケージを実際に使って、動作確認を行ってみます。 適当にコンソールアプリケーションプロジェクトとWebアプリケーションプロジェクトを作成して、今回作成したNuGetパッケージを参照に追加してみます。 ローカル環境で動作確認するための方法は、前回記事を参考にしてください。

コンソールアプリケーション(App.config)の場合

Visual Studio.NET Frameworkのコンソールアプリケーションを作成すると、勝手にApp.configファイルが含まれていると思います。 私の環境では以下のようなApp.configファイルが自動的に生成されました。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
</configuration>

今回作成したNuGetパッケージを参照に追加すると、この構成ファイルが以下のように勝手に書き換わります。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <appSettings>
    <add key="test-key" value="test-val" />
  </appSettings>
</configuration>

変換設定ファイルに記載した内容が構成ファイルに対して設定されており、ちゃんと変換設定が行われたことが確認できます。

今回はコンソールアプリケーションプロジェクトに対してNuGetパッケージを追加していますが、Windows Formsや、WPFなど、App.configを使用するプロジェクトでも同様の変換が実行されます。 またApp.configがプロジェクトに存在しない場合は、変換設定ファイルに記載した内容そのままのApp.configがプロジェクトに追加されます。

Webアプリケーション(Web.config)の場合

続いてWeb.configの変換がうまくできるかも確認してみます。 私の環境では、Visual Studioで空の.NET Framewoアプリケーション(.NET Framework)を作成すると、以下のようなWeb.configが生成されました。

<?xml version="1.0" encoding="utf-8"?>

<!--
  ASP.NET アプリケーションの構成方法の詳細については、
  https://go.microsoft.com/fwlink/?LinkId=169433 を参照してください
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.7.2"/>
    <httpRuntime targetFramework="4.7.2"/>
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs"
                type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
                type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>

</configuration>

今回作成したNuGetパッケージを参照に追加すると、この構成ファイルが以下のように勝手に書き換わります。

<?xml version="1.0" encoding="utf-8"?>

<!--
  ASP.NET アプリケーションの構成方法の詳細については、
  https://go.microsoft.com/fwlink/?LinkId=169433 を参照してください
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.7.2" />
    <httpRuntime targetFramework="4.7.2" />
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs"
                type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
                type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>

  <appSettings>
    <add key="test-key" value="test-val" />
  </appSettings>
</configuration>

こちらも変換設定ファイルに記載した内容が構成ファイルに対して設定されており、ちゃんと変換設定が行われたことが確認できます。

NuGetパッケージをアンインストールしたときの挙動

さて、ここまではインストール時に構成ファイルが正しく変換されることを確認してきましたが、今回作成したNuGetパッケージを削除したとき、どのような動作になるのでしょうか。 実は変換設定ファイルにある内容と一致するものについて、NuGetパッケージをアンインストールするとき、構成ファイルから削除してくれます。 例えば先の例で、コンソールアプリケーションのApp.configが書き換わる例について解説しましたが、このプロジェクトからNuGetパッケージをアンインストールすると、以下のように設定が削除されます。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  
</configuration>

変換設定ファイルと構成ファイルが完全一致する場合でも、NuGetパッケージのアンインストール時に、構成ファイル自体の削除は行われません。 変換設定ファイルと完全一致する構成ファイルの場合、以下のような空の構成ファイルが残されます。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  
</configuration>

また以下のように、変換設定ファイルの記載とずれがあるものについては、NuGetパッケージをアンインストールしても、設定は自動的に削除されません。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>

  <appSettings>
    <add key="test-key" value="custom-val" />  <!-- value属性の値を変更している -->
  </appSettings>
</configuration>

こういった細かな制御まで行いたい場合は、XDT変換の機能を活用することで、より詳細な変換を実現することができます。