読者です 読者をやめる 読者になる 読者になる

redwarrior’s diary

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

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

ASP.NET

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

Web Deployの整理

ASP.NET

ASP.NET MVC 5 の Web Deploy について自分用にまとめたメモ。

対象環境

Windows Server 2008
IIS 7


Web Deployment Agent Service と Web Management Service どちらを使う?(サーバー側)

Web Deployment Agent Service=Remote Agent Service

  • 日本語では「Web 配置エージェントサービス」とか「リモート エージェント サービス」と呼ばれる。
  • Windows Server 2003/IIS 6 は、これ一択。
  • Web Deployのインストール時に、選択する必要あり。
  • 管理者のみ使用可能。

参照URL:Introduction to Web Deploy : The Official Microsoft IIS Site

Web Management Service(WMSVC)=IISの管理サービス

  • 日本語では「Web 管理サービス」「IIS 管理サービス」と呼ばれる。
  • Windows Server 2008/IIS 7 から登場した。
  • サーバーOSに搭載されている。(参照URLより。最新OSは確認していない)
  • 設定を行うことで、管理者以外も使用可能。

参照URL:Visual Studio 2010 [発行]機能で配置可能とするためのサーバー設定 – monoe's blog


Visual StudioWebMatrix どちらを使う?(クライアント側)

Visual Studioで公開する場合

Visual Studioの公開(発行)は、Web Deployment Agent Service、Web Management Service の両方を使用可能。

WebMatrixで公開する場合

WebMatrixのWeb Deployは、Web Management Serviceを使用する。


Web Deployを行うユーザーは、Windowsユーザー(ドメインユーザー)とIISの内部ユーザー(IISマネージャー ユーザー)どちらを使う?

Windows ユーザーを使用する場合

特に追加の設定なし

IIS 内で完結するユーザー(IISマネージャー ユーザー)を使用する場合

IIS マネージャー ユーザー」を選択して、ユーザーを追加する。

参照URL:IIS 7の新しい運用形態 | Think IT(シンクイット)


実際に選択したのは?

サーバー側:Web Management Service

Web Management Serviceの方が新しく、機能も充実しているので、使わない手はない。

クライアント側:Visual Studio

Visual Studioを使用して開発しているため。

Web Deployを行うユーザー:Windowsユーザー(ドメインユーザー)

ドメインユーザー以外は、Web Deployを行わないため。


作業手順

上記条件でWeb Deployを行う場合のサーバー側の作業手順を以下にまとめる。

  1. .NET Framework 4.6のインストール

  2. Web Deploy 3.6のインストール
    リモート エージェント サービスは不要なのでインストールしない。それ以外はインストールする。

  3. サーバーマネージャーを起動し、[サーバーの役割] で [Web サーバー(IIS)] > [管理ツール] > [管理サービス] をインストール

  4. IIS マネージャーを起動し、[管理サービス] で [リモート接続を有効にする] をチェックする。
    Web Deployに必要。有効にしないとローカルコンピューターからしか管理サービスに接続できない。

  5. Web Deploy対象のサイトを右クリックし、[展開] > [Web 配置による発行の有効化] を行う。

    • ユーザーを入力する。プルダウンに無い場合は右側のボタンを押下して選択する。
    • 保存先を指定する。
  6. 保存された設定ファイルは、開発環境でインポート出来る。
    「管理サービスの委任」の設定が行われる。(一部の設定は、「接続の検証」時に行われているかもしれない)

ユーザーのアクセス許可を設定する必要はあるか?

[Web 配置による発行の有効化] で選択したユーザーは設定されているので、新たに設定する必要はない。

管理サービスの委任を設定する必要はあるか?

[Web 配置による発行の有効化] で選択したユーザーは設定されているので、新たに設定する必要はない。

フォルダのアクセス権限を編集する必要はあるか?

[Web 配置による発行の有効化] を行ったサイトのフォルダ(Default Web Siteならば、wwwrootフォルダ)に対して、選択したユーザーにはフルコントロールが与えられるので、新たに設定する必要はない。

