いぇいいぇい

ASP.NET、PHPを中心に書いています

よく使うRazor記述パターン

自分用メモ。随時更新。

方針

Razor構文はいろいろな書き方をサポートしているけど、コードの見やすさを重視して、使うパターンをなるべく少なくする方針です。これだけ覚えとけばOKというものを書きます。

基本編

変数や式の結果を出力

基本ですね。@()を使うと変数や式の結果を出力できます。出力は自動でHtmlエスケープされます。

<span>@(Model.Title)</span>
<span>@(TimeZoneInfo.ConvertTimeFromUtc(Model.Time, Model.UserTimeZone).ToString("yyyy-MM-dd HH:mm"))</span>
<div class="exec_result_@(Model.Status == ExecStatus.Succeeded ? "suc" : "err")"></div>

Htmlエスケープしたくない場合は@Html.Raw()を使います。

<head>
@Html.Raw(Model.CommonCSSLink)
..
</head>

foreach文を使う

@foreach (var item in Model.Shops)
{
    ...
}

if文を使う

@if (User.IsInRole("Admin"))
{
    ...
}
else if (User.IsInRole("Operator"))
{
    ...
}
else
{
    ...
}

ヘルパー関数を呼ぶ

次項を参照。

アンカー(リンク)を出力

Url.Actionで指定アクションのURLを出力したり、Html.ActionLinkで指定アクションのaタグを出力したりできます。 アクション以外にController名、Area名、URLパラメータなども指定可能です。

<a href="@Url.Action("Logout")">ログアウト</a>
@Html.ActionLink("ログアウト", "Logout")

応用編

既定の名前空間を宣言する

よく使う名前空間があって、毎回cshtmlusingするのが面倒くさいときはViews\Web.configに名前空間を定義すればOKです。

  <system.web.webPages.razor>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        ..
        <add namespace="Mamemaki.TestApp.Models" />
        <add namespace="Mamemaki.TestApp.ViewModels" />
        <add namespace="Mamemaki.TestApp.Helpers" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

URL、HTMLまたはAjax関連のヘルパー関数を拡張する

組み込み(WebViewPageクラスを参照)で用意されているヘルパーは以下の3つ。

  • UrlHelper
  • HtmlHelper
  • AjaxHelper

これらに関連する処理なら、以下のように拡張関数を定義してやればいいです。

/// <summary>
/// 現在のコントローラー名を小文字で返します。
/// </summary>
/// <param name="urlHelper"></param>
/// <returns></returns>
public static string CurrentController(this UrlHelper urlHelper)
{
    return urlHelper.RequestContext.RouteData.Values["controller"].ToString().Trim().ToLower();
}

/// <summary>
/// 店舗写真のURLを返します。
/// </summary>
/// <param name="urlHelper"></param>
/// <param name="shop"></param>
/// <returns></returns>
public static string ShopImageThumb(this UrlHelper urlHelper, ShopInfo shop)
{
    var routeValues = new RouteValueDictionary();
    routeValues.Add("id", shop.ID);
    if (shop.LastModifiedDate.HasValue)
        routeValues.Add("time", shop.LastModifiedDate.Value.Ticks);
    return urlHelper.Action("ImageThumb", "Shops", routeValues);
}

独自のヘルパーを定義する

独自のヘルパーを定義することも可能です。自分はそのような場面に出くわして無いので省略。

ほとんどの場面では組み込みのヘルパーを拡張すればよいと思います。だってビューの処理でHTMLに関係ない処理なんて思いつかないもんね。

ASP.NET Identityでユーザーに紐付いた別テーブルの情報を、DBアクセスなしにページヘッダに表示する方法

契約情報があって、その下に複数のユーザーをぶら下げるという構成の場合。(契約情報=ユーザーとしてもいいんだろうけど、担当者の変更などを考えると複数ユーザーにできたほうがよいと思った。)

ユーザーテーブルに契約情報テーブルを関連付ける

まずASP.NET Identityでユーザーテーブルに契約情報テーブルを関連付けたい。

調べた結果ApplicationUserは普通のモデルクラスなのでCode Firstなら単純に関連テーブルとして定義してやればいい。

public enum SubscriptionKind
{
    Free = 0,
    Standard = 1,
}

public enum SubscriptionStatus
{
    Active = 0,
    Cancelled = 1,
}

public class Subscription
{
    public int ID { get; set; }

    public SubscriptionKind SubscriptionKind { get; set; }

    public SubscriptionStatus SubscriptionStatus { get; set; }
}

public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public int SubscriptionId { get; set; }

    public virtual Subscription Subscription { get; set; }

    public DateTime CreatedDate { get; set; }
}

契約情報をクレームに追加する

さらに契約情報をページヘッダに表示したい。毎回DBアクセスしたくないのでクレーム用Cookieから読み込みたい。

public class ApplicationClaimTypes
{
    public const string SubscriptionKind = "http://mamemaki.com/claims/subscriptionkind";
    public const string SubscriptionStatus = "http://mamemaki.com/claims/subscriptionstatus";
}

public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
    {
        // authenticationType が CookieAuthenticationOptions.AuthenticationType で定義されているものと一致している必要があります
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // ここにカスタム ユーザー クレームを追加します
        userIdentity.AddClaim(new Claim(ApplicationClaimTypes.SubscriptionKind, this.Subscription.SubscriptionKind.ToString()));
        userIdentity.AddClaim(new Claim(ApplicationClaimTypes.SubscriptionStatus, this.Subscription.SubscriptionStatus.ToString()));
        return userIdentity;
    }
}

GenerateUserIdentityAsync()でクレームを追加してやるといいらしい。他のタイミングじゃダメなんだろうか。。?

契約情報をページヘッダに表示する

これで_Layout.cshtmlなどからDBアクセスせずに契約情報を表示できた。

<li>契約種別: @Html.Raw(((System.Security.Claims.ClaimsIdentity)User.Identity).FindFirst(ApplicationClaimTypes.SubscriptionKind).Value)</li>

こりゃ便利ですな。