ASP.NET Core MVC - 部分ビューを再帰で呼び出す
部分ビューから自身の部分ビューを再帰で呼び出しできるよね?と思って試したらできましたというお話。
以下は次のul・liを作るサンプルです。
モデルを準備
まずはノードを表すモデルとデータを用意します。
// ノード public class Node { // 名前 public string Name { get; set; } // 子ノード一覧 public IEnumerable<Node> Children { get; set; } = Enumerable.Empty<Node>(); } // サンプルデータ public static class Sample { // 適当なデータを作る public static IEnumerable<Node> GetNodes() => new[] { new Node { Name = "A", // 子は2つ Children = new[] { new Node { Name = "A1", }, new Node { Name = "A2", } }, }, new Node { Name = "B", // 子が3つ Children = new[] { new Node { Name = "B1", }, new Node { Name = "B2", // 子が2つ Children = new[] { new Node { Name = "B2-1", }, new Node { Name = "B2-2", }, }, }, new Node { Name = "B3", } }, }, new Node { Name = "C", }, }; }
partialタグを使って部分ビューを呼び出す
続いてindex.cshtml。
partialタグはHtml.PartialAsyncやHtml.RenderPartialAsyncに相当するタグヘルパーです。ASP.NET Core 2.1から使えるようになりました。
このpartialタグを使って部分ビューを呼び出してレンダリングしています。
<!-- index.cshtml --> @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> // 部分ビューを呼び出す <partial name="_List" model="Sample.GetNodes()" /> </body> </html>
部分ビューから部分ビューを呼び出す
続いて部分ビュー(_List.cshtml)です。自身の部分ビューのレンダリングを再帰で呼び出しています。
<!-- _List.cshtml --> @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @model IEnumerable<Node> <ul> @foreach (var node in Model) { <li> @node.Name @if (node.Children.Any()) { // 自身の部分ビューを呼び出す(再帰) <partial name="_List" model="node.Children" /> } </li> } </ul>
これで想定通りのHTMLが生成されました。
ASP.NET Core MVC - クッキーを使う
クッキーを設定する
レスポンスにクッキーを設定するには、IResponseCookies.Appendメソッドを使います。
// クッキーを設定する HttpContext.Response.Cookies.Append("test", "Hoge"); // レスポンスに追加されるSet-Cookieヘッダ //Set-Cookie: test=Hoge; path=/
ASP.NETのときは、1つのクッキーに複数のキーバリューを簡単に設定できましたが、Coreでは自分で実装する必要がありそうです。そういった使い方をあまりしなくなったのかな。
クッキーを取得する
リクエストからクッキーを取得するには、IRequestCookieCollectionインターフェイスを使います。
// 次のようなCookieヘッダが送られてくるとして //Cookie: test=Hoge // クッキーを取得 var value = HttpContext.Request.Cookies["test"]; Console.WriteLine(value); // Hoge // TryGetValueメソッドを使ってクッキーを取得するのもあり if (HttpContext.Request.Cookies.TryGetValue("test", out var value)) { Console.WriteLine(value); // Hoge }
インデクサで取得する場合、クッキーがないと戻り値はnullになります。
クッキーを削除する
クッキーを削除(=HTTPレスポンスに期限切れの空のクッキーを設定)するには、IResponseCookies.Deleteメソッドを使います。
// クッキーを削除する HttpContext.Response.Cookies.Delete("test"); // レスポンスのSet-Cookieヘッダ //Set-Cookie: test=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax
とりあえず基本はこんな感じですかね。
ASP.NET Core MVC - @addTagHelperは名前空間ではなくアセンブリ名を指定する
タグヘルパーを作ってみていきなりハマったのでもメモしておきます。
@addTagHelper
ディレクティブでタグヘルパーを使えるようにしますが、このときに指定するのは名前空間ではなくアセンブリ名です。
例えば次のようなTagHelperを作ったとして、
// アセンブリはWebAppとして namespace WebApp.TagHelpers { // 適当なタグヘルパー public class SampleTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { // 何かする } } }
これをビューで使うためには次のようにアセンブリ名を指定します。
@* アセンブリ名を指定する *@ @addTagHelper *, WebApp @* こうではない @addTagHelper *, WebApp.TagHelpers @*
名前空間を指定してもエラーにならないようで小一時間悩みました。
参考
T-SQL - 再帰CTEで指定した期間の日付一覧を作る
T-SQLのメモです。
開始日と終了日を指定して、その期間の日付一覧を取得したいと思います。
再帰CTE(共通テーブル式)を使って書くとこんな感じになるのかなと。
-- 指定した期間の日付一覧を取得するクエリ -- @start 開始日(その日は含む) -- @end 終了日(その日は含まない、とする) declare @start date = N'2018-07-01'; declare @end date = N'2018-08-01'; with Dates(Date) as ( select @start union all select dateadd(day, 1, Date) from Dates where Date < dateadd(day, -1, @end) ) select * from Dates -- 再帰回数を無制限にするなら option (maxrecursion 0);
再帰CTEを使わない方法をちょっと考えてみたんですが思いつかなかったです。
参考
ASP.NET Core MVC - TempDataを使う
今回はTempDataを使ってみたいと思います。いつものようにDocsを参考にしています。
ASP.NET Core でのセッションとアプリの状態 | Microsoft Docs
StartupクラスでTempDataを構成する
まずはTempDataを使うために準備します。と言ってもStartup.ConfigureServicesでAddMvcメソッドを呼び出すだけでいいみたいです。
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure<CookieTempDataProviderOptions>(options => { // TempDataのクッキーの名前を変えるなら options.Cookie.Name = "temp"; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseMvcWithDefaultRoute(); } }
コード中のコメントに「TempDataのクッキー」とありますが、CoreではないMVCを使っていた方は疑問に思うかもしれません。詳しくは後述します。
TempDataの読み書き
TempDataに文字列や数値を読み書きする場合は従来と変わりません。
// TempDataに文字列を書き込む TempData["key"] = "Hoge"; // TempDataから文字列を読み込む var value = (string)TempData["key"];
ただTempDataにオブジェクトを直接読み書きすることはできないようです。
セッションと同じようにJSON文字列として読み書きする拡張メソッドを用意すればいいのかな。
// TempDataにオブジェクトを読み書きする拡張メソッドを用意する public static class TempDataDictionaryExtensions { // TempDataにオブジェクトを書き込む public static void SetObject<TObject>(this ITempDataDictionary tempData, string key, TObject obj) { var json = JsonConvert.SerializeObject(obj); // JSON文字列として書き込む tempData[key] = json; } // TempDataからオブジェクトを読み込む public static TObject GetObject<TObject>(this ITempDataDictionary tempData, string key) { // JSON文字列として読み込む var json = (string)tempData[key]; return string.IsNullOrEmpty(json) ? default(TObject) : JsonConvert.DeserializeObject<TObject>(json); } } // 用意した拡張メソッドを使う // TempDataにSampleクラスを書き込む TempData.SetObject("key", new Sample()); // TempDataからSampleクラスを読み込む TempData.GetObject<Sample>("key");
TempDataとクッキー
ASP.NET Core 2.0からデフォルトではTempDataのストア(格納先)にクッキーが使われるようになりました。
クッキーに格納されるデータは暗号化されるとのことです。
またTempDataとCookieのHTTPヘッダの関係は、
といった動きになっていました。
TempDataのストアをセッションにする
TempDataのストア(格納先)をセッションにする場合は、Statupクラスでセッションを使うように構成しつつ、AddSessionStateTempDataProviderメソッドを使います。
public class Startup { public void ConfigureServices(IServiceCollection services) { // サービスにセッションを追加 services.AddSession(options => { // セッションクッキーの名前を変えるなら options.Cookie.Name = "session"; }); // MVC関連のサービスを追加 services.AddMvc() // TempDataのストアをセッションにする .AddSessionStateTempDataProvider(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // セッションを使う app.UseSession(); app.UseMvcWithDefaultRoute(); } }
クッキーとセッションの使い分けはケースバイケースだろうなと思いますが、ちょっと悩みそう。
以上TempDataの基本的な使い方でした。