参照URL:
Visual StudioのWeb発行がどうしても上手くいきません

MVCはまり事 | Office YUAi サイト


その他・関連用語

Windows Process Activation Service(Windows プロセス アクティブ化サービス:WAS)

WCFでHTTPだけでなく、他プロトコルの通信もサポートするためのもの。Web Deployment Agent Serviceと勘違いしやすい。ポート80をリッスンしている。

World Wide Web Publishing Service(World Wide Web 発行サービス:W3SVC)

IISを起動・停止するためのサービス。Web Management Serviceと勘違いしやすい。

git-flow や GitHub Flow を運用しやすいようにアレンジして使っていました

Git

ちょっと前の開発プロジェクトで使っていたGitを使用したワークフローについて書いてみます。

ざっくり言うと、リポジトリを一つしか使用しない git-flow って感じです。もしくはルールを追加した GitHub Flow でしょうか。

以下、箇条書きで書きます。

  • master, develop, featureブランチを用意します。機能を実装するときは、developerブランチからfeatureブランチ配下に「今日の日付+バックログ項目やタスクのID」という命名でブランチを作成します。

  • featureブランチは実装中、ある程度のまとまりでサーバーにプッシュします。実装完了後は、サーバー上でプルリクエストを作成してレビューを行い、レビューが完了したらdevelopブランチにマージします。

  • リリースを行う時は、最新のdevelopブランチから、release作業用ブランチを作成して、リリース準備作業を行ったらmasterブランチ(とdevelopブランチ)にマージし、masterブランチをサーバーにプッシュしてから、masterブランチにてリリースデプロイを行います。

  • コードレビューは、既に書きましたがfeatureブランチからdeveloperブランチへのマージ時に行います。リリース作業のチェックは、プッシュ後にmasterブランチで確認します。

  • ツールは、git-flow関連はSourceTreeを使っていて、それ以外は必要に応じてコマンドラインなども使用していました。

  • プルリクエストを作成するまでは、featureブランチは担当者専用のブランチなので、ローカルのブランチでコミットの順番を入れ替えたり、集約してコミットログを整理した後、サーバーに強制プッシュすることもOKです。

  • CI/CDを行うとしたら、feature->developへのマージ後や、masterのプッシュ後になると思います。

プロジェクトは変わってしまったのですが、中々良く出来ていると思ったので、覚えている限り書いてみました。

(小ネタ)Microsoft の Markdown ガイダンス

小ネタ etc

正しくは、TFS/VSTS上でMarkdown記法を使用するときのガイダンスだけど。
Markdownを書くときに参考になりそう。いつの間にか用意されていた。

www.visualstudio.com

URLがja-jpなのに、全編英語。まあ、内容は予想がつくのでよしとする。

ちなみに、はてなMarkdownは以下を参照。

はてなで使えるMarkdown記法まとめ - 開発メモ

GitHubMarkdownは以下。

Basic writing and formatting syntax - User Documentation

ELMAH と Glimpse を組み合わせて使用する

ASP.NET

ASP.NETデバッグ作業目的に使用するツールとして、ELMAH と Glimpse がある。

ELMAH に関しては、「プログラミングMicrosoft ASP.NET MVC 第3版ASP.NET MVC 5 対応版」で知りました。

Glimpse については、以下のサイトで知りました。

両方とも便利そうなので、合わせて導入しようとしたら、ちょっとコツが必要だったので記録に残す。

インストール方法

ELMAH

NuGetでELMAH.MVCをインストールする。

Glimpse

NuGetでGlimpse.Mvc5、Glimpse.EF6をインストールする。

ELMAHとGlimpseを組み合わせる

GlimpseからELMAHを参照できるようにするツールをインストールする。具体的には、NuGetでGlimpse.Elmahをインストールする。

この状態で、ELMAHを参照しようとすると以下のJavaScriptエラーがVisual Studio上で発生します。

f:id:redwarrior:20161009200331p:plain

[続行する]をクリックすれば、いちおう画面を見ることは出来ます。また、GlimpseのElmahタブからも概要は見れます。

