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を使う

ASP.NET Core MVCの勉強の記録です。

今回は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に書き込むとクッキーが追加される(Set-Cookieされる)
  • TempDataから読み込むとクッキーが削除される(空のクッキーでSet-Cookieされる)

といった動きになっていました。

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の基本的な使い方でした。

ASP.NET Core MVC - Sessionを使う

ASP.NET Core MVCの勉強の記録です。

いつものごとくDocsを参考にして、今回はセッションを使ってみたいと思います。

ASP.NET Core でのセッションとアプリの状態 | Microsoft Docs

Startupクラスでセッションを構成する

まずはセッションを使えるようにします。

Startup.ConfigureServicesメソッドでAddSessionメソッドを呼び出して、ConfigureメソッドではUseSessionメソッドを呼び出します。

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        // いらないのかも
        //services.AddDistributedMemoryCache();

        // セッションを使う
        services.AddSession(options => {
            // セッションクッキーの名前を変えるなら
            options.Cookie.Name = "session";
        });

        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
        // パイプラインでセッションを使う
        app.UseSession();

        app.UseMvcWithDefaultRoute();
    }
}

検索すると見つかるサンプルには、ConfigureServicesメソッドでDistributedMemoryCacheを足しているものがありますが、今は不要かもしれません。 どうもAddMvcメソッドの中で追加されるっぽいです。

セッションの読み書き

セッションに対して読み書きするには、HttpContext.Sessionプロパティ(型はISession)経由で行います。

セッションに数値や文字列を格納する場合は、設定・取得する拡張メソッドが用意されています。こちらを使いましょう。

// 文字列の場合
// SetStringとGetStringの拡張メソッドを使うのに必要
using Microsoft.AspNetCore.Http;

// セッションに文字列を書き込む(キーは"key")
HttpContext.Session.SetString("key", "Hoge");

// セッションから文字列を読み込む
HttpContext.Session.GetString("key");

セッションにオブジェクトを読み書きする

セッションにオブジェクトを格納するにはどうするの?と検索したところ、JSONに変換して格納するサンプルがいくつか見つかりました。

次のような拡張メソッドを用意すると良いようです。

// セッションにオブジェクトを設定・取得する拡張メソッドを用意する
public static class SessionExtensions {
    // セッションにオブジェクトを書き込む
    public static void SetObject<TObject>(this ISession session, string key, TObject obj) {
        var json = JsonConvert.SerializeObject(obj);
        session.SetString(key, json);
    }

    // セッションからオブジェクトを読み込む
    public static TObject GetObject<TObject>(this ISession session, string key) {
        var json = session.GetString(key);
        return string.IsNullOrEmpty(json)
            ? default(TObject)
            : JsonConvert.DeserializeObject<TObject>(json);
    }
}

// 拡張メソッドを使ってみる
// セッションにSampleクラスを書き込む
HttpContext.Session.SetObject("key", new Sample());

// セッションからSampleクラスを読み込む
HttpContext.Session.GetObject<Sample>("key");

とりあえず基本的な使い方はこんな感じかなと。

まだ調べていなくて気になる部分がいくつかあります。CoreではState Serverはないの?とか、ロックされるの?とか、HttpSessionState.Abandonは?とか。

どうなんでしょう。追々調べていきたいと思います。

ASP.NET Core MVC - オプションパターンで設定ファイルを読み込む

オプションパターンを使って設定ファイルから構成情報を読み込むサンプルです。

こちらを読みながら試しました。

ASP.NET Core のオプション パターン | Microsoft Docs

上記ドキュメントにもメリットが書いてありますが、平たく言ってしまうと「オプションパターンを使うと、設定を機能ごとにグルーピングしてわかりやすく管理できますよ」ってことかなと。

ということでコードを見ていきましょう。

次の設定ファイル(appsettings.json)をコントローラやビューで読み込んでみたいと思います。

