ASP.NET MVC 13_モデルバインド

VisualStuidioCommunity2015/Fw4.5.2/C#


前回まではモデルの値をビューに表示する方法を見てきました。
今回はビューの値をコントローラーで受け取る方法を見ていきたいと思います。

モデルバインド

モデルバインドとはクライアントからの入力値を、アクションメソッドの引数に自動的に割り当てる機能のことです。
ポストデータやクエリーパラメータ、ルートパラメータなど、リクエストデータと同名の引数(大文字小文字は区別されない)をアクションメソッドに用意しておくだけで、自動的に割り当ててくれます。

まずはモデルバインドの基本的な動作を確認してみます。
以下の例では、ビューに用意したテキストボックス「StringValue」で入力した値が、アクションメソッド「SimpleBind」に用意された同名の引数「stringvalue」にバインドされます。

ビュー
@using (Html.BeginForm("SimpleBind", "ModelBind"))
{
    <dl>
        <dt>@Html.Label("String値:")</dt>
        <dd>@Html.TextBox("StringValue")</dd>
    </dl>
    <div>
        <input type="submit" value="送信" />
    </div>
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {

        public ActionResult Show()
        {
            return View();
        }

        public ActionResult SimpleBind(string stringvalue)
        {
            string value = $"「{stringvalue}」が入力されました。";
            return Content(value);
        }
    }
}



上記の例はアクションメソッドの引数がstring型でしたが、int型に変えてみます。

ビュー
@using (Html.BeginForm("SimpleBind", "ModelBind"))
{
    <dl>
        <dt>@Html.Label("int値:")</dt>
        <dd>@Html.TextBox("IntValue")</dd>
    </dl>
    <div>
        <input type="submit" value="送信" />
    </div>
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {

        public ActionResult Show()
        {
            return View();
        }

        public ActionResult SimpleBind(int intvalue)
        {
            string value = $"「{intvalue}」が入力されました。";
            return Content(value);
        }
    }
}
テキストボックスに何も入力せずに送信したり、数値に変換できない文字を入力して送信すると、「アクションパラメータの引数にnullが設定できないよ」とエラーになってしまいます。

モデルバインダーは、リクエストデータに値が設定されていないときや、アクションメソッドの同名の引数に指定した型に変換できない場合は、引数にnullを設定しようとします。

アクションメソッドの引数が値型の場合、nullを受け入れられるようにnullable型にしておく必要があります。
public ActionResult SimpleBind(int? intvalue)
{
    string value = $"「{intvalue}」が入力されました。";
    return Content(value);
}


アクションメソッドの引数にモデルを指定すると、リクエストデータと同名のプロパティに値をセットしてくれます。
モデルのプロパティが値型の場合、必須でなければnullableにしておきます。

モデル
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class BindSampleViewModel
    {
        [Display(Name = "string値")]
        public string StringValue { get; set; }

        [Display(Name = "int値")]
        public int? IntValue { get; set; }
    }
}
ビュー
@model Practice01_Begin.Models.BindSampleViewModel

@using (Html.BeginForm("BindSampleResult", "ModelBind"))
{
<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.StringValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.StringValue)</dd>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
</dl>
<input type = "submit" value = "送信" />
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {
        public ActionResult BindSample()
        {
            return View();
        }
        public ActionResult BindSampleResult(Models.BindSampleViewModel mdl)
        {
            string value = $"「{mdl.StringValue},{mdl.IntValue}」が入力されました。";
            return Content(value);
        }
    }
}


オーバーポスティング攻撃の対策

アクションメソッドの引数にモデルを指定する場合は「オーバーポスティング攻撃」を受ける可能性が出てきます。
たとえば以下のように、モデルにビューに表示するだけのプロパティ「ImportantValue」があったとします。
using System.ComponentModel.DataAnnotations;

namespace Practice01_Begin.Models
{
    public class BindSampleViewModel
    {
        [Display(Name = "string値")]
        public string StringValue { get; set; }

        [Display(Name = "int値")]
        public int? IntValue { get; set; }

        [Display(Name = "権限")]
        public string Role { get; set; }
    }
}
ビューはStringValueとIntValueのみが編集でき、Roleは表示するだけになっていたとします。
@model Practice01_Begin.Models.BindSampleViewModel

@using (Html.BeginForm("BindSampleResult", "ModelBind"))
{
<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.StringValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.StringValue)</dd>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
    <dd>@Html.DisplayNameFor(mdl => mdl.Role)</dd>
    <dd>@Html.DisplayFor(mdl => mdl.Role)</dd>
</dl>
<input type = "submit" value = "送信" />
コントローラーは先ほどと同じで、引数にモデルを指定します。
ただし、モデルのRoleの値が’admin’であれば、何かしら重要な処理をするとします。
このとき悪意あるユーザーがポストデータのRoleを「admin」に改ざんしたデータを送信すると、意図しない動きになってしまいます。
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {
        public ActionResult BindSample()
        {
            var mdl = new Models.BindSampleViewModel();
            mdl.StringValue = "StringValue";
            mdl.IntValue = 12345;
            mdl.Role = "user";
            return View(mdl);
        }  
       public ActionResult BindSampleResult(Models.BindSampleViewModel mdl)
 {
     if (mdl.Role != null)
     {
         //"重要な処理をする";
     }
     string str = $"「StringValue:={mdl.StringValue},IntValue:={mdl.IntValue}, Role:={mdl.Role}」が入力されました。";
     return Content(str);
     }
 }
}
自動的にバインドしたくないプロパティがある場合は、
「バインドするプロパティを明示的に指定する」か「バインドしないプロパティを指定する」ことでオーバーポスティング攻撃を防御できます。

バインドするプロパティを明示的に指定するには、
コントローラーのアクションメソッドの引数にBind属性をつけ、Includeプロパティにバインドするプロパティ名を設定します。
public ActionResult BindSampleResult
([Bind(Include = "StringValue, IntValue")] Models.BindSampleViewModel mdl)
{
    ・・・略
}
バインドしないプロパティを指定するには、
コントローラーのアクションメソッドの引数にBind属性をつけ、Excludeプロパティにバインドしないプロパティ名を設定します。
public ActionResult BindSampleResult
([Bind(Exclude = "Role")] Models.BindSampleViewModel mdl)
{
    ・・・略
}
ためしにビューでRoleプロパティを入力できるようにして実行してみます。

Bind属性をつけるとRoleには入力値がバインドされていません。

Bind属性を外すとRoleniha入力値がバインドされています。


IncrudeプロパティかExcludeプロパティのどちらを使用すればよいか、参考にしている本「実践プログラミング ASP.NET MVC5」では、原則としてIncludeプロパティを利用すべきとの記載があります。
Excludeプロパティではあとからプロパティを追加した場合、Exclude定義の更新を忘れて意図しないバインドを受け入れてしまう可能性があり
Includeプロパティではあとからプロパティを追加した場合、Include定義の更新を忘れても、値がバインドされていないという明確な結果になり、問題もすぐ特定できるためとありました。

ASP.NET MVC 12_テンプレートヘルパー ~その2
DataType属性、DisplayFormat属性

VisualStuidioCommunity2015/Fw4.5.2/C#


DisplayForヘルパーはモデルの値を表示形式で出力します。
EditorForヘルパーはモデルの値を入力形式で出力します。
モデルの値の型やDataType属性に指定した値により出力形式が変わります。

DataType属性による DisplayForヘルパー/ EditorForヘルパー の出力の違い

前回はモデルの値の型(sring型、int型、long型、decimal型、DateTime型、bool型、bool?型、列挙型)による出力の違いをみました。
今回はDataType属性(名前空間:System.ComponentModel.DataAnnotations)による出力の違いをみたいと思います。

DataType.Text

DisplayForヘルパーでの出力は、モデルの値がそのまま出力されるだけで、DataType属性の影響はありません。
EditorForヘルパーでは、モデルの値がint型の場合は<input type="number" >と出力さるところ、DataType.Text属性を付けると<input type="text" >と出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "int型(DataType.Text)")]
    [DataType(DataType.Text)]
    public int IntValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.IntValue = 123456789;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.IntValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
</dl>
出力
<dl>
    <dt>int型(DataType.Text)</dt>
    <dd>123456789</dd>
    <dd><input class="text-box single-line" data-val="true" 
     data-val-number="フィールド int型(DataType.Text) には数字を指定してください。" 
     data-val-required="int型(DataType.Text) フィールドが必要です。" 
     id="IntValue" name="IntValue" type="text" value="123456789" /></dd>
</dl>

DataType.Html

DisplayForヘルパーでの出力は通常HTMLエンコードされて出力されますが、DataType.Html属性をつけるとエンコードされずに出力されます。
EditorForヘルパーでの出力には、DataType属性の影響はありません。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.Html)")]
    [DataType(DataType.Html)]
    public string HtmlValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.HtmlValue = "<font color='red'>赤字</font>";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.HtmlValue)</dt>   
    <dd>@Html.DisplayFor(mdl => mdl.HtmlValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.HtmlValue)</dd>
</dl>
出力
<dl>
    <dt>string型(DataType.Html)</dt>   
    <dd><font color='red'>赤字</font></dd>
    <dd><input class="text-box single-line" id="HtmlValue" name="HtmlValue" type="text" 
        value="&lt;font color=&#39;red&#39;&t;赤字&lt;/font&gt;" /></dd>
</dl>

DataType.MultilineText

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーでの出力では、テキストエリアが出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.MultilineText)")]
    [DataType(DataType.MultilineText)]
    public string MultilineTextValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.MultilineTextValue = "1行目\r\n2行目";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.MultilineTextValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.MultilineTextValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.MultilineTextValue)</dd>
</dl>

出力
<dl>
<dt>string型(DataType.MultilineText)</dt>
        <dd>1行目
            2行目</dd>
        <dd><textarea class="text-box multi-line" id="MultilineTextValue" 
        name="MultilineTextValue">
        1行目
        2行目</textarea></dd>
</dl>

DataType.EmailAddress

DisplayForヘルパーではメールリンク(<a href="mailto:~">)が出力されます。
EditorForヘルパーではテキストボックス(<input type="email" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
 [Display(Name = "string型(DataType.EmailAddress)")]
 [DataType(DataType.EmailAddress)]
 public string EmailAddressValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.EmailAddressValue = "aaa@gmail.com";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.MultilineTextValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.MultilineTextValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.MultilineTextValue)</dd>
