まだIT業界で消耗してるの? 😊ソフトウェア開発者のブログ

Bootstrap 4 Alpha 6 を使ってみた

2017年1月11日

Bootstrap 4 Alpha 6 が1月6日に公開されました。今回のバージョンでかなり完成に近づいたと思うので、テスト的に使ってみました。次は、Beta になる予定で、残っている Issues の数もそれほど多くないので、基本的な所での変更はあまりないだろうと思っています。

自分の場合は、Visual Studio 2015 を使っていて ASP.NET Core で Web アプリケーションを作ることが多いので、最初に Visual Studio の ASP.NET Core Web Application のテンプレートを使って試してみました。

Bootstrap 4 に更新するためには、bower.json を以下のように変更します。bootstrap のバージョンを変更し、ツールチップの表示に Tether という JavaScript のライブラリーを利用するため、そのライブラリーを追加するだけです。

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "bootstrap": "4.0.0-alpha.6",
    "jquery": "3.1.1",
    "jquery-validation": "1.14.0",
    "jquery-validation-unobtrusive": "3.2.6",
    "tether": "1.4.0"
  }
}

インストールするだけであれば簡単なのですが、実行させてみると完全にレイアウトが崩れてしまいます。Ver3 から Ver4 への変更は影響がかなり大きいことがわかります。

どのような修正が必要になるのかについては、そのテンプレートを、取りあえず動くようにしたものを GitHub の方に公開していますので参考にしてください。Navbar や Carousel にもかなり修正が入っています。

それで、公式ドキュメントを結構真面目に読んでいます。英語がそれほど得意でないので、前回のブログで紹介した「Google ウェブサイト翻訳ツール」プラグインをつけたドキュメントのページが結構役に立っています。

それらの作業をして、公式ドキュメントの Migrating to v4 を読んだところでは、Ver4 の特徴は、HTML5、CSS3 の機能をフルに活用するようになったということのようです。

自分としては、Bootstrap 4 は以前よりかなり良くなっていると思うので、積極的に使っていきたいです。でも、class 名が結構変わっているので、既存のWebサイトを Bootstrap 4 にバージョンアップしようとすると修正箇所が多くなるので、手間を考えると少し憂鬱ですが、時間をかけて更新していくつもりです。

なお、Ver4 の主な変更点は、以下のようなところです。

  • IE8 と IE9 のサポートの廃止
  • Flexbox によるレイアウト
  • Less から Sass へ
  • 標準のフォントサイズは 16px
  • 使う単位を px から rem に変更
  • ブレークポイントに576pxを追加

Bootstrap 4 のドキュメントに「ウェブサイト翻訳ツール」プラグインをつけてみた

2017年1月4日

Bootstrap 4 Alpha が最初に公開されたのは、昨年の8月19日です。それから1年4ヶ月を経過しましたが、まだ Alpha 5 という状況です。開発に非常に時間がかかっていますが、最近になってやっと開発が加速しているようなので、自分もテストに使い始めました。

Bootstrap 4 Alpha のドキュメントは当然英語しかないのですが、Google翻訳にニューラルネットワークが導入されて精度が大幅に上昇しているので、Bootstrap 4 のドキュメントに「ウェブサイト翻訳ツール」プラグインをつけてみました。Bootstrap のドキュメントは、GutHub で CREATIVE COMMONS で公開されているので、著作権の問題はないので以下の URL で公開していますので興味のある方は見てください。

http://bootstrap.pp22.net/

プラグインを追加するだけで、翻訳ができてしまうのは、素晴らしいです。

Google 翻訳は、まだまだ、おかしなところも多いですが、あれば便利というところまではなっていると思います。以前は使い物にならなかった機械翻訳ですが、やっと少しですが実用的になったようです。

Bootstrap 4 の日本語のドキュメントはBootstrap 4 日本語リファレンスという翻訳中のものがあるのですが、現時点では未翻訳の部分が多いし、Alpha 2 ベースです。ドキュメントの翻訳には時間と手間がかかるので、もう Google 翻訳で我慢してもいいように思います。