// appsettings.json
{
    "App": {
        // ある機能の設定だと思ってもらえると
        "Sample": {
            "Value1": 10,
            "Value2": 20
        }
        // 他の機能の設定はここに足していく感じで
    }
}

設定をバインドするクラス

まずはappsettings.jsonSample以下をバインドするクラスを用意します。

// appsettings.jsonのSample以下をバインドするクラス
public class SampleSettings {
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

設定をバインドできるようにする

設定を上記クラスにバインドできるように(DIで取得できるように)するために、オプションをサービスに登録します。

具体的にはStartup.ConfigureServicesメソッドを次のようにします。

public class Startup {
    private readonly IConfiguration _config;

    public Startup(IConfiguration config) {
        _config = config;
    }

    public void ConfigureServices(IServiceCollection services) {
        services.AddMvc();

        // 設定をクラスにバインドできるようにする
        services.Configure<SampleSettings>(_config.GetSection("App:Sample"));
        // この書き方でもいいみたい
        //services.Configure<SampleSettings>(_config.GetSection("App").GetSection("Sample"));
    }

    // 略
}

コントローラで設定を参照する

コントローラで設定を参照してみましょう。

コンストラクタインジェクションでIOptions<TOptions>インターフェイスを指定すると、 SampleSettingsを取得できるようになります。

public class DefaultController : Controller {
    private readonly SampleSettings _settings;

    // DIでIOptions<SampleSettings>を取得
    public DefaultController(IOptions<SampleSettings> options) {
        // IOptions.ValueプロパティからSampleSettingsを取得できる
        _settings = options.Value;
    }

    public IActionResult Index() {
        // 設定を参照できる
        Console.WriteLine($"{nameof(SampleSettings.Value1)}: {_settings.Value1}");
        Console.WriteLine($"{nameof(SampleSettings.Value2)}: {_settings.Value2}");
        // Value1: 10
        // Value2: 20

        return View();
    }
}

ビューで設定を参照する

ビューで設定を参照するには@injectを使います。

@using Microsoft.Extensions.Options;
@inject IOptionsSnapshot<SampleSettings> Options

@{
    var settings = Options.Value;

    @* 設定を参照できる *@
    <div>@nameof(SampleSettings.Value1): @settings.Value1</div>
    <div>@nameof(SampleSettings.Value2): @settings.Value2</div>
    @*
        <div>Value1: 10</div>
        <div>Value2: 20</div>
    *@
}

設定を再読込する

IOptionsの代わりにIOptionsSnapshotを使うと、Webアプリケーション実行中に変更したappsettings.jsonの値を再読み込みできるようになります。

public class DefaultController : Controller {
    private readonly SampleSettings _settings;

    // IOptionsの代わりにIOptionsSnapshotを使うと再読み込みできる
    public DefaultController(IOptionsSnapshot<SampleSettings> options) {
        _settings = options.Value;
    }
}

ドキュメントによると、リクエストごとに1回読み込んでキャッシュされるっぽいです。

ASP.NET Core 2.0 以降では、オプションは、要求の有効期間中にアクセスされ、キャッシュされたとき、要求につき 1 回計算されます。

引用元

おしまい。

ASP.NET Core MVC - グローバルフィルタで認証

グローバルフィルタを使って認証を必要とする方法です。認証を必要とするというかアクセス制御するというか。

ウェブアプリのうちログインを必要とする部分が多くてログイン不要な部分が少ない場合、グローバルフィルタで全体を制御しつつ、ログインなしで利用したいコントローラ・アクションにAllowAnonymous属性をつけると手堅いかなと思います。

Core MVCでは、ConfigureServicesメソッド内でAddMvcメソッドを使ってサービスを追加するときに、AuthorizeFilterを作成してMvcOptions.Filtersに追加すればいいみたいです。

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        // クッキー認証を行うためにサービスを登録
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();

        services.AddMvc(options => {
            // ウェブアプリ全体で認証を必要にするため、グローバルフィルタに追加
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        });
    }
}

CoreではないMVCとかなり違ったので悩みました。たぶんこれでいいはず。

参考