</dl>

出力
<dl>
    <dt>string型(DataType.EmailAddress)</dt>
    <dd><a href="mailto:aaa@gmail.com">aaa@gmail.com</a></dd>
    <dd><input class="text-box single-line" id="EmailAddressValue" 
        name="EmailAddressValue" type="email" value="aaa@gmail.com" /></dd>
</dl>

DataType.Url

DisplayForヘルパーではハイパーリンク(<a href="~">)が出力されます。
EditorForヘルパーではテキストボックス(<input type="url" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.Url)")]
    [DataType(DataType.Url)]
    public string UrlValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.UrlValue = "https://www.google.co.jp";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.UrlValue)</dt>
        <dd>@Html.DisplayFor(mdl => mdl.UrlValue)</dd>
        <dd>@Html.EditorFor(mdl => mdl.UrlValue)</dd>
</dl>

出力
<dl>
<dt>string型(DataType.Url)</dt>
        <dd><a href="https://www.google.co.jp">https://www.google.co.jp</a></dd>
        <dd><input class="text-box single-line" id="UrlValue" name="UrlValue" type="url" value="https://www.google.co.jp" /></dd>
</dl>

DataType.Password

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーではテキストボックス(<input type="password" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
        [Display(Name = "string型(DataType.Password)")]
        [DataType(DataType.Password )]
        public string PasswordValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.PasswordValue  = "ABCD123";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.PasswordValue)</dt>
        <dd>@Html.DisplayFor(mdl => mdl.PasswordValue)</dd>
        <dd>@Html.EditorFor(mdl => mdl.PasswordValue)</dd>
</dl>

出力
<dl>
 <dt>string型(DataType.Password)</dt>
        <dd>ABCD123</dd>
        <dd><input class="text-box single-line password" id="PasswordValue" name="PasswordValue" type="password" value="ABCD123" /></dd>
</dl>

DataType.PhoneNumber

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーではテキストボックス(<input type="tel" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
        [Display(Name = "string型(DataType.PhoneNumber)")]
        [DataType(DataType.PhoneNumber )]
        public string PhoneNumberValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.PhoneNumberValue  = "09012345678";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.PhoneNumberValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.PhoneNumberValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.PhoneNumberValue)</dd>
</dl>

出力
<dl>
    <dt>string型(DataType.PhoneNumber)</dt>
    <dd>09012345678</dd>
    <dd><input class="text-box single-line" id="PhoneNumberValue" name="PhoneNumberValue" type="tel" value="09012345678" /></dd>
</dl>

DataType.DateTime、DataType.Date、DataType.Time

DisplayForヘルパーではDataType.DateTimeでは年月日時分秒が出力され、DataType.Dateでは年月日が、DataType.Timeでは時分が出力されます。
EditorForヘルパーではそれぞれにテキストボックス(<input type="datetime" >、<input type="date" >、<input type="time" >)が出力されます。

DataType.Date、DateType.TimeではEditorForで値が出力されていません。
後述のDataFormat属性をつけると出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [DataType(DataType.DateTime)]
    public DateTime DateTimeValue { get; set; }

    [Display(Name = "DateTime(DataType.Date)")]
    [DataType(DataType.Date)]
    public DateTime DateValue { get; set; }

    [Display(Name = "DateTime(DataType.Time)")]
    [DataType(DataType.Time)]
    public DateTime TimeValue { get; set; }}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.DateTimeValue = DateTime.Now;
    mdl.DateValue = DateTime.Now;
    mdl.TimeValue = DateTime.Now;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.DateTimeValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.DateTimeValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.DateTimeValue)</dd>

    <dt>@Html.DisplayNameFor(mdl => mdl.DateValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.DateValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.DateValue)</dd>

    <dt>@Html.DisplayNameFor(mdl => mdl.TimeValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.TimeValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.TimeValue)</dd>
</dl>

出力
<dl>
    <dt>DateTime(DataType.DateTme)</dt>
    <dd>2016/12/23 6:11:56</dd>
    <dd><input class="text-box single-line" data-val="true" 
        data-val-date="フィールド DateTime(DataType.DateTme) は日付である必要があります。" 
        data-val-required="DateTime(DataType.DateTme) フィールドが必要です。" 
        id="DateTimeValue" name="DateTimeValue" type="datetime" value="2016/12/23 6:11:56" /></dd>

    <dt>DateTime(DataType.Date)</dt>
    <dd>2016/12/23</dd>
    <dd><input class="text-box single-line" 
        data-val="true" data-val-date="フィールド DateTime(DataType.Date) は日付である必要があります。" 
        data-val-required="DateTime(DataType.Date) フィールドが必要です。" 
        id="DateValue" name="DateValue" type="date" value="2016/12/23" /></dd>

    <dt>DateTime(DataType.Time)</dt>
    <dd>6:11</dd>
    <dd><input class="text-box single-line" 
        data-val="true" data-val-required="DateTime(DataType.Time) フィールドが必要です。" 
        id="TimeValue" name="TimeValue" type="time" value="6:11" /></dd>
</dl>

先ほどDataType属性でDataType.DateやDateType.Timeを指定すると、EditorForで出力されたテキストボックスに値が出力されていませんでした。
これはDataFormat属性(名前空間:System.ComponentModel.DataAnnotations)で書式設定方法を指定すると出力されるようになります。


DisplayFormat属性のDataFormatStringプロパティに書式文字列を設定します。
ApplyFormatInEditModeプロパティには編集時にも書式を適用する場合にtrueを設定します。
DisplayFormat属性には他にもプロパティがあります。(後述のまとめ参照)

しかし、DateType.Dateの場合は編集時の書式が「yyyy-MM-dd」ではなく「yyyy/MM/dd」になっています。
ためしに書式を「{0:yyyy年MM月dd日}」としたところ、やはり値が出力されませんでした。
chrome(バージョン:55.0.2883.87)では<intput type="date" >の要素にフォーカスすると、カレンダーが表示されますが、おそらくこのカレンダーが「/」しか対応していないのではないでしょうか。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [DataType(DataType.DateTime)]
    [DisplayFormat(DataFormatString = "{0:yy年MM月dd日}", ApplyFormatInEditMode = true)]
    public DateTime DateTimeValue { get; set; }

    [Display(Name = "DateTime(DataType.Date)")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime DateValue { get; set; }

    [Display(Name = "DateTime(DataType.Time)")]
    [DataType(DataType.Time)]
    [DisplayFormat(DataFormatString = "{0:HH:mm}", ApplyFormatInEditMode = true)]
    public DateTime TimeValue { get; set; }
}

DataType.Currency

DataType.Currencyの場合、カレントカルチャ情報に合わせた出力がされます。
DisplayForヘルパーでは、カレントカルチャの通貨情報でフォーマットされ出力されました。
EditorForヘルパーではカレントカルチャの数値情報でフォーマットされ、テキストボックス(<input type="text" >)で出力されました。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "Decimal(DataType.Currency)")]
    [DataType(DataType.Currency)]
    public Decimal CurrencyValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.CurrencyValue = 12345678.5678m;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.CurrencyValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.CurrencyValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.CurrencyValue)</dd>
</dl>

出力
<dl>
    <dt>Decimal(DataType.Currency)</dt>
    <dd>¥12,345,679</dd>
    <dd><input class="text-box single-line" data-val="true" 
        data-val-number="フィールド Decimal(DataType.Currency) には数字を指定してください。" 
        data-val-required="Decimal(DataType.Currency) フィールドが必要です。" 
        id="CurrencyValue" name="CurrencyValue" type="text" value="12345678.57" /></dd>
</dl>



まとめ

主なDataType属性
DataTypeDisplaryForヘルパーEditorForヘルパー
DataType.Textテキストボックス
<input type="text" >
DataType.Html値をエンコードせずに出力するテキストボックス
<input type="text" >
DataType.MultilineTextテキストエリア
<textarea>
DataType.EmailAddressメールリンク
<a href="mailto:~">
テキストボックス
<input type="email" >
DataType.Urlハイパーリンク
<a href="~">
テキストボックス
<input type="url" >
DataType.Passwordテキストボックス
<input type="password" >
DataType.PhoneNumber テキストボックス
<input type="tel" >
DataType.DateTime年月日時分秒が出力されるテキストボックス
<input type="datetime" >
DataType.Date年月日が出力されるテキストボックス
<input type="date" >
DataType.Time時分が出力されるテキストボックス
<input type="time" >
DataType.Currencyカレントカルチャの金額情報でフォーマットされ出力されるカレントカルチャの数値情報でフォーマットされ出力される
<input type="time" >

DisplayFormat属性
DateFormatString書式文字列
ApplyFormatInEditMode編集時にも書式を適用するかどうか
ConvertEmptyStringToNull空文字列をnullに変換するかどうか
NullDisplayText値がnullの時に表示するテキスト

ASP.NET MVC 12_テンプレートヘルパー ~その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回までは個別のHTMLヘルパーを見てきました。
今回はテンプレートヘルパーを見て行きます。

テンプレートヘルパーは、引数に指定したモデルのプロパティの型やその属性から自動的にHTML要素を生成してくれます。
たとえばstring型であればinput要素を、bool型であればcheckbox要素を生成するといった具合です。

DisplayNameForヘルパー

DisplayNameForヘルパーは、モデルのプロパティの表示名をhtmlタグで修飾せずに出力します。
属性を指定しない場合、プロパティ名をそのまま出力します。
DisplayName属性を指定した場合、指定した値を出力します。
Display属性のnameプロパティを指定した場合も、指定した値を出力します。

モデル
//DisplayName属性を使用する場合にインポートする
using System.ComponentModel;
//Display属性を使用する場合にインポートする
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class TemplateHelperViewModel
    {
        public string Text1 { get; set; }

        [DisplayName("テキスト2")]
        public string Text2 { get; set; }

        [Display(Name = "テキスト3")]
        public string Text3 { get; set; }
    }
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class TemplateHelperController : Controller
    {
        public ActionResult Index()
        {
            var mdl = new Models.TemplateHelperViewModel();
            return View(mdl);
        }
    }
}
ビュー
@model Practice.Models.TemplateHelperViewModel

@Html.DisplayNameFor(mdl => mdl.Text1)
@Html.DisplayNameFor(mdl => mdl.Text2)
@Html.DisplayNameFor(mdl => mdl.Text3)