Bootstrap 4 の開発状況ですが、9月6日に、現行バージョンである Bootstrap 3 の定期的メンテナンスを終了するという発表(Getting to v4)がありました。この状況については、InfoQの「Bootstrap v3の定期的メンテナンスが終了」という記事が参考になると思います。

現在開発中の Alpha 6 では、Flexbox がデフォルトになり従来の float レイアウトはサポートされなくなります(v4 Alpha 6 ship list)。普通に使っているブラウザーで Flexbox をサポートしていないのは IE9 だけですが、IE9 は今年(2017年)の 4月11日に Windows Vista と共にサポート切れになります。

これらの件は、当初の計画とは違うものですが、開発者の負担の軽減ということでは仕方がないことだと思います。そして、Bootstrap 4 の 問題(Issues) が順調に解消され始めています。近いうちに Alpha 6 が公開され、その次は Beta です。完成も見えてきたように思います。

ASP.NET Core でボットネット対策(その1 middlewareを使う)

2016年12月20日
DDoS 攻撃への対応

この記事は「ASP.NET Advent Calendar 2016 - Qiita」の20日目になります。

昨日のブログでは、ボットネットからアクセスがあることを書きましたが、その中で Google Cloud Datastore は自動スケールするので役に立ちそうだということを書きました。自動スケールされると破産するのではと心配する人もいると思いますが、Google Cloud では費用制限を設定するので破産することはないし、1秒間に1万回の読み込みをしても、1時間だと $21.6 とそれほど大したことはありません。

ただし、攻撃を放置しておくと結構な料金になってしまうので、攻撃を止める方法を考えてみました。

まず、DDoS 攻撃の現状がどうなっているかについては、Googleのシンクタンク部門である Google Ideas とセキュリティ企業の Arbor Networks と協力して、DDoS攻撃の状況をリアルタイムで表示するDigital Attack Mapを公開しています。そのサイトの Understanding DDoS によると、1週間小さな組織のシステムをダウンさせる能力があるボットネットを $150 で借りられるそうです。自分の場合は、最初はこの程度でできる攻撃への対応を考えてみようと思っています。

DDoS 攻撃への対応

攻撃を止める基本は、ボットのIPアドレスを特定して、そこからのアクセスを拒否することです。IPアドレスが特定できれば、アクセスの拒否については、ファイアーウォール、リバースプロキシーでも簡単にできます。そのため、DoS攻撃の場合は、比較的簡単に防御できます。

しかし、DDoS 攻撃の場合は、大量のボットから攻撃があるので、そもそも正常なアクセスなのか攻撃なのかを区別するのが容易でないし、攻撃元を特定して遮断していく作業は容易でありません。もし、攻撃元がボットをどんどん変更して攻撃してくれば、対応は本当に困難になります。

HTTP リクエストの場合、接続する URL 以外に IPアドレス、Referer、User-Agent の情報が送られてきます。低価格で借りたボットネットであれば、これらの情報に片寄りが生じるはずなので情報をうまく解析すれば、ボットのIPアドレスは特定できるはずです。

また、Pokémon GO で有名になったコイル警備員、すなわち Google の reCAPTCHA を使うことも有効です。アクセス拒否は、もし間違って拒否をするとそのユーザーを失うことになるので、疑わしいという状況では使いたくありません。でも、コイル警備員だとユーザー側の負担は遙かに軽いので、疑わしIPアドレスに対して使う事ができます。

Middleware を作成して特定のIPアドレスからのアクセスを拒否する

まず最初に、ASP.NET Core アプリケーションで、特定のIPアドレスからのアクセスを拒否するケースを考えてみます。

この場合には、HTTP Request があって、そのリクエストが MVC の処理に入るまでに処理をする必要があるため、Middleware を作成して処理する必要があります。ASP.NET Core の Middleware の Document に、middleware pipelineの図(下図)がありますが、

middleware pipeline の画像

その図をみれば、どう処理をしたらいいかをイメージできると思います。標準の MVC Middleware コンポーネントの前にカスタム Middleware コンポーネントを作成して処理をすることになります。

Middleware の作成に Visual Studio を使う場合は、「ミドルウェア クラス」のテンプレートがあるのでそれを使うと便利です。それを少し変更して非同期にすると下のようなコードになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace MyApp
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;
        public MyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            await _next(httpContext);
        }
    }
    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class MyMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddleware>();
        }
    }
}

