redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

TFS 2015のXAMLビルドでNuGetの復元を行わない設定をしたらビルドが超速く終わるようになった

メモ:TFS 2017は、NuGetサーバーをローカルで建てることが出来るらしいので、そちらを使った方がディスク容量の節約になると思いますし、そちらを使いましょう。

TFS 2015から新しいビルド方式が用意されて、TFS 2017もリリースされ、今後あまり使われなくなっていくと思われるXAMLビルドですが、自動ビルド&デプロイの方法を確立した事もあり、しばらく使い続けることになりそうです。

TFS 2015上のXAMLビルドで、形として残していなかったノウハウがあったので、備忘録として残しておきます。

環境

困っていた事

作成したプロジェクトをTFS上でビルドする時に、NuGetによる復元が毎回発生してしまいビルドが上手くいきません。

Windows Server自体は外部と通信できるが、NuGetクライアントが外部通信するためには設定が必要でした。

NuGetサーバーを内部に建てるという方法もあったと思うが、空いているサーバーもなくやり方がわかりませんでした。

対応した内容(概要)

まず、リポジトリの容量が増えても良いので、プロジェクトに全てのパッケージを含めました。

しかし、NuGetによる復元で外部接続が発生し、タイムアウトするまでプロジェクトの内のパッケージを参照しないため、ビルドに約1時間近くかかってしまっていました。

そこでビルド プロセス テンプレートをカスタマイズして、NuGetによる復元をオフにしたところ、1時間かかっていたビルドを1分に短縮することが出来ました。

ビルド プロセス テンプレートのカスタマイズ方法

ビルド プロセスのカスタマイズ | Microsoft Docs

上記の「ビルド プロセス テンプレートのカスタマイズ」リンク先ページに手順がのっていますが、その通りにはやらずに以下の手順でやりました。

  1. ビルド定義のプロセスメニューから、ビルドプロセステンプレート(GitTemplate.12.xaml)をダウンロード
  2. ダウンロードしたファイルの名前を「GitTemplate.NotRestore.xaml」に変更
  3. プロジェクトの直下に保存して、ソリューションエクスプローラからファイルを追加
  4. 追加したファイルを開くと専用のエディタが開き編集可能になる
  5. エディタ上から「MSBuildの実行」を探す、無い場合は右上の「すべて展開」をクリックすると、表示されるはず。
  6. MSBuildの実行」を選択すると、プロパティウィンドウにプロパティが表示されるのでその中から「RestoreNuGetPackages」を探し、Falseに変更する
  7. 保存してエディタを閉じる

カスタマイズしたテンプレートをビルドに使用する

これで手元での修正が完了しました。次は修正したビルド プロセス テンプレートをTFS上のビルドに使用します。以下の手順でやりました。

  1. 修正したファイルをコミット&プッシュする
  2. ビルド定義のプロセスメニューから、ビルド プロセス ファイルのプルダウンからプッシュしたファイルを選択する(リポジトリサーバー上に無いと表示されない)
  3. 保存する

ビルド完了後に、TFS上からログを参照するとNuGetによる復元が行われていないことが確認できます。

以上です。

IIS上で動くASP.NET (MVC)アプリケーションの権限について

ASP.NET MVC アプリケーションを別サーバーにデプロイして動作確認しようとしたが、DBやフォルダへのアクセス権限でエラーが発生しました。以前に設定したのが少し前で、やり方を忘れていたのと、IIS上でASP.NET MVC アプリケーションの権限について理解があやふやな部分があったので調べました。

ASP.NET MVCでしか確認していないけれど、MVC固有の設定はしていないので ASP.NET上のアプリケーション全般に通じる話かもしれない。

Webアプリケーション、アプリケーションプールについて

そもそもIIS上でのWebアプリケーションやアプリケーションプールとは何なのか?については、以下のサイトに書いてありました。

aspnet.keicode.com

aspnet.keicode.com

メモ

  • IIS上のアプリケーションは、Application Pool単位で実行される。Application Poolは自作も可能(.NET Framework Versionを指定する)
  • IIS上のアプリケーションは、ワーカープロセス(w3wp.exe)で起動し、その設定がApplication Poolとしてまとめられている
  • ワーカープロセスは、Webサイトへのアクセスとは別の権限で動作する