DisplayName属性(名前空間:System.ComponentModel)は、表示名しか指定できませんが、
Display属性(名前空間:System.ComponentModel.DataAnnotations)は、表示名以外にも表示方法をカスタマイズするためのプロパティがあります。
たとえば表示名をリソースファイルから出力するなど、Display属性でなければできない事がありますので、より汎用的なDisplay属性を使用する方がよさそうです。

DisplayForヘルパー/ EditorForヘルパー

DisplayForヘルパーはモデルの値を表示形式で出力します。
EditorForヘルパーはモデルの値を入力形式で出力します。
モデルの値の型やDataType属性(名前空間:System.ComponentModel.DataAnnotations)に指定した値により出力形式が変わります。

モデルの型による出力

まずはモデルの値の型によってどのような出力がされるか見ていきます。
string型、int型、long型、decimal型、DateTime型、bool型、bool?型(Nullable)、列挙型についてみてみます。

モデル
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{    
    public enum WeweathereType
    {
        [Display(Name = "晴れ")]
        sunny,
        [Display(Name = "曇")]
        cloudy,
        [Display(Name = "雨")]
        rainy
    }
    
    public class DisplayForViewModel
    {
        [Display(Name ="String型")]
        public string StringValue { get; set; }

        [Display(Name = "int型")]
        public int IntValue { get; set; }

        [Display(Name = "long型")]
        public long LongValue { get; set; }

        [Display(Name = "decimal型")]
        public decimal DecimalValue { get; set; }

        [Display(Name = "DateTime型")]
        public DateTime DateTimeValue { get; set; }

        [Display(Name = "bool型")]
        public bool BoolValue { get; set; }

        [Display(Name = "bool(Nullable)型")]
        public bool? NullableBoolValue { get; set; }

        [Display(Name = "enum型")]
        public WeweathereType EnumValue { get; set; }  
    }
}
コントローラー
using System;
using System.Collections.Generic;
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class TemplateHelperController : Controller
    {
        public ActionResult Index()
        {
            var mdl = new Models.DisplayForViewModel();
            mdl.StringValue = "赤字";
            mdl.IntValue = 123456789;
            mdl.LongValue = 123456789012345;
            mdl.DecimalValue = 12345.99999m;
            mdl.DateTimeValue = new DateTime(2017,01,01);
            mdl.BoolValue = true;
            mdl.NullableBoolValue = true;
            mdl.EnumValue = Models.WeweathereType.rainy;
            return View(mdl);
        }
    }
}
ビュー
@model Practice.Models.Index

@Html.DisplayNameFor(mdl => mdl.StringValue)
@Html.DisplayFor(mdl => mdl.StringValue)
@Html.EditorFor(mdl => mdl.StringValue)
@Html.DisplayNameFor(mdl => mdl.IntValue)
@Html.DisplayFor(mdl => mdl.IntValue)
@Html.EditorFor(mdl => mdl.IntValue)
@Html.DisplayNameFor(mdl => mdl.LongValue)
@Html.DisplayFor(mdl => mdl.LongValue)
@Html.EditorFor(mdl => mdl.LongValue)
@Html.DisplayNameFor(mdl => mdl.DecimalValue)
@Html.DisplayFor(mdl => mdl.DecimalValue)
@Html.EditorFor(mdl => mdl.DecimalValue)
@Html.DisplayNameFor(mdl => mdl.DateTimeValue)
@Html.DisplayFor(mdl => mdl.DateTimeValue)
@Html.EditorFor(mdl => mdl.DateTimeValue)
@Html.DisplayNameFor(mdl => mdl.BoolValue)
@Html.DisplayFor(mdl => mdl.BoolValue)
@Html.EditorFor(mdl => mdl.BoolValue)
@Html.DisplayNameFor(mdl => mdl.NullableBoolValue)
@Html.DisplayFor(mdl => mdl.NullableBoolValue)
@Html.EditorFor(mdl => mdl.NullableBoolValue)
@Html.DisplayNameFor(mdl => mdl.EnumValue)
@Html.DisplayFor(mdl => mdl.EnumValue)
@Html.EditorFor(mdl => mdl.EnumValue)
出力

データ型による出力の違い
データ型DisplayForヘルパーの出力EditorForヘルパーの出力
string型htmlエンコードされて出力されるテキストボックス
<input type="text" >
int型テキストボックス
<input type="number" >
long型テキストボックス
<input type="number" >
decimal型値が丸められて出力されるテキストボックス
<input type="text" >
値が丸められて出力される。
DateTime型テキストボックス
<input type="datetime" >
bool型無効なチェックボックス
<input type="checkbox" disabled="disabled" >
hidden要素は出力されない
チェックボックス型
<input type="checkbox" >
Hidden要素(<input type="hidden" >)が出力される。
bool?型(Nullable)無効なドロップダウン
<select disabled="disabled">
ドロップダウン
<select>
列挙型テキストボックス
<input type="text" >
decimal型は予想に反してtype="text"として出力されました。
bool型はチェックボックスとして、Nullableなbool型はドロップダウンとして出力されます。
enum型はテキストボックスとして表示されましたが、コチラではドロップダウンとして表示されると記載されています。


<dl>
<dt>String型</dt>
        <dd><font color='red'>赤字</font></dd>
        <dd><input class="text-box single-line" id="StringValue" name="StringValue" type="text" 
         value="<font color='red'>赤字</font>" /></dd>
<dt>int型</dd>
        <dd>123456789</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド int型 には数字を指定してください。" 
         data-val-required="int型 フィールドが必要です。" id="IntValue" name="IntValue" 
         type="number" value="123456789" /></dd>
<dt>long型</dd>
        <dd>123456789012345</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド long型 には数字を指定してください。" 
         data-val-required="long型 フィールドが必要です。" 
         id="LongValue" name="LongValue" type="number" value="123456789012345" /></dd>
<dt>decimal型</dd>
        <dd>12346.00</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド decimal型 には数字を指定してください。" 
         data-val-required="decimal型 フィールドが必要です。" 
         id="DecimalValue" name="DecimalValue" type="text" value="12346.00" /></dd>
<dt>DateTime型</dd>
        <dd>2017/01/01 0:00:00</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-date="フィールド DateTime型 は日付である必要があります。" 
         data-val-required="DateTime型 フィールドが必要です。" id="DateTimeValue" 
         name="DateTimeValue" type="datetime" value="2017/01/01 0:00:00" /></dd>
<dt>bool型</dd>
        <dd><input checked="checked" class="check-box" disabled="disabled" 
         type="checkbox" /></dd>
        <dd><input checked="checked" class="check-box" data-val="true" 
         data-val-required="bool型 フィールドが必要です。" id="BoolValue" name="BoolValue" 
         type="checkbox" value="true" />
         <input name="BoolValue" type="hidden" value="false" /></dd>
<dt>bool(Nullable)型</dd>
        <dd><select class="tri-state list-box" disabled="disabled">
         <option value="">設定なし</option>
         <option selected="selected" value="true">True</option>
         <option value="false">False</option>
         </select>
        </dd>
        <dd><select class="list-box tri-state" id="NullableBoolValue" name="NullableBoolValue">
         <option value="">設定なし</option>
   <option selected="selected" value="true">True</option>
   <option value="false">False</option>
   </select>
  </dd>
<dt>enum型</dd>
        <dd>rainy</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-required="enum型 フィールドが必要です。" id="EnumValue" name="EnumValue" 
         type="text" value="rainy" /></dd>
</dl>

ASP.NET aspxのインライン式