このコードと middleware pipeline の図を眺めていると大まかな使い方は理解できました。Invoke メソッドの、await _next(httpContext) が次のパイプラインを呼び出す処理で、その前後にコードを書くことで独自の処理を行わせることができます。

以下は、上のコードを修正して、特定のIPアドレスからのアクセスを拒否するようにしたサンプルです。

    public class AntiBotMiddleware
    {
        private readonly RequestDelegate _next;
        //拒否するアドレスの指定
        private readonly List<string> _blackIpAddress = new List<string> {"192.168.0.1", "192.168.0.2"};
        public AntiBotMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            //IPアドレスの取得
            string remoteIpAddress = httpContext.Connection.RemoteIpAddress.ToString();
            if (_blackIpAddress.Contains(remoteIpAddress))
            {
                //403 を返す                
                httpContext.Response.StatusCode = 403;
                return;
            }
            await _next(httpContext);
        }
    }

_next を呼ばなければ、次のパイプラインに行かずに返ります。それで、ブラウザーでは「サーバーエラー 403アクセスが拒否されました」と表示されるようになります。

この middleware を実行させるためには、Startup.cs の Configure メソッドに登録する必要があります。

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseAntiBotMiddleware();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

ここで、注意すべきことは、登録の順番が重要になってくるということです。今回の middleware は、その性格からいって UseMVC より前に置く必要があります。そして、UseStaticFiles の前におけば、静的ファイルもアクセス拒否の対象になり、後ろに置けば静的ファイルを除いたものがアクセス拒否の対象になります。

このサンプルは、プロキシーサーバーを使っている場合はうまく動作しません。httpContext.Connection.RemoteIpAddress が、プロキシーサーバーのアドレスになるためです。以下は、Nginx をプロキシーサーバーに使っている場合のサンプルです。

        public async Task Invoke(HttpContext httpContext)
        {
            //IPアドレスの取得
            string remoteIpAddress = GetRequestIP(httpContext);
            if (_blackIpAddress.Contains(remoteIpAddress))
            {
                //403 を返す                
                httpContext.Response.StatusCode = 403;
                return;
            }
            await _next(httpContext);
        }
        private static string GetRequestIP(HttpContext httpContext)
        {
            string ip = GetHeaderValueAs("X-Forwarded-For", httpContext);
            if (string.IsNullOrWhiteSpace(ip))
                return  httpContext.Connection.RemoteIpAddress.ToString();
            return ip;
        }
        private static string GetHeaderValueAs(string headerName, HttpContext httpContext)
        {
            StringValues values;
            if (httpContext?.Request?.Headers?.TryGetValue(headerName, out values) ?? false)
            {
                return values[0];
            }
            return null;
        }

なお、このサンプルが動作するためには、Nginx側で、X-Forwarded-For の設定をしておく必要があります。

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Dependency Injection を使う

上のサンプルで不便なのは、拒否するアドレスの指定を初期化で行っているところです。拒否するアドレスは画面で検索できたり変更できた方がいいですね。その場合には、Dependency Injection(DI)を使うのが自然です。DI という名前を聞くと難しそうに思うのし、解説を読んでも難しいのですが、以下のように簡単に作って試してみたら動作しました。

まず、普通に Class を作ります。(正式には、最初に Interface を作るようです)

public class AntiBotServise
{
    public List<string> BlackIpAddress { get; set; }
    public AntiBotServise()
    {
        BlackIpAddress = new List<string> {"192.168.0.1", "192.168.0.2"};
    }
}

すべてのリクエストで共通に使用するので、Startup.cs の ConfigureServices で AddSingleton を使って DIサービスコンテナに登録します。

public void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<AntiBotServise>();
   // Add framework services.
   services.AddMvc();
}

Middleware の方で、次のように Injection します。

public class AntiBotMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AntiBotServise _antiBotServise;
    public AntiBotMiddleware(RequestDelegate next, AntiBotServise antiBotServise)
    {
        _next = next;
        _antiBotServise = antiBotServise;
    }
    public async Task Invoke(HttpContext httpContext)
    {
        //IPアドレスの取得
        string remoteIpAddress = GetRequestIP(httpContext);
        if (_antiBotServise.BlackIpAddress.Contains(remoteIpAddress))
        {
            //403 を返す
            httpContext.Response.StatusCode = 403;
            return;
        }
        await _next(httpContext);
    }
}