アプリケーションの権限について

では、Application Poolの権限は、どうやって調べるのか?こちらは以下のサイトに書いてありました。

blog.shibayan.jp

IIS のアクセス許可 - クリエイティブWeb

メモ

IIS 7.0(Windows Server 2008)とIIS 7.5(Windows Server 2008 R2)の違い

メモ

IIS 7.0(Windows 2008)
  • Application PoolのIDは、NETWORK SERVICEが既定値
  • 実在のユーザーアカウントではないが、IIS AppPool\(Application Pool名)というIDでアクセス制御をすることが可能
    • アクセス制御は可能だが、ワーカープロセスはNETWORK SERVICEで動作する
    • IIS7.5と同じ動きにしたい場合は、Application PoolのIDをApplicationPoolIdentityにする
IIS 7.5(Windows 2008 R2)
  • Application PoolのIDは、ApplicationPoolIdentityが既定値
  • ApplicationPoolIdentityでは、ワーカープロセスはApplication Poolごとに IIS AppPool\(Application Pool名)という仮想アカウントで動作する
  • 全てのApplication Poolのアカウントは、IIS_IUSRS グループに含まれる

DBやフォルダへの権限設定方法

作成したアプリケーションのDBやフォルダへのアクセス許可等の権限を設定するには、以下のどちらかを設定すれば良いようです。

  1. IIS_IUSRS グループを追加して権限を設定すると、Application Poolを使用するアプリケーションつまり、IIS上の全てのアプリケーションが設定した権限を持つ。
  2. IIS AppPool\(Application Pool名)アカウントを追加して権限を設定すると、対象のApplication Poolを使用するアプリケーションが設定した権限を持つ。

以上です。

補足

アプリケーションからDBへのログインは、Windows認証を使用しています。(Web.configの接続文字列にユーザー名・パスワードを指定しない)

(小ネタ).NET Frameworkの本体と、targeting packは別物

タイトルからして、何当たり前のことを言っているんだ感が全開ですが、今後同じ間違いをしないように書いておきます。

開発環境とネットワークでつながっていない環境に、ASP.NET MVC 5 アプリケーションをデプロイする必要がありました。

デプロイの「発行先を選択」でフォルダを選択して、ローカルPCのパスを指定します。

指定したパスにアプリケーション一式が格納されたフォルダが作成されるので、それを何らかの手段で対象の環境に持っていき、IISのサイトの下にフォルダごと配置します。以下のサイトの内容と大体同じ手順です。

[VisualStudio] 作成したWebプロジェクトをIISに乗せる – .NETちょこっとリファレンス

その後、ページを表示したら、Web.config の以下の部分でエラーが発生。

    <compilation debug="true" targetFramework="4.6" />

これは、.NET 4.6が入っていなさそうだなと思い、確認するとやはり .NET 4.5.2 しか入っていませんでした。

そこで .NET Framework 4.6 Targeting Pack をインストールして確認すると、エラーの内容が以下のように変化しました。

メソッドが見つかりません:'!!0[] System.Array.Empty()'
例外の詳細:System.MissingMethodException:メソッドが見つかりません:'!!0[] System.Array.Empty()'

上記メッセージの後にスタックトレースが続くのですが、それによると発生個所はUnityConfig.cs でした。

さて、勘のいい方ならば、すでにわかったと思いますが、上記のエラーメッセージを元に調べると、.NET 4.6 でビルドしたプログラムを .NET 4.5.2 上で実行しようとした時に発生するエラーでした。

つまり、.NET Framework 4.6 Targeting Pack を入れたのに、.NET Framework 4.6本体を入れていなかったのです。

過去の自分の記事を参考にしたのですが、そちらはそもそもビルドサーバーの話でした。

教訓:公式ドキュメントをちゃんと確認する。

.NET Framework Developer Pack または再頒布可能パッケージをインストールするには | Microsoft Docs

.NET Framework のシステム要件 | Microsoft Docs

ちなみに(1):実行環境には、.NET Framework 4.6 Targeting Pack は不要みたいです。

ちなみに(2):.NET 4.6.1以降の Developer Pack には、.NET Framework本体も同梱されています。

デプロイできたけど実行できない場合の対処方法(とTFS上のデプロイ)