覚えられないので自分用にメモ
詳しくはhttps://support.microsoft.com/ja-jp/kb/976112
<% ~ %> コード ブロックを埋め込む ASPとの後方互換性を保持するための埋め込みコードブロック
<%= ~ %>式を表示 Response.Write(...) で代用できる埋め込みコードブロック。
文字列などを表示するもっとも簡単な方法。
<%:= ~ %>式を表示(HTMLエンコード付
<%@ ~ %>ディレクティブ ページの設定を行う aspxページの設定を指定する構文
<%# ~ %>データバインディング式 RepeaterコントロールなどでDataBindしている場合に使用する。
<%# Eval("hoge") %>
<%$ ~ %>式ビルダー アプリケーション構成ファイルやリソース ファイルに含まれる情報に基づいて、コントロールのプロパティの値を設定する。
<%-- ~ --%>サーバー側コメント ブロック

ASP.NET MVC 12_HTMLヘルパー ~その4~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • DropDownList/ DropDownListFor
  • ListBox / ListBoxFor
  • EnumDropDownListFor

入力要素のレンダリング


DropDownList/ DropDownListFor

まずはDropDownListです。

ドロップダウンリストの選択肢リストの作成にはIEnumerable<SelectListItem>を使用する方法とSelectListを使用する方法があります。
下記のサンプルはIEnumerable<SelectListItem>を使用した方法です。
選択肢の作成と同時に選択状態を表すselectedプロパティを指定します。
ビューで選択肢リストを作成していますが、コントローラーで作成しViewBag経由で引き渡してもいいですし、ビューモデル経由で引き渡してもいいです。
@{ 
    var items= new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" , Selected = true},
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
DropDownList:
@Html.DropDownList("DropDownListID", items)
DropDownList(selectListitem)の出力:
<select id="DropDownListID" name="DropDownListID">
<option value="1">日曜日</option>
<option value="2">月曜日</option>
<option selected="selected" value="3">火曜日</option>
<option value="4">水曜日</option>
<option value="5">木曜日</option>
<option value="6">金曜日</option>
<option value="7">土曜日</option>
</select>
次にSelectListを使用する方法です。
SelectListは1つの項目を選択できる一覧を表すクラスです。
IEnumerable<SelectListItem>を実装しています。

まず選択肢リストになるIEnumerableなリストを作成します。
そして選択肢リスト、選択値用フィールド名、表示用フィールド名、および選択値を指定して、SelectListインスタンスを作ります。
DropDownListメソッドには作成したSelectListを指定します。
@{

    var items = new List<KeyValuePair<string,string>>()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };

    var list = new SelectList(items,    //選択肢リスト
                        "Key",          //Value値に指定するプロパティ名
                        "Value",        //Text値に指定するプロパティ名
                        "4");           //選択値
}
DropDownList(SelectList):
@Html.DropDownList("DropDownListID", list)

続いてDropDownListForです。
こちらはIEnumerable<SelectListItem>を使用しても、選択値はモデルから設定できます。
@{
    var items = new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" },
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
DropDownListFor:
@Html.DropDownListFor(mdl => mdl.DropDownValue, items)
SelectListを使用した方法です。
@{ 
    var items = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string,string>("1", "日曜日"),
                new KeyValuePair<string,string>("2", "月曜日"),
                new KeyValuePair<string,string>("3", "火曜日"),
                new KeyValuePair<string,string>("4", "水曜日"),
                new KeyValuePair<string,string>("5", "木曜日"),
                new KeyValuePair<string,string>("6", "金曜日"),
                new KeyValuePair<string,string>("7", "土曜日"),
            };

    var list = new SelectList(items,"Key","Value"); 
}
@Html.DropDownListFor(mdl => mdl.DropDownValue, list)

ListBox/ ListBoxFor

複数選択できるリストボックスです。
ドロップダウンリストの選択肢リストの作成にはIEnumerable<SelectListItem>を使用する方法とMultiSelectListを使用する方法があります。

まずはListBoxです。
下記のサンプルはIEnumerable<SelectListItem>を使用した方法です。
@{ 
    var items= new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" , Selected = true},
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" , Selected = true},
        };
}
ListBox:
@Html.ListBox("ListBoxID", items)
ListBoxの出力:
<select id="ListBoxID" multiple="multiple" name="ListBoxID">
<option value="1">日曜日</option>
<option value="2">月曜日</option>
<option selected="selected" value="3">火曜日</option>
<option value="4">水曜日</option>
<option value="5">木曜日</option>
<option selected="selected" value="6">金曜日</option>
<option value="7">土曜日</option>
</select>
次にMultiSelectListを使用する方法です。
MultiSelectListは複数の項目を選択できる一覧を表すクラスです。
IEnumerable<SelectListItem>を実装しています。
{
    var items = new List<KeyValuePair<string, string>>()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };
    var selectedItems = new String[] { "3", "6" };

    var list= new MultiSelectList (items, "Key", "Value", selectedItems);
}
ListBox:
@Html.ListBox("ListBoxID", list)
つづいてListBoxForです。
IEnumerable<SelectListItem>を使用する例です。
ビューモデル
public class HtmlHelperViewModels
{
    public string[] ListBoxValues { get; set; }
}
コントローラー
public class HtmlHelperController : Controller
{
    public ActionResult Index()
    {
        var mdl = new Models.HtmlHelperViewModels();
        mdl.ListBoxValues = new string[] { "3", "6" };
        return View(mdl);
    }
}
ビュー
@{
    var items = new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" },
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
ListBoxFor:
@Html.ListBoxFor(mdl => mdl.ListBoxValues, items)
次にMultiSelectListを使用する方法です。
@{
    var items = new List<KeyValuePair<string, string<<()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };
    var selectedItems = new String[] { "3", "6" };
    var list = new MultiSelectList(items, "Key", "Value", selectedItems);
}
ListBoxFor(SelectList):
@Html.ListBoxFor(mdl => mdl.ListBoxValues , list)

EnumDropDownListFor

ASP.NET MVC 5.1 からは列挙帯からDropDownListを生成することができます。
モデル
namespace Practice.Models
{
    public class HtmlHelperViewModels
    {
        public WeekdayType EnumValue { get; set; }
    }

    public enum WeekdayType
    {
        Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
    };
}
コントローラー
namespace Practice.Controllers
{
    public class HtmlHelperController : Controller
    {

        public ActionResult Index()
        {
            Models.HtmlHelperViewModels mdl = new Models.HtmlHelperViewModels();
            mdl.EnumValue = Models.WeekdayType.Thursday;
            return View(mdl);
        }
    }
}
ビュー
@Html.EnumDropDownListFor(mdl => mdl.EnumValue)
EnumDropDownListForの出力: 
<select data-val="true" data-val-required="EnumValue フィールドが必要です。" id="EnumValue" name="EnumValue">
<option value="0">Monday</option>
<option value="1">Tuesday</option>
<option value="2">Wednesday</option>
<option selected="selected" value="3">Thursday</option>
<option value="4">Friday</option>
<option value="5">Saturday</option>
<option value="6">Sunday</option>
</select>

EnumHelper.GetSelectListメソッドを使用すると、列挙帯からIList<SelectListItem>オブジェクトを生成することもできます。
@{ 
    IList<SelectListItem> enumList = 
            EnumHelper.GetSelectList(typeof(Practice.Models.WeekdayType));       
}
DropDownListFor:
@Html.DropDownListFor(mdl => mdl.EnumDropDownValue, enumList)

DropDownList:
@Html.DropDownList("EnumDropDownListID", new SelectList(enumList,"Value","Text","2"))
DropDownListの表示する値を列挙子の名前ではなく、他の表示名にしたい場合は、列挙子にDisplay属性で表示名を指定します。
using System.ComponentModel.DataAnnotations;
・・・省略・・・
public enum WeekdayType
{
    [Display(Name = "月曜")]
    Monday,
    [Display(Name = "火曜")]
    Tuesday,
    [Display(Name = "水曜")]
    Wednesday,
    [Display(Name = "木曜")]
    Thursday,
    [Display(Name = "金曜")]
    Friday,
    [Display(Name = "土曜")]
    Saturday,
    [Display(Name = "日曜")]
    Sunday
};

ASP.NET MVC 12_HTMLヘルパー ~その3~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • RadioButton/ RadioButtonFor
  • CheckBox/ CheckBoxFor

入力要素のレンダリング


RadioButton/ RadioButtonFor

ラジオボタンのテキストは<label>タグで設定し、<label>タグでRadioButtonまたはRadioButtonForを囲みます。
ラジオボタンの選択肢の数だけ、<label>タグとRadioButtonまたはRadioButtonForを用意します。
RadioButtonメソッドは第一引数nameに指定するフィールド名をすべて同じ値にします。
RadioButtonForメソッドは第一引数に指定するモデルのプロパティをすべて同じにします。
RadioButton:



RadioButtonFor:


RadioButtonの出力:
<label><input id="RadioButtonID" name="RadioButtonID" type="radio" value="RadioValueA" /> RadioA</label>
<label><input checked="checked" id="RadioButtonID" name="RadioButtonID" type="radio" value="RadioValueB" /> RadioB</label>

RadioButtonForの出力:
<label><input id="RadioValue" name="RadioValue" type="radio" value="RadioValueA" /> RadioA</label>
<label><input checked="checked" id="RadioValue" name="RadioValue" type="radio" value="RadioValueB" /> RadioB</label>
出力されたhtmlを見るとname属性とid属性がすべて同じ値になっています。
idを分けたい場合は属性で指定します。
RadioButton:



RadioButtonFor:



CheckBox/ CheckBoxFor

チェックボックスのテキストは<label>タグで設定し、<label>タグでCheckBoxまたはCheckBoxForを囲みます。
チェックボックスの選択肢の数だけ、<label>タグとCheckBoxまたはCheckBoxForを用意します。
ラジオボタンと異なり、 CheckBoxメソッドは第一引数nameに指定するフィールド名をそれぞれ別の値にします。
CheckBoxForメソッドは第一引数に指定するモデルのプロパティはそれぞれのチェック状態を表すモデルのプロパティにします。
CheckBox:




CheckBoxFor:



CheckBoxの出力(Aだけ記載):
<label>
<input checked="checked" id="CheckBoxID_A" name="CheckBoxID_A" type="checkbox" value="true" />
<input name="CheckBoxID_A" type="hidden" value="false" />
 CheckA</label>

CheckBoxForの出力(Aだけ記載):
<label>
<input checked="checked" data-val="true" data-val-required="CheckBoxValueA フィールドが必要です。" 
id="CheckBoxValueA" name="CheckBoxValueA" type="checkbox" value="true" />
<input name="CheckBoxValueA" type="hidden" value="false" />
 CheckA</label>
出力内容を確認すると<input type="checked">と<input type="hidden">の2つの要素が出力されています。
チェックボックスがチェックされなかった時にも、チェックされなかったというfalse値をサーバーに送信するためです。
※CheckBoxForには「data-val-required」属性が出力されていますが、今は無視します。


ラジオボタンやチェックボックスの選択肢が多い場合、選択肢の数だけタグを書くのはメンドクサイですね。
Asp Web Form にあったCheckBoxListやRadioButtonListが欲しいところです。
毎回RadioButtonのidを属性で指定しなければいけないのもメンドクサイです。
そんなときは自作のHtmlヘルパーを作って対応します。
いずれ自作してみたいと思いますが、今は無視します。

ASP.NET MVC 11_HTMLヘルパー ~その2~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • Label / LabelFor
  • TextBox / TextBoxFor
  • TextArea / TextAreaFor
  • Password / PasswordFor
  • Hidden/ HiddenFor

入力要素のレンダリング

TextBodForやLabelForなどの「For付き」メソッドはモデル関連付いたフォーム要素を生成します。
TextBoxやLabelなどの「Forなし」メソッドはモデルに関連付かないフォーム要素を生成します。

属性にreadonlyやclassなどのC#の予約語を使用する場合は、「@」を付けます。

Label / LabelFor

Labelメソッドは引数に指定した文字列を出力します。
LabelForは引数に指定したモデルのプロパティ名を表示します。
モデルのプロパティの値を表示するわけではありませんので注意してください。

コントローラー
public ActionResult Index()
{
    Models.HtmlHelperViewModels mdl = new Models.HtmlHelperViewModels();
    mdl.LabelText = "LabelTextValue";
    return View(mdl);        
}
ビュー
Label:
@Html.Label("LabelTextValue")

LabelFor:
@Html.LabelFor(mdl => mdl.LabelText)
Labelの出力:
<label for="LabelTextValue">LabelTextValue</label>

LabelForの出力:
値「LabelTextValue」ではなくプロパティ名「Labeltext」が出力されている
<label for="LabelText">LabelText</label>

LabelForで表示するプロパティ名はDisplay属性やDisplayName属性を使用してカスタマイズすることができます。
//DisplayName属性を使用する場合にインポートする
using System.ComponentModel;
//Display属性を使用する場合にインポートする
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class HtmlHelperViewModels
    {
        [DisplayName("表示名1")]
        public string LabelText1{ get; set; }

        [Display(Name = "表示名")]
        public string LabelText2{ get; set; }
    }
}

モデルのプロパティの値をラベルで出力したい場合は、Labelメソッドを使用します。
Label:
@Html.Label(Model.LabelText)