Controller や View でも、同様に Injection できるので、画面で検索できたり変更したりできるようになります。

補足

長くなってきたので、middleware で eCAPTCHA を使う話は次回にします。

ASP.NET Advent Calendar では、Azure の話が多いようです。確かにエンタープライズ向けのクラウドでは Azure がベストだと思います。

しかし、DDoS の件を調べていると Google の名前がたびたび出てきます。Google は、公開用 Web サーバーが有力な収益源の一つなので、その関係のサービスは、Google の方がはるかに充実しています。また、.NET Foundation に加盟したことでもわかるように、Google Cloud Platform での .NET サポートはかなり進んできているという印象です。

ボットネットの脅威を考えたら Google Cloud を使うのがベストだ

2016年12月19日

これは Google Cloud Platform(2) Advent Calendar 2016 の19日目の記事です。

今年の11月に「Mirai」ボットネットが、Twitterなどの利用するDNSサービスへDDoS攻撃をしてサービスをダウンさせた事件があって、IoT ボットネットが話題になっています。先日、その IoT ボットネットから自分のWebサイトにもアクセスがありました。もちろん IoT ボットネットからという確証はありませんが、早朝から深夜までアクセスがあったので、常時電源の入っている機器には間違いありません。それだと普通のPCではないし、IP アドレスを調べるとモバイル回線からのアクセスはないのでスマートフォンでもなさそうです。

攻撃者は遊び程度だったようで、50台ぐらいのボットネットで、アクセスの間隔もかなり開いていました。それでもサーバーの CPU 負荷が普段より数倍になったので気がつきました。自分のWebサイトは時々スパイクアクセスがあるので、サーバーにかなり余裕をもたせていたので、今回は無事でした。

でも、もう少し攻撃が激しければ、システムがダウンしたと思います。そういうことを考えれば、サーバーはできるだけ増強したいのですが、その分サーバー料金がかさむのが痛いところです。

スパイクアクセスの場合は、同じページにアクセスが集中するので出力キャッシュが効果的なのですが、DDoS攻撃の場合はどういう攻撃されるか分からないので、現状だと、データベースの部分が弱いので、その能力のアップを急ごうと思っています。

現在、AWS を使っているので、DynamoDB だったら短時間でスループットを上げられるのかと調べてみると、よくある質問に以下のような回答がありました。スループットを増やすのに数分以上かかるようで、それだったらシステムはダウンしてしまいます。

Q: テーブルのプロビジョニングされたスループットレベルを変更するにはどのくらい時間がかかりますか?

通常、スループットを減らす場合は数秒から数分、増やす場合は数分から数時間かかります。

追加スループットが必要になった時点でスループットを増加したり増加のスケジュールを立てたりすることはお勧めできません。スループット容量のプロビジョニングは、必要なときに確実に容量を確保できるようかなり前から行っておくことをお勧めします。

それで Google Cloud Platform を調べたら、さすがに Google です。Cloud Datastore は、完全に自動スケールで、何も考える必要はありません。料金は以下のように完全に使っただけの料金になります。(以下東京リージョンの料金です)

  • データの保存 $0.18 GB/月
  • エンティティの読み込み数 $0.06 /10万エンティティ
  • エンティティの書き込み数 $0.18 /10万エンティティ
  • エンティティの削除数 $0.02 /10 万エンティティ

一方、AWS の DynamoDB の料金は以下のようになっています。

  • 書き込みスループット: $0.00742 :10 ユニットの書き込み容量あたり/1 時間 (1 時間あたり最大 36,000 回の書き込みを実行するために十分な容量)

  • 読み込みスループット: $0.00742 : 50 ユニットの読み込み容量あたり/1 時間 (1 時間あたり最大 180,000 回の強力な整合性のある読み込み、または最大 360,000 回の結果整合性のある読み込みを実行するために十分な容量)