しかし、やはりエラーメッセージは無くしておきたい。

対処方法

エラー内容からELMAHの画面で発生しているようなので、Glimpseの対象からELMAHを外します。以下のようにurisタグで指定したパスを除外できます。

<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
  <!-- 
        For more information on how to configure Glimpse, please visit http://getglimpse.com/Help/Configuration
        or access {your site}/Glimpse.axd for even more details and a Configuration Tool to support you. 
    -->
  <runtimePolicies>
    <uris>
      <add regex=".*/elmah"/>
    </uris>
  </runtimePolicies>
</glimpse>

これでELMAHのページを直接開いた場合も、GlimpseからELMAHのページを開いた場合もエラーがなくなります。

盲点

ここまで設定して導入したELMAHですが、Glimpseに比べて実はあまり使っていません。

なぜかというと、運用フェイズに入っていないと言うのもありますが、Unity(DIコンテナの方)を使うと、スタックトレースがDI対象のインターフェースまでしか出ないため、YSOD(Yellow Screen of Death)を見てもエラーが特定できないためです。

それでも、いつエラーが起きたかを後から見返せるので、設定しておいて損はないと思います。

動作環境

  • Visual Studio 2015 Professional
  • ASP.NET MVC 5.2.3
  • Entity Framework 6.1.3
  • Elmah.Mvc 2.1.2
  • Glimpse.Mvc5 1.5.3
  • Glimpse.EF6 1.6.5
  • Glimplse.Elmah 1.1.1

AutoMapper は使わない!

ASP.NET .NET

ASP.NET MVC で開発をしている時に気になってくるのが、Entity Framework で作成したエンティティと画面で使用するモデル(ViewModel)の詰め替えをどうするかということ。

少しググってみると、AutoMapper を使用すると良いと出てくる。プロパティを一つずつコピーするのはいかにも冗長な作業でなんとかしたいと思うのはわかるし、一行でプロパティのコピーが出来るのは便利だと思う。ただし、これはやりたくない。

.NET ではなく Java の話だけど、DXO(Data Exchange Object)というものが話題になったことがあった。その時に DBFlute というフレームワークを作っている id:jflute さんの書いていたことが印象に残っている。
DXO を提供するツールを使うことで開発は少し削減されるかもしれないが、エラー発生時にデバッグで悩んでしまえばトータルではマイナスになるという趣旨の内容だったと思う。
その後調べた範囲では DXO を使ったツールはそれほど流行らなかったようで、この主張は的を得ていると思った。

AutoMapper はその時の DXO .NET で実現するものだと思えるので、使わない方が良いと思っている。
しかし、冗長なコードを書くのもまた面倒なのでどうしたものかなと思い、でも、C# には LINQ があるからそんなに冗長にはならないのではないかとも思った。
今回、実際に試してみた結果、想像以上にスッキリと書けて、マジで AutoMapper は使わなくても良いと思えたので、ここに方法を記す。

前置きがものすごい長くなったが、ソースコードで表すとこんな感じ。

モデルクラス(Book.cs)

public class Book
{
    public string Title { get; set; }
    public Author author { get; set; }
}

モデルクラス(Author.cs)

public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

ViewModel(BookViewModel.cs)

public class BookViewModel
{
    public string Title { get; set; }
    public string AuthorName { get; set; }
}

詰め替え処理

//単独オブジェクト
BookViewModel vmodel = BuildViewModel(book);

//コレクション
IEnumerable<BookViewModel> vmodels = books.Select(BuildViewModel); //booksはIEnumerable<Book>

//詰め替えを行うメソッド
private static BookViewModel BuildViewModel(Book book)
{
    return new BookViewModel
    {
        Title = book.Title,
        AuthorName = book.FirstName + " " + book.LastName,
    };
}

staticの詰め替え用のメソッドを用意する必要はあるが、呼び出す側は単独オブジェクトだろうが、コレクションだろうが1行で済む。

プロパティ名の異なるクラス間の変更や、コレクションの変更では、AutoMapperでも対応付けるための記述が必要になるので、AutoMapperを使って手間を省けるのはプロパティが同じクラス間だけなのではと思う。