TextBox / TextBoxFor

TextBox:
@Html.TextBox("TextBoxID", "TextBoxValue", new { @readonly = "readonly", size = "20", maxlength = 40 })

TextBoxFor:
@Html.TextBoxFor(mdl => mdl.TextBoxText, new { @readonly = "readonly", size = "20", maxlength = 40 })
TextBoxの出力:
<input id="TextBoxID" maxlength="40" name="TextBoxID" readonly="readonly" size="20" type="text" value="TextBoxValue" />

TextBoxForの出力:
<input id="TextBoxText" maxlength="40" name="TextBoxText" readonly="readonly" size="20" type="text" value="TextBoxTextValue" />

TextArea / TextAreaFor

TextArea:
@Html.TextArea("TextAreaID",                    //要素名
                "TextAreaValue",                //値
                5,                              //行数
                50,                             //桁数
                new { @class = "multiline" }    //属性
                )

TextAreaFor:
@Html.TextAreaFor(mdl => mdl.TextAreaText, 5, 50, new { @class = "multiline" })
TextAreaの出力:
<textarea class="multiline" cols="50" id="TextAreaID" name="TextAreaID" rows="5">
TextAreaValue</textarea>

TextAreaForの出力:
<textarea class="multiline" cols="50" id="TextAreaText" name="TextAreaText" rows="5">
TextAreaTextValue</textarea>

Password / PasswordFor

Password:
@Html.Password("PasswordID", "PasswordText", new { size = 10, maxlength = 20 })

PasswordFor:
@Html.PasswordFor(mdl => mdl.PasswordText, new { size = 10, maxlength = 20 })
Passwordの出力:
<input id="PasswordID" maxlength="20" name="PasswordID" size="10" type="password" value="PasswordText" />

PasswordForの出力:
<input id="PasswordText" maxlength="20" name="PasswordText" size="10" type="password" />
PasswordForの出力を見るとValueプロパティが設定されていません。
ASP.NET Web Formでも同様でしたがPasswordテキストボックスには値が表示できません。
値を表示させるにはvalue属性を使用しますが、ソースの表示でパスワードの値が丸見えになることに注意してください。
@Html.PasswordFor(mdl => mdl.PasswordText, new { size = 10, maxlength = 20, value = Model.PasswordText })
<input id="PasswordText" maxlength="20" name="PasswordText" size="10" type="password" value="PasswordTextValue" />

Hidden / HiddenFor

Hidden:
@Html.Hidden("HiddenID", "HiddenValue")

HiddenFor:
@Html.HiddenFor(mdl => mdl.HiddenValue)
Hiddenの出力:
<input id="HiddenID" name="HiddenID" type="hidden" value="HiddenValue" />

HiddenForの出力:
<input id="HiddenValue" name="HiddenValue" type="hidden" value="HiddenValue" />

ASP.NET MVC 10_HTMLヘルパー ~その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


ASP.NET Web Formsではサーバーコントロールがありました。
ASP.NET MVCではサーバーコントロールにかわって、HTMLヘルパーを使用しコントロールをレンダリングします。

基本的なHTMLヘルパー

  • BeginForm / BeginRouteForm
  • EndForm
  • Label / LabelFor
  • TextBox / TextBoxFor
  • TextArea / TextAreaFor
  • Password / PasswordFor
  • Hidden/ HiddenFor
  • RadioButton/ RadioButtonFor
  • CheckBox/ CheckBoxFor
  • DropDownList/ DropDownListFor
  • ListBox / ListBoxFor
  • EnumDropDownListFor

HTMLフォームのレンダリング

BeginForm 、EndForm

BeginFormメソッドは<form>タグを生成します。
EndFormメソッドは</form>タグを生成します。
@Html.BegionForm()
    ・・・フォームの内容(省略)
@Html.EndForm()
BeginFormメソッドはusing構文を使用することもできます。
using構文を使用すると、ブロックの終了で</form>タグを生成します。
@using (Html.BegionForm())
{
    ・・・フォームの内容(省略)
}
BeginFormメソッドの引数をまったく指定しない場合、現在のアクションメソッドにPostします。
BeginFormメソッドにはいろいろなオーバーロードが用意されています。
主な引数を指定した場合の例です。
@{
    //アクション名
    string actionName = "Edit";
    //コントローラー名
    string controllerName = "HtmlHelper";
    //アクションメソッドへの引数
    object routeValues = new { id ="1",otherParam ="xxx"};
    //HTTPメソッド(GET/POST)
    FormMethod frmMethod = FormMethod.Post;
}
   
@using (Html.BeginForm(actionName, controllerName, routeValues, frmMethod))
{
    ・・・フォームの内容(省略)
}
「<form action="/helper/HtmlHelper/Edit/1?otherParam=xxx" method="post">」と生成されます。


BeginRouteForm

BeginRouteFormメソッドを指定すると、ルート定義ファイルで設定されているルート名へ送信するフォームを生成できます。
ルート定義についてはコチラ ASP.NET MVC 04_ルーティングの基礎

ルート定義ファイルの内容です。
App_Start/RouteConfig.cs
routes.MapRoute(
    name: "HtmlHelper",
    url: "helper/sample/{controller}/{action}/{id}",
    defaults: new { controller = "HtmlHelperSample", action = "Edit", id = UrlParameter.Optional }
);
以下の例では、ルート名「HtmlHelper」、コントローラー「現在のコントローラー名」、アクション「現在のアクションメソッド名」に送信されるformタグが生成されます。
ルート定義のデフォルトコントローラーのデフォルトアクションではありません。
現在HelloControllerのWorldアクションを実行していれば、
「<form action="/helper/sample/Hello/World" method="post">」と生成されます。
@using (Html.BegionRouteForm("HtmlHelper"))
{
    ・・・フォームの内容(省略)
]

コントローラ名やアクションを指定する場合、匿名型のオブジェクトとして指定します。
@{ 
    //ルート名
    string routeName = "HtmlHelperSample";
    //ルートパラメータ
    object routeValues = new
    {
        Controller = "HtmlHelperSample",
        Action = "Edit",
        id = 1,
        otherParam = "xxx"
    };
    //HTTPメソッド(GET/POST)
    FormMethod frmMethod = FormMethod.Post;
}

@using (Html.BeginRouteForm(routeName, routeValues, frmMethod))
{
    ・・・フォームの内容(省略)
}
「<form action="/helper/sample/HtmlHelperSample/Edit/1?otherParam=xxx" method="post"> 」と生成されます。

ASP.NET MVC 09_Razorビューエンジン ~Razor構文 その2~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いてRazorの構文を見ていきます。

「@using」で名前空間をインポート

Razorは標準でだいたいの名前空間をインポートしてくれますが、インポートされない名前空間は「@using」でインポートすることができます。
@using MyApp.MyExtensions;

@{ 
    bool ret = string.Empty.MyExtensionMethod();
}

「@if」コードブロックで条件分岐

コードブロック内ではifを使用できますが、コードブロック外でifを使用したい場合は「@if」コードブロックを使用します。
@{ int num = new System.Random().Next(1, 10); }

@if (num % 2 == 0)
{
    @:偶数
}
else
{
    @:奇数
}

「@switch」コードブロックで条件分岐

@switch (num)
{
    case 2:
        @: numは2です。
        break;
    default:
        @: numは2以外です。
        break;
}

「@for」コードブロックで繰り返し処理

@{ int max = 10; }
@for (int i = 0; i < max; i++)
{
    

@i

}

「@foreach」コードブロックで繰り返し処理

@{ int[] nums = { 2, 4, 6 }; } 
@foreach (int val in nums)
{
    

@val

}

「@while」コードブロックで繰り返し処理

@{ int counter = 1; }
@while (counter < 10 )
{
    

@counter

counter++; }

「@functions」コードブロックでメソッドやプロパティを定義

「@functions」コードブロック内ではメソッドやプロパティを定義できます。
@functions
{
    // メソッド定義
    bool isEven(int num)
    {
        bool ret = false;
        if (num % 2 == 0)
        {
            ret = true;
        }
        return ret;
    }

    //プロパティ定義
    string _str = "Hello";
    String Str
    {
        get { return _str + " World"; }
        set { _str = value; }
    }
}

8は偶数:@isEven(8)

プロパティの値:@_str / @Str

通常のコードブロック内でメソッドを定義したければ、匿名メソッド使えばよさそうです。
@{ 
    Func<int, bool> isOdd = (val) =>
    {
        bool ret = false;
        if (num % 2 != 0)
        {
            ret = true;
        }
        return ret;
    };
}

8は奇数:@isOdd(8)


条件付きナゲット (ASP.NET MVC5 以降)

コードナゲットを属性に使用すると、式が「trueである」又は「nullでない」場合にのみ属性が生成されます。

たとえば下記の例であれば、
式「styleRed」はnullではないので、style属性は出力されます。
式「styleNull」はnullなので、style属性自体が出力されません。
@{ string styleRed = "color:red"; }

style = color:red

@{ string styleNull = null; }

style = null

実行してソースの表示で出力されたHTMLを確認した結果です。

style = color:red

style = null

ASP.NET MVC 08_Razorビューエンジン ~Razor構文 その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


今回はビューエンジンについてです。
ビューエンジンはコントローラーから渡されたデータと、ビューの定義ファイル(cshtmlファイル)を組み合わせ、
サーバー側でレンダリング(HTMLコードに変換)する処理を行います。

ASP.NET MVC で使用できる代表的なビューエンジンは
ASP.NET Webフォームで使用していたASPXエンジンと、ASP.NET MVC3で新たに登場したRazorエンジンがあります。

ASP.NET MVC4ではプロジェクトを作成する時に、ASPXエンジンかRazorエンジンのどちらを使用するか選択できました。
ASP.NET MVC5では選択することができなくなり、標準でRazorビューエンジンが使用されます。


コードナゲット(インライン式)

ビューテンプレート(ビュー定義ファイル)にコードを埋め込んだ部分をコードナゲットと呼びます。
ASPXエンジンでは<%・・・%>と書いていた部分です。
Razorでは「@・・・」の1文字で始まり、閉じる必要がありません。コードナゲットの終わりは自動的に判定されます。

たとえば以下のコードでは「Model.Message」を式とみなします。
実行するとモデルのMessageプロパティの内容が出力されます。

@Model.Message


明示的コードナゲット