こちらは、スループットで料金が決まります。この料金の記載だと、結果整合性のある読み込みだと、50ユニットの設定で、月2億5千万回のアクセスができ $5.34 で済みます。Google Cloud Datastore だったら $155 もかかると言いたいのでしょう。さすがに Amazon は商売上手です。

でも、公開用 Web サーバーの場合は、スパイクアクセスやDDoS攻撃のことを考えるとそんな単純な話ではありません。

価格を比較するため、公開用 Web サーバーで月1千万PVのWebサイトがあるとします。月1千万PVあれば Webサービスだとかなり楽に経営ができます。

Cloud Datastore の料金は、1pv に必要なデータベースへのリクエストを1エンティティとすると、料金は月 $6 になります。仮に 1pv当たり平均 10 エンティティが必要だとしても、月 $60 です。安い言い切るのは難しいかもしれませんがリーズナブルな料金です。

DynamoDB の場合は、容量をあらかじめ確保しておく必要があります。スパイクアクセスやDDos攻撃を考えると予測不可能で必要な容量の計算なんかできないですね。取りあえず1秒間に100pvのアクセスということにして、1pv で必要なデータベースへのアクセスが1ユニットだとすると、50ユニットが必要で月$5.34です。Cloud Datastore とほぼ同じ金額になります。

でも、ここでよく考えてみてください。DynamoDB は、全体のスループットではなくて、テーブル毎にスループットを設定する必要があります。ローカルセカンダリインデックスは、5個まででかつテーブルの作成のときに定義してやる必要がありそれ以降の変更はできないので、一つのテーブルに各種のデータを詰め込むのは無理で、それなりの数のテーブルが必要になると思います。DDos攻撃はどこが狙われるか分からないので、1秒間に100pvのアクセスに耐えるようにしようと思えば、すべてのテーブルにそにアクセスに耐えるだけのユニットを割り当てる必要があります。そしてさらに最悪なのが、1pv 当たりに必要なデータベースへのアクセスが平均では2ユニットであっても、あるページで50ユニット必要であれば、そのページが狙われるとダメなので、そのテーブルには50×50=2500ユニットも割り当てる必要がありそれだけで月$267になってしまいます。

1秒間に100pvというのは、どの程度かというと、今回のボットネットからの自分のサイトへのアクセスは、1台で1秒に15回以上のアクセスがありました。10台あれば150pvになるので100pvの容量だとエラーが多発するようになります。1秒間に100pvというのは、DDoS攻撃対策では、殆ど話にならないレベルです。

こう考えていくと、DynamoDB は、DDoS対策としては最悪でした。そして、通常の利用においても DynamoDB ファーストにするとテーブル数が多くなってコストが嵩みます。このことは、AWS もよく理解しているようで、Amazon AWSでユーザ数1100万以上にスケーリングするためのビギナーズ・ガイド という記事から引用します。

NoSQL データベースではなく、最初はSQLデータベースを使用しましょう。

  • まずはSQLデータベースの使用を推奨します。技術は確立されています。

  • 既存のコードやコミュニティ、サポートグループ、書籍やツールが多く存在します。

  • 最初の1000万ユーザでSQLデータベースが破壊されることはありません。(データベースが大規模でない限り)壊れることはないでしょう。

  • スケーラビリティの明確なパターンがあります。

NoSQL データベースが必要になるのはいつなのでしょうか。

  • 5TB以上のデータを初年に格納する必要がある場合や信じられないほどのデータベース集中型のワークロードの場合。

  • アプリケーションの要件が超低遅延である場合。

  • 高いスループットが必要な場合。読み込みや書き込みの際の出入力にかなり手を加える必要があります。

  • リレーショナルデータがない場合。

要するに DynamoDB は、アプリケーションを高速化するために、ごく少数のテーブルに対して使うべきもののようです。この影響で、業務系の人を中心に、NoSQL ファーストはダメだと思っている人が多いと思いますが、AWS の事情であって、本来はどちらがいいというものではなくて、アプリケーションの性格によって使うべきものです。

