銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Visual Studio Code で ASP.NET Core とTypeScript開発をセットアップしたい(未完)

Visual Studioを使って、ASP.NET Core で TypeScriptを使うセットアップについては公式のドキュメントがあります。

ASP.NET Core · TypeScript

なのですが、Visual Studio Codeを使った場合のセットアップ方法が見つからなかったので試してみました。ただ、とりあえず動いたけど、まだいまいち思い通りの挙動にならないという状態です。

まずはじめにyo aspnetでEmptyWebApplicationを生成します。ドキュメントに併せて、StaticFilesを利用できるようにします。

project.json

"Microsoft.AspNetCore.StaticFiles": "1.0.0"

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //略
    app.UseDefaultFiles();
    app.UseStaticFiles();
    //略
}

次にTypeScriptを追加します。

$ mkdir scripts
$ touch scripts/app.ts
function sayHello() {
    const compiler = (document.getElementById("compiler") as HTMLInputElement).value;
    const framework = (document.getElementById("framework") as HTMLInputElement).value;
    return `Hello from ${compiler} and ${framework}!`;
}

tsconfigを追加しますが、今回はプロジェクトのルートに配置することにします。

$ yo aspnet:TypeScriptConfig
{
  "compilerOptions": {
      "noImplicitAny": true,
      "noEmitOnError": true,
      "sourceMap": true,
      "target": "es5"
  },
  "files": [
      "./scripts/app.ts"
  ],
  "compileOnSave": true
}

次にgulpでTypeScriptのビルドタスクを記述することにします。必要なパッケージを追加できるようにpackage.json を作成します。

$ yo aspnet:PackageJson
{
    "version": "0.0.0",
    "name": "aspnetcorewithtypescript",
    "devDependencies": {
        "gulp": "3.9.1",
        "del": "2.2.0",
        "gulp-typescript": "2.13.6"
    }
}

で、gulpfileなのですが、問題が一つあって、Visual Studio CodeでCtrl+Shift+Bでビルドするコマンドはtasks.jsonに記述しますが、これは単一のコマンドしか指定できません(単一のコマンドでオプションを変えたものを複数実行することはできる)。そこで、dotnet 関連のビルドもgulpにまとめて記述してみることにします。

$ yo aspnet:Gulpfile
/// <binding AfterBuild='default' Clean='clean' />
/*
This file is the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/
"use strict";

var gulp = require('gulp');
var del = require('del');
var gutil = require('gulp-util');
var spawn = require('child_process').spawn;
var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");

var paths = {
    scripts: ['scripts/**/*.js', 'scripts/**/*.ts', 'scripts/**/*.map'],
};

function runner(cmd, args){
    return function(callback) {
        let command = spawn(cmd, args);
        command.stdout.pipe(process.stdout);
        command.stderr.pipe(process.stderr);
        command.on('close', function (code) {
            if (code !== 0) {
                throw new gutil.PluginError('dotnet', `Exited with code ${code}: ${cmd} ${args}.`);
            }
            callback();
        });
    }
}

gulp.task('clean', function () {
    return del(['wwwroot/scripts/**/*']);
});

gulp.task('default', function () {
    return tsProject.src()
        .pipe(ts(tsProject))
        .js.pipe(gulp.dest("wwwroot/scripts"));
});

gulp.task('dotnet-build', runner('dotnet', ['build']));
gulp.task('dotnet-restore', runner('dotnet', ['restore']));
gulp.task('dotnet-run', runner('dotnet', ['web']));
gulp.task('dotnet-test', runner('dotnet', ['test']));

これでgulp経由で起動できるようになったので.vscode/tasks.jsonを書き直します。

{
    "version": "0.1.0",
    "command": "gulp",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "default",
            "args": [],
            "isBuildCommand": true
        },
        {
            "taskName": "dotnet-build",
            "args": ["dotnet-build"],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
} 

ここまでやると、Ctrl+Shift+BでTypeScriptのビルドとdotnet buildがまとめて実行されるようになりました。が、問題はいくつかあって、一つはF5デバッグ起動するときにエラーになります。これはtasks.jsonのtasknameを変更したため、launch.jsonのpreLaunchTaskに指定しているタスクが実行できないためです。そして、preLaunchTaskは1つしか指定できないのでどうしよう、という状態です。

もっといいセットアップ方法がある気がするのですが、いったん調査結果としてまとめてみました。

ASP.NET Core on Linux で Session Replication するにはNFSが必要そうだというお話

ASP.NET Core でSession Replication (Sessionを外部で管理して、複数台で動かしても、また再起動したあともSessionデータを利用できるようにする)をやろうと考えた時のお話です。まだ確信をもっているわけではないのですが、調べたことをまとめるのも兼ねて記事にしました。

まずASP.NET Core でのアプリケーションの状態管理(リクエストの間だけ有効とかSessionとか)についてはこちらのページに記述があります。

Managing Application State — ASP.NET documentation

この項に記述がありますが、IDistributedCacheというインターフェースを通してDI経由で分散キャッシュの仕組みを容易に導入することができます。また導入しておくと、セッションもこのキャッシュを利用して保存される仕組みになるようです。ASP.NET チームの実装としては現在SQLServerとRedisが利用可能になっていました。

NuGet Gallery | Packages matching Microsoft.Extensions.Caching

ただ、Redisについては.NET Standardには対応しておらず、別の方の実装が利用可能です。

www.nuget.org

で、ここまでを使えば、.NET Core on RHEL な環境でもRedisを使って分散キャッシュでSessionを管理できます。が、問題はここからで、これだけだと再起動後にマシンが変わる、具体的にはContainerを利用していてデプロイのたびにContainer を作り直すような環境では再デプロイのたびにSessionが新規に発行されます。この理由を実装を追い掛けながら確認していたのですが、どうやらクライアントから送信されているCookieの値をSessionIdと関連付ける部分にマシン固有で生成している値を利用しています。この部分については「Data Protection」という名前で解説されています。

Configuring Data Protection — ASP.NET documentation

この実装を見る限り、Linuxから使うためには、複数マシンにNFSをマウントする、などのファイル共有を利用する必要がありそうです。

github.com

RedisだけでなくNFSの利用も必要なのがいまいちな感じなので、roadmapとあわせて調べていく予定です。

YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa で .NET Core on Linux について飛び入りトークしてきました

yapcasia8oji-2016mid.hachiojipm.org

2日目の日曜の朝に飛び入りトークしてきました。以下が資料になります。

doc.co

.NET Core のRTMが6/27に決まったあと、そうだYAPCで話そうと思ったもののすでにセッションは締め切られていたので、飛び入りトークとして話してきました。最初はわざわざ飛び入りトークルームに聞きに来る方いるかなあと懸念していましたが、ありがたいことに話し終わった後も(次の飛び入りがまだ入っていないこともあり)しばらくQ&A的にいろいろ面白い話をすることができました。

続きを読む