Razorでは式の終わりを自動的に判定しますが、明示的に式の範囲を指定したい時もあります。
式の範囲を指定する場合「@(・・・)」と丸かっこで囲みます。

たとえば次の例ではモデルのUserNameプロパティの内容に、静的なコンテンツである「さん」を結合して出力したいとします。
以下のように書くと、モデルに「UserNameさん」プロパティが定義されていないとコンパイルエラーになっていまいます。

@Model.UserNameさん

こんなときは「@(…)」を使用して、明示的にインライン式の範囲を指定します。
以下のコードではModel.UserNameまでを式とみなし、UserNameプロパティの内容に「さん」を結合して出力します。

@(Model.UserName)さん

次の例えはモデルのPriceプロパティの内容(1000)に1.08を掛けて「1080」と出力しようとしたものとします。
しかしPriceプロパティの後の空白が式の終わりと判定されてしまうため、「1000 * 1.08」と出力されてしまいます。

@Model.Price * 1.08

そこで「1080」と出力するために、式の終わりをは明示的に指定します。

@(Model.Price * 1.08)


@のエスケープ

ほとんどの場合、Razorは@がコードナゲットを表しているのかどうかを正しく判定します。
それでも文字列として出力したい@がコードナゲットとして認識されてしまう場合は、@を@@と2重にしてエスケープします。

@@Model.UserNameはモデルのプロパティです。


コードブロック

複数の文で構成されるコード・ブロックを記述したい場合は、「@{・・・}」と波括弧で囲みます。

以下の例ではコードブロック内で変数msgを定義し、コードブロック終了後のコードナゲットで変数msgの内容を出力します。
@{
    string msg = "Hello World";
}

@msg


コードブロック内で変数や文字列を出力したい場合は下記のようにタグで囲みます。
@{ 
    String ptn1 = "パターン1";
    

htmlタグで囲んで: @ptn1

}
タグで囲みたくない場合などは、先頭に「@:」を付けるとその行は静的コンテンツとして出力されます。
@{
    String ptn2 = "パターン2";
    @:複数行で出力するなら @ptn2
}
静的コンテンツが複数行になる場合は、下記のように<text>タグで囲みます。
<text>タグはRazorに静的コンテンツと知らせるためだけのダミータグで、<text>タグ自体は何も出力しません。
@{
    String ptn3 = "パターン3";
    
        複数行で出力するなら
        @ptn3
    
}
コードブロック内ではC#のコードが書けます。
@{
    //コメント     
    int number = new System.Random().Next(1, 10);
    if (number % 2 == 0)
    {
        @:number「@number」は偶数
    }
    else
    {
        @:number「@number」は奇数
    }
}

コメント

Razorでは「@*・・・*@」で囲まれた部分はコメントとみなされます。
HTMLのコメント「<!--・・・-->」は「ソースの表示」で表示されますが、Razorコメントは表示されません。
@*
    ここからはコメントです。

    @Model.Message

    @{
        int number = new System.Random().Next(1, 10);
    }
    ここまではコメントです。
*@

ASP.NET MVC 07_ビューでHelloWorld(モデル)

VisualStuidioCommunity2015/Fw4.5.2/C#


前回はビュー変数を使用して、ビューに「Hello World」 を表示しました。
今回はモデルを使用して、ビューに「Hello World」を表示します。

モデルの作成

まずはモデルから作成します。
Modelsフォルダを右クリックし、コンテキストメニューから「クラス」を選択します。
ファイル名は「HelloViewModel.cs」とします。
ビューに表示する文字列を取得および設定する「Message」プロパティを定義します。

Models/HelloViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Practice01_Begin.Models
{
    public class HelloViewModel    {

        public String Message { get; set; }

    }
}

モデルを作成したら、一旦ビルドしておきます。

コントローラの作成

前回まで使用していたコントローラー「HelloControler」に新たにアクションメソッド「ShowModel」を追加します。
17行目で、先ほど作成したHelloViewModelクラスのオブジェクトを作成し、
18行目で、Messageプロパティに「Hello World」を設定しています。
29行目では、Viewメソッドの引数にHelloViewModelオブジェクトを指定して、ビューを表示します。

Controllers/HelloControler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Practice01_Begin.Controllers
{
    public class HelloController : Controller
    {

        //・・・前回までのコードは省略

        public ActionResult ShowModel()
        {
            //HelloViewModelオブジェクトを作成し初期値を設定する
            Models.HelloViewModel mdl = new Models.HelloViewModel();
            mdl.Message = "Hello World";
            //モデルを指定してビューを表示する
            return View(mdl);

        }
    }
}

ビューの作成

最後にビューを作成します。
コントローラーのアクションメソッド「ShowModel」にカーソルを置いた状態で右クリックし、コンテキスメニューより「ビューの追加」をクリックします。
「ビューの追加」ダイアログで、以下の項目を設定します。
・ビュー名: ShowModel
・テンプレート: Empty
・モデルクラス: HelloViewModel
・レイアウトページの使用:チェックON
テンプレートをデフォルトの「Empty(モデルなし)」から「Empty」を選択すると、モデルクラスが選択できるようになります。

作成したビューのコードを見てみましょう。
1行目に「@model Practice01_Begin.Models.HelloViewModel」と記述されています。
Viewメソッドでモデルを受け渡す場合は、@modelディレクティブで使用するモデルを宣言しておきます。
9行目ではModelプロパティでHelloViewModelモデルのMessageプロパティの値を出力しています。

Views/Hello/ShowModel.cshtml
@model Practice01_Begin.Models.HelloViewModel

@{
    ViewBag.Title = "ShowModel";
}

ShowModel

@Model.Message


デバッグ実行して、ShowModelビューに Hello World が表示されることを確認します。

ASP.NET MVC 06_ビューでHelloWorld(ビュー変数)

VisualStuidioCommunity2015/Fw4.5.2/C#


前回作成したビューに「Hello World」 を表示してみます。

ビューに表示する文字列「Hello World」をコントローラーのアクションメソッドで定義し、
ビューではコントローラーから受け取った文字列を表示します。

コントローラーとビューでデータを受け渡しする方法には、ビュー変数を利用する方法やモデルを利用する方法などがあります。
今回はビュー変数を利用します。

ビュー変数を利用すると手軽にコントローラーとビューで値の受け渡しができます。
その反面、コントローラーとビューの依存が高まります。
MVCパターンでは、コントローラーからビューにデータを渡す場合はモデルを使うことになっています。

ビュー変数とは、コントローラーとビューでデータを受け渡しできるコンテナー(値を詰め込めるもの)のことで、
Controllerクラスの親クラスであるControllerBaseクラスのViewDataプロパティやViewBagプロパティを利用します。

ControllerBase.ViewDataプロパティ

Controllerクラスの親クラスであるControllerBaseクラスのViewDataプロパティは、名称をキーに値を格納するコレクション(集まり)です。
まずはコントローラーのアクションで、キー「Message」 値「Hello World」をViewDataプロパティに格納します。

Controllers/HelloController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Practice01_Begin.Controllers
{
    public class HelloController : Controller
    {
        // GET: Hello
        public ActionResult Index()
        {
            return View();
        }

     
        public ActionResult ShowHelloMessage()
        {
            //ControllerクラスのContentメソッドはActionResult派生クラスであるContentResultクラスを返す。
            //ContentResultクラスは引数に指定した文字列をテキスト形式で出力する。
            return Content("Hello World");
        }

        public ActionResult Show()
        {
            //ViwDataはControllerの基底クラスであるControllerBaseクラスのプロパティ。
            //名称をキーに値を格納するコレクション。
            ViewData["Message"] = "Hello World";

            //ControllerクラスのViewメソッドはActionResult派生クラスであるViewResultクラスを返す。
            //ViewResultクラスはアクションメソッドの結果を対応するビューを表示する。
            return View();
        }
    }
}
次はShowビューのコードです。
HTMLにコードを埋め込むには「@」を使用します。
ViewDataに格納するキーは大文字小文字が区別されません。
ViewDataに格納していないキーを指定してもエラーにならず、何も出力されませんん。

Views/Hello/Show.cshtml

@{
    ViewBag.Title = "Show";
}

Show

ViewDataの値を表示する

Messageの値:@ViewData["Message"]

messageの値(キーは大文字小文字を区別しない):@ViewData["message"]

MSGの値(格納されていないキーを指定したときは何も表示されない):@ViewData["MSG"]



ControlBase.ViewBagプロパティ

Controllerクラスの親クラスであるControllerBaseクラスのViewBagプロパティは、dynamic型のプロパティです。
dynamic型とは匿名クラス(無名クラス)を格納する型で、そのプロパティなどはコンパイル時にチェックされず、実行時に解決されます。
つまり匿名クラスとは事前に定義されたクラスではないので、そのクラスのメンバー情報(プロパティなど)は何があるかコンパイラにはわかりません。
どんなプロパティ名を指定してもエラーにはなりません。(そのかわり、インテリセンスも効きません。)

33行目でViewBagpプロパティの匿名クラスに「Message」プロパティとその値を設定しています。
Controllers/HelloController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Practice01_Begin.Controllers
{
    public class HelloController : Controller
    {
        // GET: Hello
        public ActionResult Index()
        {
            return View();
        }

     
        public ActionResult ShowHelloMessage()
        {
            //ControllerクラスのContentメソッドはActionResult派生クラスであるContentResultクラスを返す。
            //ContentResultクラスは引数に指定した文字列をテキスト形式で出力する。
            return Content("Hello World");
        }

        public ActionResult Show()
        {
            //ViwDataはControllerの基底クラスであるControllerBaseクラスのプロパティ。
            //名称をキーに値を格納するコレクション。
            ViewData["Message"] = "Hello World";

            //ViewBagはControllerの基底クラスであるControllerBaseクラスのプロパティ
            //dynamic型の値を取得/設定できる。
            ViewBag.Message = "Hello World";

            //ControllerクラスのViewメソッドはActionResult派生クラスであるViewResultクラスを返す。
            //ViewResultクラスはアクションメソッドの結果を対応するビューを表示する。
            return View();
        }
    }
}
次はShowビューのコードです。
ViewDataと同様で
ViewBagプロパティの匿名クラスに設定されたプロパティ名は大文字小文字が区別されません。
匿名クラスに設定していないプロパティ名を指定してもエラーにならず、何も出力されませんん。

Views/Hello/Show.cshtml

@{
    ViewBag.Title = "Show";
}

Show

