redwarrior’s diary

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

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 のインストール

.NET Framework システム要件

ちなみに(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プロパティに設定する方法でだいぶ進めてしまったので、手間やわかりやすさを考慮した結果、上記のようになりました。

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

動作環境

Entity Framework で既存DBを使用した時にログ出力されるエラーの解消方法

新年一つ目の記事ということで、あけましておめでとうございます。今年もよろしくお願いいたします。

Entity Framework を使用して開発を進めていく場合に、開発中は Code First を使用してDBを再作成するけれど、 本番環境やステージング環境には既にDBが用意されていることが多いと思います。

その場合、データベースイニシャライザーを変更して、DBを再作成しないようにします。
この状態で公開(発行)すると、テーブル名やカラム名と対応するModelクラスのプロパティ名が一致していれば動作しますが、
ログ出力を設定していると以下のエラーが記録されています。

エラー: オブジェクト名 'dbo.__MigrationHistory' が無効です。

このエラーの解消方法を調べました。

dboスキーマに __MigrationHistory テーブルが無いので作れば良いのかと思ったのですが、わざわざ作る必要はありませんでした。

以下のようにGlobal.asax等で、データベースイニシャライザーに null を設定して、起動時に __MigrationHistory テーブルの存在をチェックに行かないようにすればOKでした。

Database.SetInitializer<DbContextを継承したクラス名>(null);

以上です。

環境
  • Entity Framework 6.1.3

ASP.NET MVC でユーザの氏名(!=アカウント名)を表示する(Windows認証を使用)

ASP.NET MVCWindows認証を使用して、ログインアカウント名(ドメイン名\アカウント名)からユーザーの氏名(フル・ネーム:Full Name)を取得して画面に表示する方法を調べたら、以下のサイトに書いてあった。

stackoverflow.com

上記サイトが見れなくなった時のために、ソースコードを載せておく。

using (var context = new PrincipalContext(ContextType.Domain))
{
    var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
    var firstName = principal.GivenName;
    var lastName = principal.Surname;
}

PrincipalContextクラス、UserPrincipalクラスを使用してアカウント名から、ユーザーの氏名を取得している。

実際に使ってみた注意点としては

  • アセンブリ参照の追加が必要

    Visual Studio上で[参照マネージャー]画面の[アセンブリ] > [フレームワーク]から、System.DirectoryServices.AccountManagementをチェックする。

  • DLLの実行環境へのコピーを設定しておくと良い

    ソリューションエクスプローラーで参照の配下にある System.DirectoryServices.AccountManagement を選択して、プロパティウィンドウの[ローカルにコピー]を True に設定すれば、プロジェクト直下ある Web.config の書き換えが不要になる。
    (設定しない場合は、Web.config を書き換える必要がある)

  • Viewsディレクトリの下にある Web.config で namespace の設定をすれば、cshtml で使用する場合に @using が不要になる。

  • _Layout.cshtml 等のレイアウトに設定することが多いと思うが、毎回呼び出される個所なので、負荷がかからないように対策(キャッシュ等)を行う。