クラウドも AWS、Azure、Google Cloud のどれがいいのかという一般的な答えはないと思います。しかし、システムダウンしないWebサーバーにしようと思ったら、Cloud Datastore は殆どコストなしで無制限にスケールできるので Google Cloud を使った方がはるかにいいと思います。Google Cloud のアクセス集中に対する強さは、今年 Pokémon GO で実証されています。一方、AWS で、SQLデータベースをメインに使った場合、エンタープライズ向けの閉じたシステムだと、アクセス数はそれなりの精度で想定できるので問題はあまり生じないと思いますが、公開 Web サーバーで、DDoS攻撃 のことまで考えると非常に難しい問題になります。SQLデータベースではスケールさせるのにコストが非常に高くなるし、急激なアクセス増への対応も簡単ではないと思います。この点は、Azure も同じだと思います。

自分の Web サイトは、できれば DDoS攻撃に強いものにしていきたいので、サーバーを AWS から Google Cloud Platform に移そうと思っています。でも、Amazon EC2 のリザーブドインスタンスを買っているので、その期間が終了するまでは待ちます。リザーブドインスタンスは料金の割引は大きいのですが制約は厳しいと思います。google Compute Engine の方は、1ヶ月単位で自動的に継続割引が適用されます。何もしなくてもいいのには気に入りました。(計算してみたら AWSは3年の標準リザーブドインスタンスだとAmazon EC2 の料金が安いですが、3年縛りは厳しいし、値下げが適用されないのでリスクが高いです。コンバーティブルリザーブドインスタンスにすると google Compute Engine の継続割引後の料金とほぼ同じです。)

.NET Core で PostgreSQL を使ってみたら結構いけていたという話

2016年12月15日

これは PostgreSQL Advent Calendar 2016の15日目の記事です。

この Web サイトは、今年の4月から ASP.NET を Core の方に更新して Ubuntu サーバーで運用しています。実際に運用してみて、IIS よりも Nginx を使う方が便利だと思うようになってきたので、サーバーはすべて Linux サーバーにする予定です。

その際の問題は、データベースをどうするかということです。ASP.NET とはなんといっても SQL Server を使うのが定番です。それで、自分も SQL Server Express を使ってアプリを作っていました。しかし、残念なことに SQL Server が Linux に対応するのは来年の半ばということです。

最近、PostGIS を使いたいという思惑もあって、PostgreSQL を使ってみたら、結構いけていると感じたので、この記事を書くことにしました。まだ、使い始めて1ヶ月なので、今後問題が見つかるかもしれないので最終的どうなるかはわかりませんが、PostgreSQL を使っていこうと思っています。

Entity Framework Core で使う

.NET Core や .NET Framework から、PostgreSQL を使う場合は、Npgsql というデータプロバイダーを使用します。Npgsql には、Entity Framework Core(EF Core)用のプロバイダーもあるので、EF Core で使っている範囲では、SQL Server を使うのと違いは殆どありません。

参考 Npgsql の EF Core 用のドキュメント

例として、Visual Studio の ASP.NET Core の Web アプリケーションのテンプレートで認証を「個別のユーザーアカウント」にした場合に、EF Core が使用されているので、それを PostgreSQL を使うように修正してみます。

  1. Nuget パッケージ の追加 Npgsql.EntityFrameworkCore.PostgreSQL を追加します。

  2. Startup.cs の修正 以下のように、options.UseSqlServer と SQL Server を使う設定になっているので、

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
       .AddEntityFrameworkStores<ApplicationDbContext>()
       .AddDefaultTokenProviders();
    ------ 以下省略
}

options.UseNpgsql に変更して、Npgsql を使うようにします。

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
                options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
       .AddEntityFrameworkStores<ApplicationDbContext>()
       .AddDefaultTokenProviders();
    ------ 以下省略
}
  1. appsettings.json に記載してある接続文字列を修正します。
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=test;Username=postgres;Password=password"
  },
------ 以下省略
}

SQL Server から PostgreSQL を使うように変更するのはそれだけです。

接続文字列に設定したユーザーが、データベースを作成できる権限を持っているユーザーであれば、これでデータベースも勝手に作ります。もし、そうでなければ事前にデータベースを作っておきます。

ただし、それで Run して、ユーザーの登録をしようとするとエラーになります。Data ディレクトリに Migration フォルダーが作成されていて、SQL Server 用のものが事前に作られているためです。 それで、それらのファイルを削除してからするとちゃんとしたエラーメッセーが出ます。メーセージの通りにマイグレーションをしてやると正常に動くようになります。