ViewDataの値を表示する

Messageの値:@ViewData["Message"]

messageの値(キーは大文字小文字を区別しない):@ViewData["message"]

MSGの値(格納されていないキーを指定したときは何も表示されない):@ViewData["MSG"]

ViewBagの値を表示する

Messageの値:@ViewBag.Message

messageの値(プロパティ名は大文字小文字を区別しない):@ViewBag.message

MSGの値(定義していないプロパティ名を指定したときは何も表示されない):@ViewBag.MSG

ASP.NET MVC 05_ビューの作成

VisualStuidioCommunity2015/Fw4.5.2/C#


今回はコントローラーからビューを表示してみます。

ビューを表示するコントローラーの作成

まずはビューを表示するアクションメソッドを作成します。
前々回作成したコントローラー「HelloControler」に ビューを表示するアクションメソッド「Show」を定義します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Practice01_Begin.Controllers
{
    public class HelloController : Controller
    {
        // GET: Hello
        public ActionResult Index()
        {
            return View();
        }

     
        public ActionResult ShowHelloMessage()
        {
            //ControllerクラスのContentメソッドはActionResult派生クラスであるContentResultクラスを返す。
            //ContentResultクラスは引数に指定した文字列をテキスト形式で出力する。
            return Content("Hello World");
        }

        public ActionResult Show()
        {
            //ControllerクラスのViewメソッドはActionResult派生クラスであるViewResultクラスを返す。
            //ViewResultクラスはアクションメソッドの結果を対応するビューを表示する。
            return View();
        }
    }
}

ビューの作成

アクションメソッドを作成したら、次にビューを作成します。
アクションメソッド「Show」にカーソルを合わせて右クリックし、コンテキストメニューより「ビューの追加」をクリックします。

「ビューの追加」ダイアログが表示されるので、デフォルトのまま追加ボタンをクリックします。

ソリューションエクスプローラーで、Viewsフォルダの直下にHelloフォルダ/Show.cshtmlファイルが作成されていることを確認します。

アクションメソッドで引数なしのViewメソッドを呼び出しましたが、
引数なしのViewメソッドは、デフォルトで「Views/コントローラー名/アクション名.cshtml」のビューを検索し表示します。

ですので、ビューは
Viewsフォルダの直下に、コントローラー名のフォルダ、その直下に アクション名.cshtml という名前で配置する必要があります。


デバッグ実行し「http://localhost:12345/Hello/Show」とアドレスを入力するか、
ビューのコードを表示した状態でデバッグ実行し、ビューが表示されることを確認します。

ASP.NET MVC 04_ルーティングの基礎

VisualStuidioCommunity2015/Fw4.5.2/C#


前回は、コントローラーとアクションメソッドを作成しました。

その際、
ASP.NET MVCでは「フロントコントローラ方式」で、リクエストを1カ所で受け取り、URLから個々のコントローラークラスに振り分けます。
コントローラークラスには複数のアクションメソッドが定義されていて、URLからどのアクションメソッドが実行されるかが決定します。
アクションメソッドではユーザーからのリクエストに応じた処理を行って、ビューを表示するなどのレスポンスを返します。
と書きました。

今回は、リクエストされたURLからどのようにコントローラーとアクションメソッドが呼び出されているのか見ていきます。
リクエストされたURLから呼び出すアクションを決定する仕組みのことをルーティングと言います。

ルート定義ファイル

ルーティングを行うためのルート定義はソリューションのApp_Startフォルダ/RouteConfig.csファイルに記載されています。

プロジェクトを作成するとデフォルトで以下の内容が書かれています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Practice01_Begin
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}
RouteConfigクラスのRegisterRoutesメソッドで、ルート定義を作成します。

16行目~20行目でリクエストURLが、どのコントローラーのどのアクションメソッドにマッピングされるかが定義されています。
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

RouteCollectionクラスのMapRouteメソッドで、マッピングのルートを設定します。
MapRouteメソッドの引数
・nameはルートを識別するための名前です。一意になるように設定します。
・urlはマッピングを行うためのURLのパターンを設定します。
・defaultsはurlで指定したプレースホルダー({}で囲まれた部分)が省略された時のデフォルト値を指定します。

つまりルート名「default」の定義は
・URLが「コントローラー名/アクションメソッド名/アクションメソッドの引数」で構成されていて
・コントローラー名が指定されていなければ「Home」コントローラーを
・アクションメソッドが指定されていなければ「Index」アクションメソッドを
・アクションメソッドの引き数idはUrlParameter.Optionalで省略可能です。
ということになります。

以下のようなリクエストURLはすべて、HomeコントローラーのIndexアクションメソッドが呼ばれれます。
URLの大文字小文字は区別されません。
http://localhost:12345
http://localhost:12345/Home
http://localhost:12345/Home/Index
http://localhost:12345/Home/Index/xxx


ルート定義をアプリケーションに適用する

RouteConfigクラスのRegisterRoutesメソッドで、ルート定義を作成しただけではルーティングは行われません。
あたりまえですけど、RegisterRoutesメソッドではメソッドの処理を書いただけで、メソッドを呼び出さなければその処理は実行されません。
「Config」と付いていると、定義を書くだけでアプリケーションに適用されそうな気がしてしまうのは私だけでしょうか・・・

アプリケーションでルーティングを行うためには、Global.asaxファイル内の Application_Startイベントで、RouteConfigクラスのRegisterRoutesメソッドを呼び出します。
Global.asaxはグローバルアプリケーションファイルと呼ばれ、アプリケーションレベルでのイベントを処理するためのクラスです。
アプリケーションが起動すると、Application_Startイベントが発生します。
このApplication_Startイベント内でRouoteConfigクラスのRegisterRoutesメソッドを呼び出すことで、ルーティングがアプリケーションに適用されます。


プロジェクトを作成すると、Global.asaxファイルにはデフォルトで以下の内容が書かれています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Practice01_Begin
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}
17行目でRouteConfigクラスのRegisterRoutesメソッドを呼び出しています。

ルートを定義してみる

新しいルートを定義してみます。

デフォルトルートより前にGreetingルートを追加してみました。
Greetingルートでは、URL「http://localhost:12345/Greeting/Show」を前回作成したHelloコントローラのShowHelloMessageアクションメソッドにマップします。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Practice01_Begin
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //Greetingルート
            routes.MapRoute(
                name: "Greeting",
                url: "Greeting/Show",
                defaults: new { controller = "Hello", action = "ShowHelloMessage" }
            );
            //デフォルトルート
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            
        }
    }
}
MapRouteメソッドの引数 url にコントローラー名とアクション名が含まれない場合、引数 default にコントローラ名とアクション名が必須になります。
デバッグ実行し、アドレスバーに「http://localhost:12345/Greeting/Show」と入力します。
前回同様に「Hello World」と表示されます。

次に、GreetingルートをDefaultルートの後ろに定義してみます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Practice01_Begin
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //デフォルトルート
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
            //Greetingルート
            routes.MapRoute(
                name: "Greeting",
                url: "Greeting/Show",
                defaults: new { controller = "Hello", action = "ShowHelloMessage" }
            );

        }
    }
}
先ほどと同じようにデバッグ実行し、アドレスバーに「http://localhost:12345/Greeting/Show」と入力します。
今度は以下のようなエラー画面が表示されました。

URL 「http://localhost:12345/Greeting/Show」 はDefaultルートが適用され、Greetingがコントロール名、Showがアクションメソッド名としてルーティングされるんですね。

ルートを定義する順番には注意が必要です。

ASP.NET MVC 03_HelloWorld

VisualStuidioCommunity2015/Fw4.5.2/C#


お約束で「Hello World」の出力です。

コントローラーとアクションメソッド

ASP.NET Webフォームは「ページコントローラ方式」で個々のページでリクエストを処理していましたが、
ASP.NET MVCでは「フロントコントローラ方式」で、リクエストを1カ所で受け取り、URLから個々のコントローラークラスに振り分けます。
コントローラークラスには複数のアクションメソッドが定義されていて、URLからどのアクションメソッドが実行されるかが決定します。
アクションメソッドではユーザーからのリクエストに応じた処理を行って、ビューを表示するなどのレスポンスを返します。

つまりコントローラークラスとアクションメソッドがないと何も始まらないということです。

コントローラーの作成

まずはコントローラークラスを作成します。

Conrollersフォルダを右クリックしコンテキストメニューより 「追加」 > 「コントローラー」 を選択します。
「スキャンフォールディングを追加」ダイアログより「MVC 5 コントローラー -空」を選択します。

コントローラー名を表示するダイアログが表示されますので、「HelloControler」とします。

以上でControllersフォルダにHelloControl.csファイルが作成されました。

コントローラークラスの約束事ですが
コントローラーの名前には必ず「Controller」というサフィックスを付けます。
コントローラー名はこの「Controller」を除いた部分になります。(「HelloController」のコントローラー名は「Hello」になります。)
Controllerクラスを継承します。

アクションメソッドの定義

次にコントローラークラスにアクションメソッドを定義していきます。

従来のASP.NET Webフォームでは、ユーザーがボタンをクリックしたときの処理は各ページのコードビハインドにイベントとして記述されていました。
ASP.NET MVCでは、ユーザーがボタンをクリックしたときの処理はコントローラクラスにアクションメソッドとして記述します。

それではHelloControllerに「Hello World」を出力するアクションメソッドを定義します。

コントローラー「HelloController」のコードを見てみると、最低限のコードが記載されています。
デフォルトで用意されているIndexメソッドをまねて、以下のようなShowHelloMessageアクションメソッドを定義します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Practice01_Begin.Controllers
{
    public class HelloController : Controller
    {
        // GET: Hello
        public ActionResult Index()
        {
            return View();
        }

     
        public ActionResult ShowHelloMessage()
        {
            //ControllerクラスのContentメソッドはActionResult派生クラスであるContentResultクラスを返す。
            //ContentResultクラスは引数に指定した文字列をテキスト形式で出力する。
            return Content("Hello World");
        }
    }
}
アクションメソッドの条件は、コントローラークラスに定義されている、戻り値がActionResultでpublicなメソッドです。
コントローラークラスに定義されているpublicなメソッドはすべてアクションメソッドと見なされます。
戻り値がActionResultでないpublicメソッドを定義する場合は、NonActionAttribute属性を付ける必要があります。

ContentメソッドはControllerクラスのメソッドで、引数に指定した文字列をテキスト形式で出力します。