ASP.NET MVC5でWebアプリケーションを作成し、IISへの公開も成功して、さて確認するぞ!という事でアクセスしたらエラーが発生して表示できなかったので、原因を調べました。

動作環境

設定内容

IIS 7では以下の設定が必要です。修正プログラムをインストールすれば不要なようです。また、IIS7.5は最初から不要です。

Web.configの修正

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"> //ここを追加

参考URL: https://support.microsoft.com/ja-jp/help/980368/a-update-is-available-that-enables-certain-iis-7.0-or-iis-7.5-handlers-to-handle-requests-whose-urls-do-not-end-with-a-period

TFS上のデプロイ

TFS上でWeb Deployをする場合は、以下も必要でした。

pubxmlファイルの修正

<AllowUntrustedCertificate>True</AllowUntrustedCertificate> //ここを追加

以上です。

(小ネタ)Table-Per-Hierarchy とは

仕事の都合やら、質の高い内容を書こうとしたせいで、4か月近く更新が出来ていませんでした。今日からリフレッシュして、改めて気負わずに記事を書いていきたいと思います。

Entity Framework 6とEntity Framework Coreの違いを紹介した記事を読んでいて、その中でEntity Framework Coreは、Table-Per-Hierarchy (TPH) のみ対応という記述が出てきた。

Feature Comparison | Microsoft Docs

Table-Per-Hierarchyとは何だろう?と思って調べてみると、書籍「SQLアンチパターン」の5章で紹介されているシングルテーブル継承(Single Table Inheritance)の事だという事がわかった。

Entity Frameworkでサポートしているのは驚きだった。でも、なんでわざわざ別の名前をつけたのだろうか。HibernateJavaのO/Rマッパー)でも同じ名前が使われているみたいだが、新たなパターンかと思ってしまった。

バリデーションのメッセージを変更する

ASP.NET MVCの入力値の検証は便利ですよね。デフォルトでもメッセージの意図は伝わります。
でも、カラムに必ず○○フィールドは~とつくため、これをなくしたい場合もよくあると思うので変更方法を調べました。

ちなみに、良くあるバリデーションのメッセージを手間をかけずに変更したいので、カスタムValidationについては書いていません。

バリデーションエラーのメッセージを変更したい場合

検証に使用している属性の ErrorMessage プロパティにメッセージを設定します。

第3回 モデル・バインドとアノテーション検証の実装 − @IT

日付型や数値型に別の型(文字列)が入力された時のメッセージを変更したい場合

以下のサイトの2番目の回答(29ポイントのやつ)が大事です。 stackoverflow.com

Global.asax の修正とリソースファイルの作成だけで変更が出来ます。
日付型のメッセージを変更したい場合は、FieldMustBeDate をキーとしてリソースファイルに値を設定します。
数値型の場合は、FieldMustBeNumeric がキーです。

※バリデーションのメッセージとは異なる扱いなので注意。

属性による検証のメッセージでリソースファイルを使用したい場合

以下のサイトの回答が参考になります。 stackoverflow.com

なお手元の環境では、App_GlobalResources.MessagesMessages にして、using Resources; を追加する必要がありました。

必須(Required)属性のメッセージを一括変更したい場合

必須属性のメッセージにリソースファイルを使用する場合や、属性に毎回設定を書きたくない場合は、以下のサイトとリンク先を読むと良いです。

d.hatena.ne.jp

内容に従って、クラスを作成すると必須属性からErrorMessageの指定を削ることが出来ます。

実際どうしたか

最後に、バリデーションのメッセージ変更方法はいくつかありますが、実際のプロジェクトではどうしたかを備忘録として書いておきます。

  • 日付/数値型の不一致の場合……リソースファイルを使用
  • それ以外……属性のErrorMessageプロパティで指定

必須(Required)属性のアダプターは、一度作成してみたのですが、それ以外に正規表現(RegularExpression)、メールアドレス(EmailAddress)、URL(Url)、範囲(Range)属性等々も使用しているため、各々の属性についてアダプターを作成する手間がかかります。

また、既にErrorMessageプロパティに設定する方法でだいぶ進めてしまったので、手間やわかりやすさを考慮した結果、上記のようになりました。

落ち着いた段階でリソースファイルを参照するように直していきたいと思います。

動作環境