A database operation failed while processing the request.
NpgsqlException: Exception while reading from stream 
IOException: Unable to read data from the transport connection: 既存の接続はリモート ホストに強制的に切断されました。. 
SocketException: 既存の接続はリモート ホストに強制的に切断されました。 
Use migrations to create the database for ApplicationDbContext
In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:
PM> Add-Migration [migration name] 
PM> Update-Database
Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
> dotnet ef migrations add [migration name] 
> dotnet ef database update

コードファーストで開発する場合は、SQL Server なのか PostgreSQL なのかを気にしなくても開発できると思います。

細かい点でいうと作成されたテーブルをみると下の図のようにカラム名が Pascal ケースになっています。

AdWordのアカウントのリンクの画像

SQL Server では、カラム名の大文字小文字を区別しないのでPascalケースは便利なのですが、PostgreSQL では、列名の大文字小文字を区別し、SQL文の中の列名は小文字に変換されます。それで、カラム名が「AspNetUsers」だと SQL 文の中では、常に二重引用符でくくって "AspNetUsers" としないといけなくなって結構手間になります。そのまま Pascal ケースで使うのか、それとも fluent API か attributes を使ってスネークケースに変換して使うのか悩ましいところです。自分は EF Core で使うのが主で、生の SQL をあまり使わないので、Pascal ケースのままでもいいのではないかと思っています。

データベースファーストの場合も、SQL Server を使うのと殆ど違いはなく、Microsoft.EntityFrameworkCore.SqlServer.Design の代わりに Npgsql.EntityFrameworkCore.PostgreSQL.Design を追加し、

dotnet cli の場合は、

dotnet ef dbcontext scaffold "Host=localhost;Database=mydatabase;Username=myuser;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL

Powershell の場合は、

Scaffold-DbContext "Host=localhost;Database=mydatabase;Username=myuser;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL

というように、Microsoft.EntityFrameworkCore.SqlServerNpgsql.EntityFrameworkCore.PostgreSQL に置き換えてやれば、EF model を作成できます。

なお、DbContext のプロパティ名はカラム名を Pascal ケースに変換したものになります。

以上のように EF Core で使う限りにおいては、SQL Server を使うのと差はなく、実際に使っても今のところ大きな問題はでていません。

PostgreSQL を使うメリット、デメリット

SQL Server よりも便利な点としては、where 句で下のように正規表現を使うとサーバー側で処理をしてくれるという点です。( 参照 ドキュメント)

var customersStartingWithA = context.Customers.Where(c => Regex.IsMatch(c.CompanyName, "^A"));

まだ使っていませんが、circle 等の幾何データ型、配列型、JSONB型のマッピングは可能なようなので、EF Core でも、これらのデータの取得と更新は可能なようです。

ただし、PostgreSQL の独自の機能を本格的に使うためには、EF Core ではなくて、Npgsql を直接使う必要があり少し手間がかかります。

でも、少し調べてみると、.NET で PostgreSQL を Document Db として使うためのソフトとして Marten というソフトが開発されています。まだ使い始めたばかりですが、結構使いやすい感じです。

PostgreSQL のデメリットといえば、慣れの問題かも分かりませんが pgAdmin よりは、Visual Studio Management Studio の方が使いやすいという点です。

PostgreSQL にするか MySQL にするか

Linux で動作するオープンソースの RDB といえば、PostgreSQL よりも MySQL の方が有名です。

まず、.NET Core 対応という点で比較すると、PostgreSQL の方は、Npgsql の .NET Core 対応はアルファ版が出てもう1年以上になります。その後も開発が進められてきており、かなり安定してきていると思います。一方、MySQL の方は .NET Core に Connector/Net 7 で対応予定で現在開発版が公開されています。

最近の状況では、ZDNet Japan の「PostgreSQL」がスタートアップ企業に選ばれる理由という記事が参考になると思います。以下に最近のデータを載せておきます。

Hacker News Hiring Trends 2016年11月 Hacker News Hiring Trendsの画像

indeed Job Trends indeed Job Trendsのリンクの画像