以上でコントローラーとアクションメソッドの作成が終了しました。

IEでデバッグ実行し、起動したページのアドレスに「http://localhost:12345/Hello/ShowHelloMessage」と入力します。
※「12345」の部分は実行するたびに変わります。

「Hello World」が表示されます。

ASP.NET MVC 02_プロジェクトの作成

VisualStuidioCommunity2015/Fw4.5.2/C#


最初はお約束でHelloWorldを出力してみます。

プロジェクトの作成

まずはASP.NET MVCプロジェクトを作成します。
Visual Studio 2015 Community を起動し、メニューより ファイル > 新規作成 > プロジェクト を選択します。
「新しいプロジェクト」ダイアログから インストール済み > テンプレート > Visual C# > Web > ASP.NET Webアプリケーション を選択します。

プロジェクト名、保存場所を指定してOKボタン押すと「新しいASP.NET プロジェクト」ダイアログが開きます。
「MVC」テンプレートを選択し、「Host in the cloud」をチェックOFFします。

以上でプロジェクトは作成できました。

プロジェクトには以下のようなフォルダが標準で作成されています。
App_Dataデータベースなどアプリケーションで使用するデータを保存するフォルダ
App_Start認証やルーティングなどの設定ファイルを保存するフォルダ
Contentスタイルシートなどを保存するフォルダ
Controllersコントローラークラスを保存するフォルダ
fontsBootstrapで使用しているアイコンを保存しているフォルダ
Modelsモデルクラスを保存するフォルダ
ScriptsJavaScriptやjQueryなどを保存するフォルダ
Viewsビューを保存するフォルダ

ASP.NET MVC 01_環境の作成

ASP.NET MVC 5 実践プログラミングを買いました。

プログラミングMicrosoft ASP.NET MVC 第3版ASP.NET MVC 5 対応版 (マイクロソフト公式解説書)も買いました。

この本を教科書にして手を動かしながら少しずつ勉強していこうと思います。

環境作成

まずは環境作成からです。
無償のVisual Studio Community Edition 2015を利用しようと思います。

下記のサイトからWebインストーラー「Microsoft Web Platform Installer 5.0」をダウンロードします。

ダウンロードした「wpilauncher.exe」を実行し、右上の検索ボックスに「2015」と入力して検索を実行します。

検索結果の一覧から「Visual Studio Community Edition 2015」を追加し、インストールを開始します。
インストールはかなりの時間がかかりました。

次にVisualStudio2015を日本語化するために以下のサイトから「Microsoft Visual Studio 2015 Language Pack」をダウンロードします。
https://www.microsoft.com/ja-jp/download/details.aspx?id=48157
ダウンロードした「vs_langpack.exe」を実行しインストールします。
こちらも結構な時間がかかりました。

以上で環境作成は終了です。

ASP.NET MVC 始めました。

今年初めに転職してもうすぐ1年です。

職場の99%がCOBOL出身者で、.NET(VB)に移行された方たちでした。
仕事は.Net2013(VB)+Oracle。Windows Form アプリ開発。

…あれ?.Netだよね?え?VB6じゃね?


そんな職場のVBあるあるですが
option strict off。
むちゃな型変換(戻り値stringなのにtrue/falseが返る)
金額計算で桁落ち発生。
onにしようと提案するも却下

すべてのメソッドはtrue、falseを返す。
しかし呼び出し元ではtrue。falseの結果は無視される

例外キャッチしてログを残すが、各開発者が気になるところだけtry~catch。
例外一元管理を提案。
それでも例外はキャッチされ、再スローではなく新スローされる。もしくは握りつぶされる。

クラス、継承、インターフェースがわからない
インターフェースを使ったら、皆がわからないからダメだと言われた

省略しすぎた変数名。クラス名。
省略しないメソッド名を書いたら長すぎると笑われた

変数名は接頭辞必須。
しかし値型でなければ接頭辞はすべてobjXXXX。意味ないだろソレ。

プロジェクト名やフォーム名はアルファベット3文字+数字5ケタ。
ベテランでもなければ対応表がなければ、どれが何なのかわからない。
もちろん対応表はない。

ストアド大好き
ストアドはCOBOL時代からだから、慣れてるもんね

あげたらキリがない。

もうね。
あ~残りの人生、ここで一生VBでWinフォームつくるんだ~
クソおもんないっ!ってちょっと腐ってました。

Winフォームの方は会社内で遺産があり、新しい作り方は受け付けないって感じで
ワンマン社長(開発もする)に釘さされました。

Webフォーム(VB)にはまだ参戦できそうだったので、そちらの方に力入れてたんですが
案件が少ない。
(それでも少しは影響を与えることができたかな・・・)

そんな中ASP.NET MVCで開発するぞっって話が出てきました。
私が参加できるかは微妙だけど、チャンスがあるなら手をあげれるように

ASP.NET MVC 始めました。

.NET 例外「Cannot drop database. ’データベース名’ because it is currently in use」

ASP.NET MVC5のお勉強中です。
サンプルに従って順次実行していってるのですが、

デバッグ実行して、ローカルDBからデータをViewに表示する

デバッグ停止

サーバーエクスプローラーでDBの内容を確認する。

再度デバッグ実行して、Viewを表示すると例外が発生して表示できません。

例外の内容は
Cannot drop database. ’データベース名’ because it is currently in use
データベースが使用中で削除できないとのことです。

サーバーエクスプローラーでDBの内容を確認すると
データベースに接続中になり、アイコンが緑のコンセントマークになります。

データベースを右クリックメニューから「切断」して実行すると、アイコンが×に変わります。

データベースを切断してから実行すると、例外が出ずに実行できました。

サンプルでは
DropCreateDatabaseAlwaysやDropCreateDatebaseIfModelChangesを使用してデータベースに初期データを作成しているので、
データベースに接続したままだと、データベースが削除できないヨと怒られたんですね。

Oracle ストアドの内容をファイルに出力する

Oracleのストアドの内容をバッチファイルでsqlplusで出力する方法です。

まずSQLファイルを作成します。
CドライブにCreate.sqlでファイルを作成したとします。
ストアドの出力先はCドライブのOutput.sqlとしています。
set echo off
set heading off
set termout off
set pause off
set pagesize 0
set linesize 1000
set trimspool on
set feedback off

spool C:\Output.sql

SELECT TEXT
FROM   ALL_SOURCE
WHERE  NAME = 'ストアド名'
ORDER BY TYPE, LINE;

spool off;

quit

次にバッチファイルを作成します。
CドライブにCreate.batでファイルを作成したとします。
sqlplusを使用して先ほど作成したsqlファイルを実行します。
sqlplus Oracleユーザー名/パスワード@データベース名 @C:\Create.sql

バッチを実行すると、ストアドの内容が「そのまま」出力されています。

ストアドの内容をCreate文付きで出力するのならコチラ。
set pages 0
set lines 200
set long 65535
set longc 65535
set trimspool on

spool 出力先ファイルパス

SELECT 
 DBMS_METADATA.GET_DDL(OBJ.OBJECT_TYPE, OBJ.OBJECT_NAME, OBJ.OWNER) AS SCRIPT
FROM DBA_OBJECTS OBJ
WHERE
 OBJ.OWNER ='ユーザー名'
 AND OBJ.OBJECT_TYPE ='PACKAGE'
 AND OBJ.OBJECT_NAME ='ストアド名';

spool off;

quit

ASP.NET Webフォームアプリケーションをコピーして、新しいアプリケーションを作成する際の注意

ちょっとハマったので自分用メモです。

VisualStudio2013 ASP.NET Webフォームアプリケーションであるプロジェクトを作成しました。
これをAプロジェクトとします。

次にAプロジェクトをコピーしBプロジェクトを作成しました。
アセンブリ名などを変更し、特に問題なく実行できることを確認しました。


次に
まずAプロジェクトを実行し、起動したIEを閉じます。

BプロジェクトのWeb.configを修正し起動させます。 このとき変更後のWeb.configが読み込まれず、AプロジェクトのWeb.configが読み込まれていました。
またBプロジェクトにブレークポイントを置いても、ブレークポイントで止まりませんでした。
ブレークポイントに警告「ブレークポイントは現在の設定ではヒットしません。(以下略)」と表示されていました。

各プロジェクトのプロパティでWebタグの「サーバー」の仮想ディレクトリが同じことが原因でした。

.NET(VB) いまさらだけどVBでYieldを書いてみた

C#ではVisualStudio2005(C#2.0)でyieldが登場し、
VBではVisualStudio2012(VB11)でやっとyieldが使えるようになりました。

「いつか使ってみたいなぁ」と思っていたけれど、まったく使う機会がありませんでした。
もうね「このまま一生使うこともないのかなぁ」と思ってました。
しかし、この度やっとyieldを使ってみることができました!しかもVBで!

うれしかったですね。
「yield便利~♪」より「yieldやっと使えたよ~っ泣」って。

yield便利だけど使う機会がほぼないんですよね。
特に私のような末端の底辺PGにはw
しかもジェネリックの登場で自作コレクションを作る機会自体が減ってますからね。

今回はASP.NETで自作コレクションクラスを作る必要がありyieldを使ってみました。
(少しハマった部分もありました。別記事で書きます。)

yieldの使い方はネットにごろごろあるので自分用のメモ程度に記載しておきます。

VisualStuidio2013/Fw4.0/VB
Public Class MyCollection
    Implements IEnumerable(Of String)

    Private _items As New List(Of String)

    Public Iterator Function GetEnumerator() As IEnumerator(Of String) _
    Implements IEnumerable(Of String).GetEnumerator
        For Each s As String In Me._items
            Yield s
        Next
    End Function

    Public Function GetEnumerator1() As IEnumerator _
    Implements IEnumerable.GetEnumerator
        Return GetEnumerator()
    End Function
End Class

自作コレクションクラスをForEachでクルクルまわせたり、Linqで使えるようにするためにIEnumerable(Of T)を実装させます。
IEnumerable(Of T)を実装すると、GetEnumerator()メソッドとGetEnumerator1()メソッドを実装する必要があります。

GetEnumerator1()メソッドは、もう一方のGetEnumerator()を返すように実装します。
GetEnumerator()メソッドは、Yeildを使用できるようにIteratorキーワードを付けます。
Public Iterator Function GetEnumerator() As IEnumerator(Of …
あとはコレクションの要素をクルクル回して1コづつYeildで返してあげればOKです。