プロジェクト都合にも寄るのかもしれないけど、デバッグ時のわかりやすさと天秤にかけると、自分はAutoMapperはいらないかなと思った。

ASP.NET MVC でテキストボックスとラベルに別の書式を使用する

ASP.NET

ASP.NET MVCでは、ビューヘルパーを使用することで、テキストボックス等のフォーム部品をシンプルに記述することが出来ますよね。

さらに、***Forメソッドを使用すると、モデルクラスのプロパティを扱うことが出来て、EditorForメソッドやDisplayForメソッドではプロパティに指定した属性によってデフォルトの表示を変更することが出来ます。

例えば、以下のように日付や小数点の書式を指定したりします。(ASP.NET MVC5で確認しました。)

モデルクラス

[DisplayName("公開日")]
[DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)]
public DateTime Published { get; set; }

[DisplayName("小数点を含む数値")]
[DisplayFormat(DataFormatString = "{0:0.000}", ApplyFormatInEditMode = true)]
public decimal Value { get; set; }

ビュー(編集画面)

@Html.EditorFor(model => model.Published)
@Html.EditorFor(model => model.Value)

ビュー(一覧画面)

@Html.DisplayFor(model => model.Published)
@Html.DisplayFor(model => model.Value)

この場合、ApplyFormatInEditModeが true に設定されているので、テキストボックスにも、ラベル(プロパティの値を文字列として出力することをここでは指します)にも同じ書式で表示されます。(公開日:2016/08/22、小数点を含む数値:1089.123)

課題

仕様変更で、テキストボックスとラベルで別の書式を使用することになりました。どうすれば良いでしょう?

ちなみに、以下のようにモデルに複数する方法はDisplayFormat属性が重複するのでダメでした。

[DisplayName("公開日")]
[DisplayFormat(DataFormatString = "{0:yyyy年MM月dd日}", ApplyFormatInEditMode = false)] //表示はyyyy年MM月dd日にしたい
[DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)]
public DateTime Published { get; set; }

[DisplayName("小数点を含む数値")]
[DisplayFormat(DataFormatString = "{0:#,0.000}", ApplyFormatInEditMode = false)] //表示はカンマ区切りにしたい
[DisplayFormat(DataFormatString = "{0:0.000}", ApplyFormatInEditMode = true)]
public decimal Value { get; set; }

対処法

調べた結果、以下の方法で書式を分けることが出来ました。2か所変更します。

  1. モデルのDisplayFormat属性のApplyFormatInEditModeをfalseにする
  2. EditorForメソッドを使用せず、TextBoxForで書式を指定する。

変更後はこうなります。ビュー(一覧画面)に変更はありません。

モデルクラス

[DisplayName("公開日")]
[DisplayFormat(DataFormatString = "{0:yyyy年MM月dd日}", ApplyFormatInEditMode = false)]  //表示はyyyy年MM月dd日にしたい
public DateTime Published { get; set; }

[DisplayName("小数点を含む数値")]
[DisplayFormat(DataFormatString = "{0:#,0.000}", ApplyFormatInEditMode = false)] //表示はカンマ区切りにしたい
public decimal Value { get; set; }

ビュー(編集画面)

@Html.TextBoxFor(model => model.Published, "{0:yyyy/MM/dd}") //入力はyyyy/MM/dd
@Html.TextBoxFor(model => model.Value, "{0:0.000}") //入力はカンマ区切りなし

これでテキストボックスとラベルに別の書式を設定することが出来ました。

応用編

ビューを修正したくない場合は、独自テンプレートを用意することでビューの修正を無しにできます。

DateTime.cshtml

@model DateTime?

@Html.TextBoxFor(model => model, "{0:yyyy/MM/dd}") //入力はyyyy/MM/dd

decimal.cshtml

@model decimal?

@Html.TextBoxFor(model => model, "{0:0.000}") //入力はカンマ区切りなし

大した修正ではないですが、知っておくと地味に役立ちます。